diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..d373ebd7 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,66 @@ +before_script: + - cd $HOME + - wget https://pypi.python.org/packages/df/c9/d9da7fafaf2a2b323d20eee050503ab08237c16b0119c7bbf1597d53f793/pyserial-2.7.tar.gz#md5=794506184df83ef2290de0d18803dd11 + - tar -xvf pyserial-2.7.tar.gz + - cd pyserial-2.7 + - python ./setup.py install + - 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 + - cd $HOME/arduino_ide/hardware + - mkdir esp8266com + - cd esp8266com + - git clone https://github.com/esp8266/Arduino.git esp8266 + - 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 + - echo 'build.f_cpu=160000000L' >> pt.txt + - echo 'build.flash_mode=dio' >> pt.txt + - cat ./platform.txt >> pt.txt + - rm -fr platform.txt + - mv pt.txt platform.txt + - mv $CI_PROJECT_DIR/libraries/SdFat/SdFat $HOME/arduino_ide/libraries/ + - mv $CI_PROJECT_DIR/libraries/IniFile $HOME/arduino_ide/libraries/ + - mv $CI_PROJECT_DIR/libraries/ESPAsyncWebServer $HOME/arduino_ide/libraries/ + - mv $CI_PROJECT_DIR/libraries/ESPAsyncTCP $HOME/arduino_ide/libraries/ + - mv $CI_PROJECT_DIR/libraries/AsyncTCP $HOME/arduino_ide/libraries/ + - mv $CI_PROJECT_DIR/libraries/oled-ssd1306 $HOME/arduino_ide/libraries/ + - mv $CI_PROJECT_DIR/libraries/ESP32SSDP $HOME/arduino_ide/libraries/ + - mv $CI_PROJECT_DIR/libraries/arduinoWebSockets $HOME/arduino_ide/libraries/ + - mv $CI_PROJECT_DIR/libraries/DHT_sensor_library_for_ESPx $HOME/arduino_ide/libraries/ + - mkdir $HOME/arduino_ide/libraries/ESP3D + - mv $CI_PROJECT_DIR/src $HOME/arduino_ide/libraries/ESP3D + - mv $CI_PROJECT_DIR/library.properties $HOME/arduino_ide/libraries/ESP3D + - cd $HOME/arduino_ide/hardware + - mkdir esp32 + - cd esp32 + - git clone https://github.com/espressif/arduino-esp32.git esp32 + - cd esp32 + - cd tools + - python get.py + - cd .. + - touch pt.txt + - echo 'build.flash_freq=80m' >> pt.txt + - cat ./platform.txt >> pt.txt + - rm -fr platform.txt + - mv pt.txt platform.txt + +job1: + script: + - cd $CI_PROJECT_DIR + - source command.sh + - export PATH="$HOME/arduino_ide:$PATH" + - arduino --get-pref + - arduino --board esp8266com:esp8266:generic --save-prefs + - sed -i "s/\/\/#define SDCARD_FEATURE /#define SDCARD_FEATURE/g" $HOME/arduino_ide/libraries/ESP3D/src/config.h + - echo "build ESP8266" + - build_sketch $CI_PROJECT_DIR/examples/basicesp3d/basicesp3d.ino + - arduino --board esp32:esp32:esp32 --save-prefs + - echo "build ESP32" + - build_sketch $CI_PROJECT_DIR/examples/basicesp3d/basicesp3d.ino + + diff --git a/.travis.yml b/.travis.yml index f5fdc0af..bc99c384 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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.4-linux64.tar.xz - - tar xf arduino-1.8.4-linux64.tar.xz - - mv arduino-1.8.4 $HOME/arduino_ide + - wget http://downloads.arduino.cc/arduino-1.8.5-linux64.tar.xz + - tar xf arduino-1.8.5-linux64.tar.xz + - mv arduino-1.8.5 $HOME/arduino_ide - cd $HOME/arduino_ide/hardware - mkdir esp8266com - cd esp8266com @@ -35,6 +35,16 @@ before_script: - python get.py - cd .. - echo 'build.flash_freq=40m' >> platform.txt + - mv $TRAVIS_BUILD_DIR/libraries/ESPAsyncWebServer $HOME/arduino_ide/libraries/ + - mv $TRAVIS_BUILD_DIR/libraries/ESPAsyncTCP $HOME/arduino_ide/libraries/ + - mv $TRAVIS_BUILD_DIR/libraries/AsyncTCP $HOME/arduino_ide/libraries/ + - mv $TRAVIS_BUILD_DIR/libraries/oled-ssd1306 $HOME/arduino_ide/libraries/ + - mv $TRAVIS_BUILD_DIR/libraries/ESP32SSDP $HOME/arduino_ide/libraries/ + - mv $TRAVIS_BUILD_DIR/libraries/arduinoWebSockets $HOME/arduino_ide/libraries/ + - mv $TRAVIS_BUILD_DIR/libraries/DHT_sensor_library_for_ESPx $HOME/arduino_ide/libraries/ + - mkdir $HOME/arduino_ide/libraries/ESP3D + - mv $TRAVIS_BUILD_DIR/src $HOME/arduino_ide/libraries/ESP3D + - mv $TRAVIS_BUILD_DIR/library.properties $HOME/arduino_ide/libraries/ESP3D script: @@ -43,9 +53,21 @@ script: - export PATH="$HOME/arduino_ide:$PATH" - arduino --board esp8266com:esp8266:generic --save-prefs - arduino --get-pref sketchbook.path - - build_sketch esp3d/esp3d.ino + - build_sketch $TRAVIS_BUILD_DIR/examples/basicesp3d/basicesp3d.ino - arduino --board esp32:esp32:esp32 --save-prefs - - build_sketch esp3d/esp3d.ino + - build_sketch $TRAVIS_BUILD_DIR/examples/basicesp3d/basicesp3d.ino + - sed -i "s/\/\/#define AUTHENTICATION_FEATURE /#define AUTHENTICATION_FEATURE/g" $HOME/arduino_ide/libraries/ESP3D/src/config.h + - arduino --board esp8266com:esp8266:generic --save-prefs + - arduino --get-pref sketchbook.path + - build_sketch $TRAVIS_BUILD_DIR/examples/basicesp3d/basicesp3d.ino + - arduino --board esp32:esp32:esp32 --save-prefs + - build_sketch $TRAVIS_BUILD_DIR/examples/basicesp3d/basicesp3d.ino + - sed -i "s/\/\/#define ASYNCWEBSERVER /#define ASYNCWEBSERVER/g" $HOME/arduino_ide/libraries/ESP3D/src/config.h + - arduino --board esp8266com:esp8266:generic --save-prefs + - arduino --get-pref sketchbook.path + - build_sketch $TRAVIS_BUILD_DIR/examples/basicesp3d/basicesp3d.ino + - arduino --board esp32:esp32:esp32 --save-prefs + - build_sketch $TRAVIS_BUILD_DIR/examples/basicesp3d/basicesp3d.ino notifications: email: diff --git a/README.md b/README.md index 9faa9ad3..2c864e10 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ESP3D[![Code Climate](https://codeclimate.com/github/luc-github/ESP3D/badges/gpa.svg)](https://codeclimate.com/github/luc-github/ESP3D) +# ESP3D 2.0 [![Code Climate](https://codeclimate.com/github/luc-github/ESP3D/badges/gpa.svg)](https://codeclimate.com/github/luc-github/ESP3D) 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) @@ -16,7 +16,7 @@ Arduino ide 1.6.5 with stable [2.0.0](http://arduino.esp8266.com/versions/2.0.0/ 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 Arduino ide 1.8.5 with stable [2.4.0](http://arduino.esp8266.com/versions/2.4.0/package_esp8266com_index.json) from ESP8266, please use https://github.com/luc-github/ESP3D/releases/tag/1.0 [![Build Status](https://travis-ci.org/luc-github/ESP3D.svg?branch=master)](https://travis-ci.org/luc-github/ESP3D) -[Development version for 2.0 (2.0 branch)](https://github.com/luc-github/ESP3D/tree/2.0) & [ESP-WEBUI (2.0 branch)](https://github.com/luc-github/ESP3D-WEBUI/tree/2.0): +[Development version for 2.0](https://github.com/luc-github/ESP3D/tree/2.0) & [ESP-WEBUI (2.0 branch)](https://github.com/luc-github/ESP3D-WEBUI/tree/2.0): Arduino ide 1.8.5 with git version from ESP8266 or ESP32 for 100% support of ESP32 : [![Build Status](https://travis-ci.org/luc-github/ESP3D.svg?branch=2.0)](https://travis-ci.org/luc-github/ESP3D) [All releases](https://github.com/luc-github/ESP3D/wiki) @@ -38,12 +38,13 @@ Especially if need to buy new modules for testing. * 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) -* UI fully constomizable without reflashing FW using html templates, [keywords](https://raw.githubusercontent.com/luc-github/ESP3D/master/docs/keywords.txt) and html files/images * Captive portal in Access point mode which redirect all unknow call to main page, here to enable/disable [CAPTIVE_PORTAL_FEATURE](https://github.com/luc-github/ESP3D/blob/master/esp3d/config.h) * mDNS which allows to key the name defined in web browser and connect only with bonjour installed on computer, here to enable/disable [MDNS_FEATURE](https://github.com/luc-github/ESP3D/blob/master/esp3d/config.h) * 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. +* Choice of web server Async or Sync +* Websocket support +* OLED screen support * The web ui add even more feature : https://github.com/luc-github/ESP3D-WEBUI/blob/master/README.md#features @@ -69,7 +70,8 @@ IP: 192.168.0.1 Mask: 255.255.255.0 GW:192.168.0.1 Baud rate: 115200 -Web port:80 +Web port:80 +On async webserver the websocket is web port => 80+1 : 81 Data port: 8888 Web Page refresh: 3 secondes User: admin @@ -84,14 +86,27 @@ Check wiki : https://github.com/luc-github/ESP3D/wiki/Direct-ESP3D-commands ## Installation 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. +2. Add libraries +* ESP3D because it is now a library so copy project to your arduino library folder +If you want async webserver (currently not recommended for ESP8266, suggested for ESP32): +* ESPAsyncWebServer from @me-no-dev +if you target ESP8266 +* ESPAsyncTCP from @me-no-dev +if you target ESP32: +* AsyncTCP from @me-no-dev +Specific for ESP32 +* ESP32SSDP +If you want sync webserver (recommended for ESP8266, slow for ESP32): +* arduinoWebSockets fron @Links2004 +If you want OLED support: +* oled-ssd1306 from @squix78 +If you want DHT11/22 support: +* DHT_sensor_library_for_ESPx from @beegee-tokyo +3. Compile project from examples\basicesp3d\basicesp3d.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 +or * 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
@@ -99,7 +114,7 @@ if embedded uploader does not show up you can force it ti display using : http:/ ## Update * Generate a binary using the export binary menu from Arduino IDE and upload it using ESP-WEBUI or embedded interface -

:warning:Do not flash your Printer fw with ESP connected - it bring troubles, at least on DaVinci

+

:warning:Do not flash your Printer fw with ESP connected on Serial - it bring troubles, at least on DaVinci, but no issue if you update using web UI

## Contribution/customization * To style the code before pushing PR please use [astyle --style=otbs *.h *.cpp *.ino](http://astyle.sourceforge.net/) @@ -107,7 +122,6 @@ if embedded uploader does not show up you can force it ti display using : http:/ * 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 supported boards or wiring ? [Check the wiki](https://github.com/luc-github/ESP3D/wiki) @@ -120,6 +134,7 @@ Check [Wiki](https://github.com/luc-github/ESP3D/wiki/Install-Instructions) and ## ESP3D is used by : * Custom version is used on azteeg mini wifi : http://www.panucatt.com/azteeg_X5_mini_reprap_3d_printer_controller_p/ax5mini.htm +* Several boards, mostly chinese ones, but without noticed me, so I can't promote them here... * More to come... If you use ESP3D on your product, drop me a message so I can link your product page here. diff --git a/docs/Commands.txt b/docs/Commands.txt index 501fc2c7..f5c860f2 100644 --- a/docs/Commands.txt +++ b/docs/Commands.txt @@ -50,9 +50,6 @@ if authentication is on, need admin password * Get hostname [ESP112]
-* Restart time client -[ESP114] - *Get/Set pin value [ESP201]P V [PULLUP=YES RAW=YES]pwd= if no V get P value @@ -61,11 +58,22 @@ 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 +* Output to oled column C and line L +[ESP210]C= L= T= + +* Output to oled line 1 +[ESP211] + +* Output to oled line 2 +[ESP212] + +* Output to oled line 3 +[ESP213] + +* Output to oled line 4 +[ESP214] + -*Save data string -[ESP300] pwd= -*Get data string -[ESP301] pwd= *Get full EEPROM settings content but do not give any passwords @@ -76,6 +84,40 @@ can filter if only need wifi or printer position in EEPROM, type: B(byte), I(integer/long), S(string), A(IP address / mask) [ESP401]P= T= V= pwd= +Positions: +* EP_WIFI_MODE 0 //1 byte = flag +* EP_STA_SSID 1 //33 bytes 32+1 = string ; warning does not support multibyte char like chinese +* EP_STA_PASSWORD 34 //65 bytes 64 +1 = string ;warning does not support multibyte char like chinese +* EP_STA_IP_MODE 99 //1 byte = flag +* EP_STA_IP_VALUE 100 //4 bytes xxx.xxx.xxx.xxx +* EP_STA_MASK_VALUE 104 //4 bytes xxx.xxx.xxx.xxx +* EP_STA_GATEWAY_VALUE 108 //4 bytes xxx.xxx.xxx.xxx +* EP_BAUD_RATE 112 //4 bytes = int +* EP_STA_PHY_MODE 116 //1 byte = flag +* EP_SLEEP_MODE 117 //1 byte = flag +* EP_CHANNEL 118 //1 byte = flag +* EP_AUTH_TYPE 119 //1 byte = flag +* EP_SSID_VISIBLE 120 //1 byte = flag +* EP_WEB_PORT 121 //4 bytes = int +* EP_DATA_PORT 125 //4 bytes = int +* EP_OUTPUT_FLAG 129 //1 bytes = flag +* EP_HOSTNAME 130//33 bytes 32+1 = string ; warning does not support multibyte char like chinese +* EP_DHT_INTERVAL 164//4 bytes = int +* EP_FREE_INT2 168//4 bytes = int +* EP_FREE_INT3 172//4 bytes = int +* EP_ADMIN_PWD 176//21 bytes 20+1 = string ; warning does not support multibyte char like chinese +* EP_USER_PWD 197//21 bytes 20+1 = string ; warning does not support multibyte char like chinese +* EP_AP_SSID 218 //33 bytes 32+1 = string ; warning does not support multibyte char like chinese +* EP_AP_PASSWORD 251 //65 bytes 64 +1 = string ;warning does not support multibyte char like chinese +* EP_AP_IP_VALUE 316 //4 bytes xxx.xxx.xxx.xxx +* EP_AP_MASK_VALUE 320 //4 bytes xxx.xxx.xxx.xxx +* EP_AP_GATEWAY_VALUE 324 //4 bytes xxx.xxx.xxx.xxx +* EP_AP_IP_MODE 329 //1 byte = flag +* EP_AP_PHY_MODE 330 //1 byte = flag +* EP_FREE_STRING1 331 //129 bytes 128+1 = string ; warning does not support multibyte char like chinese +* EP_DHT_TYPE 460 //1 bytes = flag +* EP_TARGET_FW 461 //1 bytes = flag + *Get available AP list (limited to 30) output is JSON or plain text according parameter [ESP410] @@ -109,7 +151,7 @@ if no password set it use default one * Get fw target [ESP801]
-* Clear status/error/info list -cmd can be ALL, ERROR, INFO, STATUS -[ESP999] +* Check SD presence +[ESP802] + diff --git a/esp3d/data/404.htm b/docs/Files/404.htm similarity index 100% rename from esp3d/data/404.htm rename to docs/Files/404.htm diff --git a/esp3d/data/favicon.ico b/docs/Files/favicon.ico similarity index 53% rename from esp3d/data/favicon.ico rename to docs/Files/favicon.ico index b2b24df7..6794fd9f 100644 Binary files a/esp3d/data/favicon.ico and b/docs/Files/favicon.ico differ diff --git a/docs/Files/index.html.gz b/docs/Files/index.html.gz new file mode 100644 index 00000000..4245f540 Binary files /dev/null and b/docs/Files/index.html.gz differ diff --git a/esp3d/data/macrocfg.json b/docs/Files/macrocfg.json similarity index 100% rename from esp3d/data/macrocfg.json rename to docs/Files/macrocfg.json diff --git a/docs/Readme.txt b/docs/Readme.txt new file mode 100644 index 00000000..cc5623dd --- /dev/null +++ b/docs/Readme.txt @@ -0,0 +1,9 @@ +Commands.txt list all ESP commands +wconfig.txt list all parameters for SD file config +flash.docx explain how to flash +Files directory contain all files needed by ESP3D + - index.html.gz // main ui file (mandatory) + - 404.htm // custom 404 page (optional) + - favicon.ico //custom browser esp3D icon (optional) + - macrocfg.json //macro command file (optional) + \ No newline at end of file diff --git a/docs/flash.docx b/docs/flash.docx new file mode 100644 index 00000000..0f4fcd44 Binary files /dev/null and b/docs/flash.docx differ diff --git a/embedded/out.h b/embedded/out.h new file mode 100644 index 00000000..89d14862 --- /dev/null +++ b/embedded/out.h @@ -0,0 +1,319 @@ +/* + File Generated with bin2c Conversion Tool v0.14.0 - Windows - [FINAL]. + Free for any use. + Compiled on Jan 1 2017 at 05:41:46. + DO NOT MODIFY THIS FILE!!! +*/ +/* + ut.h +*/ + +const unsigned char a[4862] = { + 0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xED, 0x5C, 0x7D, 0x93, 0xDA, 0x46, + 0x93, 0xFF, 0x2A, 0xB2, 0x52, 0x36, 0x70, 0x2B, 0x40, 0x12, 0xAF, 0x8B, 0x16, 0xF2, 0x24, 0xB1, + 0x7D, 0xF1, 0x95, 0x13, 0xBB, 0xBC, 0xEB, 0x7B, 0xAE, 0x2A, 0x4E, 0xB9, 0x84, 0x34, 0x80, 0xCE, + 0x42, 0xD2, 0x49, 0xC3, 0xEE, 0x62, 0xC2, 0x77, 0xBF, 0xEE, 0x79, 0x91, 0x46, 0x42, 0xB0, 0xEC, + 0x26, 0x79, 0xF2, 0xFC, 0x91, 0x60, 0x23, 0x98, 0x99, 0xEE, 0xE9, 0xE9, 0xE9, 0xFE, 0x75, 0x4F, + 0x0F, 0xCE, 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, 0xEF, 0x26, 0xA3, 0x13, 0xCB, 0x34, 0x9F, 0x3B, 0xED, 0x3B, 0x32, 0xFF, 0x12, + 0xD0, 0x23, 0xBD, 0x8C, 0x1D, 0xB6, 0xC2, 0xD7, 0xE4, 0x7E, 0x3F, 0x8F, 0xFD, 0x6D, 0x69, 0x0A, + 0xFD, 0x47, 0x12, 0xDE, 0x12, 0x1A, 0x78, 0xAE, 0xF6, 0x33, 0xD9, 0x10, 0xDD, 0xC8, 0xBF, 0x1B, + 0xDF, 0xA5, 0x81, 0x1B, 0x1A, 0x8A, 0x0C, 0x0A, 0xAF, 0x7E, 0x72, 0xEF, 0x84, 0x41, 0x44, 0xDA, + 0x2B, 0x12, 0x2C, 0x57, 0x30, 0x57, 0xA7, 0x6F, 0x8F, 0x07, 0x23, 0xAB, 0xDF, 0x73, 0xBC, 0x38, + 0x8C, 0xD3, 0xC9, 0x37, 0xBD, 0x5E, 0xCF, 0x99, 0xBB, 0xDE, 0x97, 0x65, 0x1A, 0x6F, 0x22, 0xBF, + 0x2D, 0x5A, 0x17, 0x8B, 0xC5, 0xBE, 0xE3, 0x01, 0x1F, 0x17, 0x88, 0xD3, 0xDD, 0xDA, 0x4D, 0x97, + 0x41, 0xD4, 0x4E, 0x19, 0x0F, 0x77, 0x43, 0x63, 0x47, 0xB4, 0x84, 0x64, 0x21, 0x1A, 0x12, 0xD7, + 0xF7, 0x83, 0x68, 0xC9, 0x5B, 0xAC, 0x01, 0xCC, 0x2B, 0x5B, 0x38, 0x15, 0x36, 0xED, 0xA9, 0x3B, + 0x0F, 0xC9, 0x6E, 0x1E, 0xA7, 0x3E, 0x49, 0x27, 0xA6, 0xC3, 0x3F, 0xB4, 0xB3, 0xC4, 0xF5, 0x60, + 0x20, 0x34, 0xAC, 0xDD, 0xFB, 0xF6, 0x5D, 0xE0, 0xD3, 0x15, 0x53, 0xCA, 0xBE, 0xC3, 0xC6, 0xB7, + 0xF9, 0x30, 0xE2, 0xEF, 0x8A, 0x2E, 0x41, 0x3A, 0xB1, 0x92, 0x7B, 0x2D, 0x8B, 0xC3, 0xC0, 0xD7, + 0xBE, 0xF1, 0x7D, 0x5F, 0x4A, 0x35, 0x8F, 0x29, 0x8D, 0xD7, 0x13, 0x1B, 0x35, 0x49, 0x81, 0x6C, + 0x15, 0x50, 0xC2, 0x66, 0x21, 0x93, 0x28, 0xBE, 0x4B, 0xDD, 0x44, 0xCA, 0x36, 0xB1, 0xD7, 0xEB, + 0x3D, 0x5D, 0xED, 0xD8, 0x9E, 0xB8, 0x61, 0xB0, 0x8C, 0x26, 0x28, 0xBF, 0x98, 0x78, 0x46, 0x71, + 0x1B, 0x66, 0x34, 0x9D, 0x51, 0xDF, 0x38, 0x68, 0x5A, 0xE5, 0x4D, 0xCC, 0x36, 0xCA, 0xA3, 0xF2, + 0xA6, 0xD5, 0x4E, 0x4E, 0x35, 0x3E, 0xBE, 0x15, 0xB7, 0x24, 0xC5, 0x9D, 0x0C, 0x85, 0x08, 0x34, + 0x4E, 0xA4, 0x6A, 0xE0, 0x63, 0x65, 0x8D, 0x55, 0xA5, 0xD4, 0x08, 0x59, 0xD7, 0xB7, 0x3A, 0xEC, + 0x3B, 0x10, 0xBB, 0xAE, 0x6F, 0xB5, 0xAB, 0xD5, 0xF4, 0xA1, 0x14, 0x8F, 0xE2, 0x26, 0x76, 0x48, + 0xEC, 0xB5, 0x0D, 0xDB, 0x24, 0x68, 0x32, 0x9A, 0x06, 0x89, 0x22, 0xF8, 0x24, 0xA2, 0xAB, 0x76, + 0xBC, 0x68, 0xD3, 0x6D, 0x42, 0x9A, 0xB1, 0xEF, 0xB7, 0x76, 0x35, 0xB6, 0x7A, 0x89, 0xAF, 0xFD, + 0x3F, 0xD6, 0xC4, 0x0F, 0x5C, 0xAD, 0xB9, 0x06, 0x03, 0xE0, 0x7C, 0x47, 0x43, 0xD0, 0x79, 0x6B, + 0xA7, 0xD8, 0xB1, 0x68, 0x1F, 0xA0, 0x61, 0xD4, 0x10, 0x5C, 0x5E, 0xDA, 0xB5, 0x04, 0x97, 0xA3, + 0x23, 0x04, 0x96, 0x6D, 0x9A, 0xB5, 0x14, 0x96, 0xC5, 0x49, 0x3A, 0x91, 0x7B, 0xAB, 0x9A, 0xAD, + 0x10, 0xD9, 0xF3, 0xBC, 0x8A, 0xC3, 0x98, 0x55, 0x77, 0x31, 0xC1, 0x58, 0x32, 0x70, 0x63, 0x44, + 0x1C, 0xB0, 0xDA, 0x88, 0xD4, 0x78, 0x29, 0xF3, 0x5D, 0xAE, 0xD0, 0xD4, 0xF5, 0x83, 0x4D, 0x36, + 0x19, 0x82, 0x91, 0xD5, 0x38, 0x81, 0xBB, 0x4B, 0xE2, 0x2C, 0xA0, 0x41, 0x1C, 0x4D, 0x52, 0x12, + 0xBA, 0x34, 0xB8, 0x25, 0x8E, 0x1F, 0x64, 0x49, 0xE8, 0x6E, 0x27, 0xF3, 0x30, 0xF6, 0xBE, 0xE4, + 0x0E, 0x81, 0xE8, 0xA3, 0x31, 0xF7, 0x65, 0x3E, 0xE1, 0x13, 0x2F, 0x4E, 0x5D, 0x46, 0xC8, 0x64, + 0x28, 0xE4, 0xDF, 0x77, 0x5C, 0x0F, 0xF9, 0xEC, 0x0A, 0xC4, 0xA8, 0x91, 0xD0, 0x34, 0x4D, 0x39, + 0x50, 0x73, 0x0D, 0x77, 0xB2, 0x88, 0xBD, 0x4D, 0x06, 0xCF, 0x55, 0x0C, 0x36, 0xBF, 0x53, 0xC1, + 0x26, 0x71, 0x23, 0x12, 0xEE, 0x0E, 0x65, 0xAF, 0x07, 0xA7, 0x23, 0xFE, 0x5F, 0x56, 0x06, 0x82, + 0x9F, 0x44, 0xDD, 0x79, 0x7C, 0xDF, 0xCE, 0x56, 0xAE, 0x1F, 0xDF, 0x4D, 0x4C, 0x0D, 0xA9, 0xF0, + 0x6F, 0xBA, 0x9C, 0xBB, 0x4D, 0xD3, 0xC0, 0x57, 0xC7, 0x1C, 0xB4, 0x9C, 0x73, 0x06, 0x09, 0x49, + 0xDB, 0x0C, 0xA1, 0x73, 0xAD, 0x21, 0xB8, 0x89, 0x0E, 0x34, 0x76, 0x68, 0xDB, 0x1D, 0x6A, 0xF4, + 0x34, 0xE2, 0x0E, 0xF0, 0x25, 0x57, 0x20, 0x1A, 0x95, 0x35, 0x01, 0x12, 0x70, 0xD3, 0x90, 0xAB, + 0xEB, 0xA1, 0x6E, 0x8A, 0x3E, 0x34, 0xA3, 0x9A, 0x2E, 0xA1, 0xC9, 0x8A, 0xF7, 0x86, 0xEE, 0x1C, + 0x94, 0x2D, 0x2D, 0x20, 0x88, 0x18, 0x2E, 0x71, 0x43, 0x28, 0x43, 0x70, 0xC5, 0x98, 0x70, 0x15, + 0x2C, 0xBA, 0xDC, 0x71, 0x0C, 0x1B, 0xE1, 0xF6, 0x32, 0x43, 0x09, 0xA2, 0x45, 0x2C, 0xF7, 0xB3, + 0x07, 0xC6, 0x3F, 0x86, 0x2D, 0x5D, 0xC4, 0xE9, 0xBA, 0x8D, 0x9E, 0x91, 0xC6, 0xC5, 0x64, 0x7C, + 0x16, 0x3E, 0x03, 0x0B, 0x1C, 0x02, 0x0E, 0x7B, 0xFD, 0x22, 0x64, 0xA0, 0x19, 0x6B, 0x96, 0x2D, + 0x27, 0x3B, 0x37, 0x94, 0x0D, 0x06, 0x83, 0x63, 0xD6, 0x52, 0xB4, 0x06, 0x6B, 0x77, 0x29, 0x1D, + 0xEA, 0xC0, 0x86, 0xD0, 0x2F, 0xCF, 0xB2, 0xA1, 0x20, 0xCA, 0x08, 0xD5, 0x8E, 0x18, 0xC9, 0xA8, + 0x6C, 0x4A, 0x0F, 0x8E, 0x6D, 0xC7, 0x6D, 0x9A, 0x42, 0xF8, 0xE6, 0x0E, 0xAA, 0x5A, 0x80, 0x46, + 0xDC, 0x8C, 0x80, 0x6E, 0xDB, 0xF1, 0x86, 0x6A, 0x1D, 0x6B, 0x90, 0x19, 0x05, 0xDF, 0x83, 0xBE, + 0xB2, 0xC2, 0xB9, 0xAB, 0xED, 0xCA, 0xF6, 0x34, 0x1C, 0xBA, 0x0B, 0x72, 0xE9, 0x00, 0x05, 0x6A, + 0x12, 0x02, 0xEE, 0x13, 0x96, 0x66, 0x98, 0xD0, 0x39, 0x96, 0x1D, 0x96, 0x69, 0x1B, 0xD6, 0x68, + 0x60, 0xD8, 0xBD, 0x9E, 0xD1, 0x19, 0xB6, 0x84, 0x0C, 0xA8, 0xEB, 0xA4, 0xE2, 0xCC, 0xDC, 0x47, + 0xE6, 0x34, 0x3A, 0x66, 0x77, 0xEA, 0x60, 0xB3, 0x64, 0x66, 0x7D, 0xD3, 0x74, 0x94, 0x10, 0xED, + 0x91, 0x88, 0x92, 0xB4, 0x1A, 0x35, 0xD7, 0x81, 0xEF, 0x87, 0x84, 0x27, 0x60, 0xF1, 0xC6, 0x5B, + 0xB5, 0x11, 0x76, 0x40, 0x9F, 0x6B, 0x37, 0x0A, 0x92, 0x4D, 0xC8, 0x40, 0xCC, 0x39, 0xDE, 0xE3, + 0x6D, 0xD2, 0x0C, 0x54, 0x94, 0xC4, 0x01, 0x63, 0x7E, 0xA6, 0xC5, 0xB0, 0x7D, 0x4B, 0xDC, 0x14, + 0x24, 0x72, 0x4E, 0xA4, 0x19, 0x8F, 0xB4, 0xE7, 0x1A, 0x13, 0x5C, 0xC7, 0x5F, 0xDB, 0x9B, 0x0C, + 0x93, 0x25, 0x12, 0x12, 0x8F, 0x72, 0x71, 0x70, 0xAD, 0x07, 0x8D, 0xD5, 0x06, 0xA6, 0xF3, 0x76, + 0x92, 0xC2, 0x32, 0xD2, 0xED, 0x69, 0xB4, 0xEE, 0xF5, 0x46, 0xEE, 0x7C, 0x54, 0xC1, 0x20, 0x9B, + 0x0C, 0x7D, 0xB7, 0x5F, 0xE2, 0x22, 0x10, 0xDD, 0x28, 0xB5, 0x71, 0x68, 0x2F, 0x35, 0x31, 0x94, + 0x2F, 0x35, 0x4D, 0x6A, 0x28, 0x27, 0x87, 0x94, 0x07, 0xF1, 0xA1, 0x46, 0x58, 0x7B, 0x3C, 0x34, + 0x2F, 0xCD, 0x8A, 0xB0, 0x96, 0x6D, 0xCF, 0xFB, 0xE6, 0xDE, 0x73, 0x13, 0xDC, 0x54, 0x89, 0xC1, + 0x2C, 0x8D, 0x1A, 0x2B, 0x29, 0xA9, 0xB0, 0xB2, 0x71, 0x01, 0xCA, 0xA3, 0xD1, 0xC8, 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, 0x57, 0x23, + 0xC1, 0x90, 0x5C, 0x7A, 0xC3, 0x3C, 0x84, 0x78, 0xA3, 0x61, 0xCF, 0x97, 0xAC, 0x7C, 0x37, 0x5A, + 0x82, 0xB6, 0x6A, 0xA0, 0xCF, 0xF6, 0x89, 0x4F, 0x2A, 0x9C, 0xC8, 0xDC, 0xF3, 0x7C, 0x4B, 0x72, + 0x72, 0x2F, 0xFB, 0xFD, 0xBE, 0xBD, 0xEF, 0xAC, 0xDC, 0xAC, 0x4D, 0xD2, 0x14, 0x20, 0xA7, 0x0C, + 0xDB, 0x65, 0x5A, 0x3E, 0xFA, 0xCF, 0x06, 0xC4, 0xA3, 0xD2, 0xD4, 0x62, 0xDA, 0xB8, 0xDF, 0x1B, + 0xF4, 0xFA, 0x4F, 0x46, 0x32, 0x74, 0xCD, 0x6F, 0x3C, 0x32, 0xEE, 0x8F, 0x7B, 0x8F, 0x91, 0xB1, + 0x4A, 0x5B, 0x92, 0x59, 0x88, 0xDB, 0xE6, 0x61, 0xB6, 0x46, 0xD3, 0x62, 0xF3, 0x4F, 0xEA, 0x9A, + 0xEF, 0xF1, 0xBF, 0x46, 0xD7, 0xB5, 0xF2, 0xD4, 0x6A, 0xDB, 0x9E, 0x0F, 0xFA, 0xB6, 0xF7, 0xFB, + 0xB4, 0x3D, 0x1C, 0xCD, 0xAD, 0xE1, 0xF8, 0x69, 0xDA, 0xE6, 0xB4, 0x15, 0xA9, 0x6B, 0xF5, 0x2D, + 0x7D, 0x04, 0x61, 0x45, 0x78, 0xC8, 0x49, 0x3C, 0xF1, 0x2F, 0xC1, 0x8C, 0x16, 0x55, 0xB7, 0xEB, + 0xF7, 0x16, 0x3D, 0x57, 0x65, 0x52, 0xC2, 0x3E, 0xD1, 0xA4, 0x00, 0x98, 0x68, 0x51, 0x90, 0x8F, + 0xB7, 0x4C, 0x0E, 0xC9, 0x26, 0x07, 0x64, 0xE7, 0xC0, 0x9E, 0x77, 0xD9, 0x33, 0x6D, 0xAF, 0x22, + 0xE6, 0x68, 0x68, 0x79, 0xD6, 0x25, 0x13, 0x33, 0x58, 0x2F, 0x77, 0x22, 0x96, 0xAD, 0xDC, 0xA8, + 0x9A, 0x12, 0x0F, 0xEB, 0xF0, 0x8A, 0x27, 0xE0, 0x9C, 0x56, 0x88, 0x50, 0x83, 0x25, 0x26, 0xBE, + 0x2A, 0xF3, 0x9A, 0x20, 0xE2, 0x5F, 0xEE, 0x78, 0x20, 0x38, 0x93, 0xF4, 0xF4, 0xCA, 0x7B, 0xA6, + 0x48, 0x3F, 0xE4, 0xD8, 0x87, 0x56, 0xFA, 0xD7, 0xAF, 0x2B, 0x04, 0xD1, 0x20, 0x43, 0xF8, 0x22, + 0x0D, 0x82, 0x1D, 0xA6, 0xF2, 0xD6, 0x89, 0xB0, 0xB1, 0x45, 0x10, 0x12, 0xF6, 0x9D, 0xBB, 0x6B, + 0x3E, 0xF6, 0xB2, 0x0F, 0xBB, 0x1A, 0x44, 0xC9, 0x86, 0xFE, 0x82, 0xA7, 0xE7, 0x29, 0x8E, 0xFB, + 0x75, 0x32, 0x91, 0xCB, 0xC2, 0xAF, 0xED, 0x4D, 0x12, 0xC6, 0xAE, 0xDF, 0x9E, 0x6F, 0x20, 0x9A, + 0xFD, 0x9D, 0x97, 0xFD, 0x6B, 0xF3, 0x32, 0xE7, 0xA4, 0x9B, 0x0F, 0xE6, 0x9E, 0x79, 0x10, 0xBA, + 0xFB, 0xC3, 0xF9, 0xD8, 0x77, 0x1F, 0xB5, 0xA9, 0xC2, 0x2A, 0xFE, 0xDE, 0xDA, 0x7F, 0x9F, 0xAD, + 0xED, 0x59, 0x73, 0xD3, 0xAF, 0x9E, 0xF4, 0xAD, 0xF9, 0xD0, 0x1F, 0x0F, 0x1E, 0xB7, 0xB5, 0x1C, + 0xC0, 0xFE, 0xDE, 0xDA, 0x7F, 0xF3, 0xAD, 0xB5, 0x87, 0x97, 0xEE, 0xDC, 0xDB, 0xE7, 0x40, 0x5D, + 0x82, 0xF3, 0x32, 0x7A, 0x2B, 0x68, 0x5E, 0x4A, 0x05, 0x04, 0x9A, 0x8B, 0x0A, 0xD3, 0x22, 0x8E, + 0x41, 0xA9, 0x27, 0x0A, 0x4C, 0xAC, 0xFE, 0xF2, 0xB4, 0x1A, 0xD3, 0x41, 0x9D, 0x17, 0x0D, 0x0E, + 0xC3, 0x24, 0xDF, 0xAB, 0xBE, 0x92, 0x34, 0xF4, 0xF0, 0xA5, 0x92, 0x2A, 0x9D, 0xBD, 0xFE, 0xE5, + 0xD8, 0x9F, 0x57, 0x54, 0x3F, 0x30, 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, 0x7A, 0x43, 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, 0x7E, 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, 0xA1, 0x94, 0x5E, 0x18, 0x4D, 0xBF, 0x33, 0x20, + 0xEB, 0xC7, 0x2F, 0xE5, 0x50, 0x9C, 0xDF, 0xB9, 0xDB, 0x27, 0xEE, 0x6D, 0xCA, 0xD6, 0xC8, 0xFB, + 0x06, 0x63, 0xF5, 0x2A, 0x26, 0xF3, 0x52, 0x42, 0x22, 0x0D, 0xB2, 0x7D, 0xA0, 0xCF, 0x0B, 0xD7, + 0xA3, 0xE1, 0xE8, 0x28, 0x3D, 0xBB, 0x57, 0xDC, 0x5F, 0x75, 0xF9, 0x4D, 0xEE, 0x55, 0x97, 0xDF, + 0xEB, 0xB2, 0xDB, 0xA6, 0x2B, 0x3F, 0xB8, 0xD5, 0x58, 0xFB, 0x54, 0xCF, 0x4D, 0xC8, 0x9D, 0xC3, + 0x62, 0x37, 0x94, 0x08, 0xE7, 0xE3, 0x97, 0x33, 0xA6, 0x3E, 0xFB, 0x6F, 0xAB, 0x63, 0x6B, 0x2F, + 0xA2, 0x79, 0x96, 0x38, 0xFC, 0xFD, 0xAA, 0x0B, 0xE4, 0xB3, 0x2B, 0x1E, 0x4D, 0x67, 0x57, 0x2B, + 0x7B, 0xF6, 0x86, 0x6A, 0x19, 0x21, 0xEB, 0x4C, 0xDB, 0xC6, 0x1B, 0xCD, 0x8F, 0xB5, 0x28, 0xA6, + 0xDA, 0xCA, 0xC5, 0x8B, 0x90, 0x68, 0xAB, 0x31, 0x87, 0xEF, 0xE0, 0x4D, 0xB2, 0x16, 0x91, 0x80, + 0xAE, 0x48, 0xAA, 0x34, 0x75, 0x96, 0x5F, 0x0D, 0x2D, 0x09, 0xB1, 0xC0, 0xAB, 0xF1, 0x90, 0xAF, + 0x05, 0x54, 0x8B, 0x53, 0xF8, 0xE2, 0x03, 0x9C, 0x21, 0xC3, 0x54, 0x5B, 0x04, 0xE9, 0xFA, 0x0E, + 0x62, 0xA5, 0x16, 0x2C, 0x80, 0x05, 0x1E, 0x84, 0xB1, 0xE4, 0x06, 0x2B, 0xB2, 0x67, 0x38, 0xA1, + 0xE7, 0x46, 0x30, 0x04, 0x14, 0x03, 0x78, 0xA3, 0x01, 0x7B, 0xA2, 0x4D, 0xB4, 0x2B, 0x57, 0xF3, + 0x42, 0x37, 0xCB, 0xA6, 0x7A, 0x7E, 0x8A, 0xD0, 0xB5, 0x55, 0x4A, 0x16, 0x53, 0x7D, 0x45, 0x69, + 0x92, 0x4D, 0xBA, 0xDD, 0x25, 0xC8, 0xB2, 0x99, 0xC3, 0x89, 0x7A, 0xDD, 0x0D, 0x37, 0x5E, 0x9B, + 0x7F, 0xED, 0xBE, 0xBA, 0x7E, 0xDF, 0x7B, 0xD9, 0xFE, 0xE7, 0xAB, 0xEF, 0x3F, 0xBE, 0xD1, 0x67, + 0x67, 0x0F, 0xBD, 0xEA, 0xBA, 0xA0, 0x61, 0xA9, 0x11, 0xD4, 0xAE, 0x98, 0x9D, 0x81, 0xB0, 0xAE, + 0x05, 0xFE, 0x54, 0xBF, 0x7E, 0xFF, 0xE6, 0xF5, 0xEB, 0x6B, 0xFD, 0xB0, 0x5B, 0xDE, 0xA3, 0xE8, + 0xB3, 0xD7, 0xD0, 0xBA, 0xD2, 0x5E, 0x43, 0x60, 0xCC, 0xB6, 0x19, 0x25, 0x6B, 0xA1, 0xE9, 0x03, + 0x02, 0xDC, 0x44, 0x60, 0xC4, 0x52, 0x28, 0x8D, 0xA5, 0x50, 0x3A, 0x46, 0x53, 0x3E, 0x0F, 0x4B, + 0x9F, 0x78, 0x1C, 0xD7, 0xB5, 0x08, 0xC2, 0xC8, 0x54, 0x5F, 0x6F, 0xB1, 0x31, 0xFB, 0xE5, 0x57, + 0x5D, 0x5B, 0x6F, 0x42, 0x1A, 0x24, 0xB8, 0xF1, 0xF2, 0x93, 0x3E, 0xD3, 0x04, 0x27, 0xA9, 0x31, + 0x1A, 0x69, 0x4A, 0x85, 0x52, 0x17, 0x33, 0xF0, 0x54, 0x8C, 0xCF, 0x51, 0xCA, 0xCE, 0x74, 0x50, + 0xBC, 0x17, 0x06, 0xDE, 0x17, 0x58, 0x23, 0x89, 0x7C, 0x9C, 0xAA, 0xD9, 0x72, 0x74, 0xED, 0xD6, + 0x0D, 0x37, 0x40, 0xF7, 0x91, 0x8D, 0xD5, 0x67, 0x25, 0x13, 0x4A, 0xD2, 0x78, 0x99, 0x62, 0x45, + 0x43, 0x58, 0xE1, 0x6D, 0x90, 0x05, 0xF3, 0x20, 0x0C, 0xE8, 0x76, 0xB2, 0x82, 0x7C, 0x8C, 0x44, + 0x52, 0xF4, 0x24, 0x5D, 0xF2, 0x29, 0xD9, 0x07, 0xB0, 0xFC, 0xA9, 0x0E, 0x86, 0x0D, 0x8B, 0xEF, + 0x4A, 0x16, 0x60, 0xD3, 0x29, 0xFF, 0x7B, 0xA0, 0xF7, 0xE3, 0xAA, 0xE3, 0x97, 0xD7, 0x57, 0x14, + 0xA8, 0xA8, 0xAF, 0x31, 0x87, 0x99, 0xEA, 0xE6, 0xF3, 0x5C, 0xA9, 0xE7, 0xA9, 0xA2, 0xB4, 0xEE, + 0x1F, 0xE2, 0x35, 0x24, 0x86, 0x7E, 0xB3, 0x81, 0xB7, 0x99, 0x0D, 0xA3, 0xE1, 0x86, 0x61, 0x43, + 0x51, 0xC3, 0x07, 0xB2, 0x00, 0x69, 0x57, 0x28, 0x39, 0xF5, 0x0F, 0x66, 0x45, 0x39, 0x73, 0x6E, + 0x3F, 0xA4, 0x04, 0x6C, 0xDF, 0x0F, 0xD2, 0x66, 0x4B, 0x57, 0x24, 0x81, 0x93, 0x3C, 0x8C, 0xCC, + 0x6E, 0x97, 0x92, 0xB2, 0x6F, 0x82, 0x4D, 0x33, 0x8C, 0xE3, 0x9F, 0x6F, 0x03, 0x72, 0xF7, 0x7D, + 0x0C, 0x1A, 0xC2, 0x03, 0x76, 0x1F, 0xFF, 0xC0, 0xF8, 0x14, 0xEC, 0x40, 0x83, 0xB6, 0x81, 0xAE, + 0x6D, 0x51, 0x77, 0xBA, 0xA4, 0xEE, 0x29, 0xD4, 0x36, 0x7C, 0x4E, 0x61, 0x90, 0x0D, 0x8F, 0x2D, + 0x7B, 0xC0, 0x2E, 0x86, 0x53, 0x5D, 0xA4, 0x79, 0x7A, 0xB7, 0xE0, 0x83, 0x43, 0xB7, 0x8C, 0x9D, + 0xE0, 0x63, 0x0D, 0x0A, 0x3E, 0xF8, 0xF9, 0x01, 0x3E, 0x98, 0x8F, 0x23, 0x1F, 0x8B, 0x0B, 0x64, + 0xC3, 0x23, 0x4F, 0x6E, 0xA1, 0x75, 0x2C, 0xBE, 0xDE, 0x09, 0x8E, 0x63, 0xD8, 0x6C, 0xC1, 0x84, + 0xE5, 0xC9, 0xFA, 0xEC, 0x02, 0x14, 0x08, 0x3C, 0x40, 0x8F, 0xA0, 0x8A, 0x99, 0x70, 0x11, 0xA1, + 0x53, 0xAE, 0x48, 0x34, 0x17, 0x9E, 0xCC, 0xE5, 0xEA, 0x13, 0x5F, 0x2B, 0xC3, 0xF3, 0x25, 0x98, + 0xF9, 0x26, 0x30, 0x53, 0x73, 0xE9, 0x2A, 0xA7, 0xC4, 0xEB, 0x3E, 0x69, 0xBC, 0x2A, 0x75, 0x17, + 0x6D, 0xA7, 0x2B, 0xED, 0x08, 0x1F, 0x92, 0x82, 0x7F, 0x29, 0xDD, 0xF2, 0xEB, 0xD2, 0xCE, 0x8B, + 0x83, 0x00, 0x1A, 0x21, 0x87, 0x64, 0x66, 0x84, 0x2B, 0xD5, 0x1C, 0x6E, 0xC0, 0xCA, 0x80, 0xF7, + 0x0A, 0xDB, 0x67, 0x3F, 0x83, 0x1F, 0xE4, 0x5F, 0xAE, 0x41, 0x4B, 0xF2, 0x4B, 0xC9, 0x80, 0x2A, + 0x6D, 0x62, 0x45, 0xAC, 0x55, 0x48, 0x2A, 0x26, 0x43, 0x07, 0xC8, 0x71, 0xE2, 0x33, 0xDA, 0x2A, + 0x1B, 0xC7, 0xE3, 0x82, 0x5C, 0xCF, 0x11, 0xDC, 0xE1, 0x91, 0x96, 0xFB, 0x63, 0x06, 0xE9, 0xE6, + 0x26, 0x2B, 0x34, 0x7A, 0xF0, 0x7E, 0x8E, 0x37, 0x16, 0xC8, 0x27, 0x21, 0xFE, 0x23, 0xC3, 0xFD, + 0x07, 0x80, 0xAF, 0xE4, 0xBD, 0x47, 0x71, 0xF0, 0xEE, 0x28, 0x0A, 0x2A, 0xF6, 0xF2, 0x34, 0xE4, + 0x03, 0xDE, 0x07, 0x18, 0xC0, 0x71, 0xEE, 0x10, 0xFD, 0x70, 0x3D, 0xEA, 0x8C, 0x8F, 0x81, 0xBE, + 0xC5, 0x5D, 0x0E, 0x7E, 0xF8, 0xB1, 0x1E, 0xFE, 0x72, 0xCE, 0x70, 0x84, 0x8C, 0xD8, 0xF0, 0x75, + 0xB6, 0xD4, 0x8F, 0xB3, 0x9F, 0x7D, 0x20, 0xB0, 0x79, 0x70, 0x06, 0x8E, 0x96, 0x79, 0xEC, 0xBD, + 0x73, 0x03, 0xDA, 0x81, 0xFF, 0xC0, 0xA9, 0x80, 0x89, 0xC2, 0xCA, 0x83, 0x1C, 0x89, 0x72, 0xCF, + 0xE1, 0x3D, 0x87, 0xC6, 0x5F, 0xDD, 0x74, 0xEE, 0x7E, 0x90, 0x85, 0x26, 0x70, 0xFE, 0xCD, 0xFD, + 0x88, 0xA5, 0x28, 0x65, 0x1B, 0x28, 0x65, 0x2D, 0x75, 0x5D, 0x3C, 0x2D, 0x85, 0x9E, 0x55, 0x6F, + 0xF6, 0x06, 0x44, 0xA7, 0xC1, 0x02, 0x0E, 0xEE, 0x98, 0xAD, 0x40, 0xF0, 0xEF, 0xD5, 0x18, 0x5A, + 0x91, 0x2E, 0xEA, 0x7C, 0x0D, 0x62, 0x25, 0xA5, 0x6E, 0x44, 0x0F, 0x9D, 0x9F, 0xC0, 0x21, 0x69, + 0x26, 0xB3, 0x8F, 0x70, 0xF0, 0x9D, 0x88, 0xE5, 0x55, 0x42, 0xA1, 0x7A, 0x49, 0x20, 0xAD, 0x81, + 0x93, 0xE7, 0x8B, 0xFC, 0x8C, 0xE7, 0xE6, 0xCF, 0xBC, 0x51, 0xA8, 0xBC, 0xB8, 0xF2, 0x2F, 0xD4, + 0xB6, 0x4A, 0xCF, 0x17, 0xE8, 0x3D, 0xF4, 0xDD, 0x01, 0x50, 0x3C, 0x42, 0xA8, 0x44, 0x90, 0xA8, + 0x82, 0xC9, 0xB6, 0x87, 0x85, 0xC3, 0xE0, 0x79, 0x44, 0x97, 0xC2, 0xE5, 0xCB, 0x4E, 0x26, 0x6D, + 0xFF, 0x98, 0xDB, 0x14, 0x71, 0x71, 0x33, 0x5F, 0x07, 0xF4, 0x03, 0xF9, 0xBF, 0x0D, 0x98, 0x1C, + 0x46, 0x33, 0xE1, 0x15, 0xBC, 0xBD, 0x16, 0x3C, 0x20, 0xD1, 0x0D, 0x12, 0x3A, 0x5B, 0x6C, 0x22, + 0x56, 0x6C, 0x01, 0x5F, 0xB8, 0x9D, 0xBB, 0x10, 0x09, 0x77, 0xB7, 0x70, 0x46, 0x06, 0x52, 0xC5, + 0xF9, 0x75, 0x83, 0x4E, 0xBD, 0x4D, 0x8A, 0x45, 0x14, 0x84, 0xEC, 0x0E, 0x1C, 0x3B, 0x03, 0xDA, + 0xD4, 0xBB, 0x7A, 0xCB, 0x88, 0xA6, 0xF0, 0x30, 0x82, 0xA9, 0xE5, 0x80, 0xB6, 0x9A, 0xE4, 0x02, + 0xE9, 0x7C, 0x21, 0x6F, 0x83, 0xC7, 0xD2, 0x86, 0x96, 0xCB, 0xF9, 0x49, 0x57, 0xD8, 0x4C, 0x1B, + 0xDD, 0x86, 0xA3, 0x1D, 0x8F, 0xE8, 0x9F, 0xF4, 0x59, 0x97, 0x79, 0x81, 0xEE, 0x04, 0x57, 0xB4, + 0x13, 0x92, 0x68, 0x49, 0x57, 0x6D, 0xCB, 0x69, 0x45, 0x17, 0x53, 0xFA, 0x4B, 0xF0, 0xEB, 0x05, + 0xCE, 0x7C, 0x64, 0xC6, 0x23, 0x13, 0xEA, 0x17, 0xD1, 0x85, 0xFE, 0xD0, 0xA4, 0xFA, 0x05, 0xE7, + 0x9E, 0xFB, 0xBB, 0x90, 0xC2, 0x08, 0x2E, 0x2E, 0x9C, 0x94, 0xD0, 0x4D, 0x1A, 0x69, 0x6C, 0x5A, + 0xD5, 0x39, 0xF5, 0x7D, 0xAE, 0x48, 0xB0, 0xAF, 0x6C, 0xF5, 0x39, 0x00, 0xC3, 0x51, 0x94, 0x59, + 0x64, 0x13, 0x0D, 0xBB, 0xDF, 0x90, 0x71, 0x9C, 0x7D, 0x96, 0xD9, 0x44, 0x03, 0xB3, 0x09, 0xCB, + 0x1E, 0xE3, 0xDF, 0x06, 0x2C, 0x5A, 0x9D, 0x4A, 0x24, 0x05, 0x8D, 0x81, 0xDD, 0x80, 0x60, 0xDE, + 0xB0, 0xE0, 0x01, 0xE1, 0xBF, 0x31, 0x6C, 0x60, 0xF8, 0xC7, 0x87, 0xE4, 0x3D, 0x28, 0x78, 0x8F, + 0x1A, 0xC2, 0x14, 0x1B, 0x18, 0xD6, 0xE1, 0xE4, 0xEA, 0x3B, 0x0D, 0xAD, 0x3B, 0x13, 0x3A, 0xAB, + 0x72, 0xAC, 0xE7, 0x61, 0x97, 0x79, 0xB0, 0xCC, 0xA0, 0x8E, 0x4B, 0xCF, 0xE4, 0x5C, 0xC6, 0x47, + 0xE4, 0x1A, 0x8E, 0x0A, 0x9E, 0x80, 0xAB, 0x67, 0x49, 0x66, 0x97, 0x79, 0x5A, 0x26, 0x67, 0x8A, + 0x4F, 0xC1, 0x75, 0xAC, 0x72, 0xED, 0x3F, 0x86, 0xA9, 0x7D, 0x59, 0xCB, 0xA4, 0x77, 0xE6, 0x72, + 0xFB, 0x9C, 0x4B, 0xBF, 0xC7, 0x45, 0x1B, 0x71, 0xC9, 0x46, 0x39, 0x4F, 0x85, 0xE5, 0xF0, 0x5C, + 0x9E, 0xC3, 0x3F, 0x81, 0xE7, 0xF8, 0x8F, 0xE0, 0xC9, 0xF3, 0x3F, 0xC5, 0xC0, 0xF1, 0x4C, 0x2F, + 0xED, 0x9B, 0x1B, 0xE9, 0xF9, 0xF6, 0x6D, 0xF7, 0xE1, 0x4F, 0x03, 0x82, 0x33, 0xF8, 0xA3, 0xE6, + 0x4F, 0x1B, 0x3F, 0x8D, 0x8C, 0x9E, 0xF6, 0xD6, 0x36, 0xC6, 0xDA, 0xDB, 0x91, 0x61, 0xF5, 0xD8, + 0xBB, 0xA9, 0xBD, 0xB5, 0xC4, 0x63, 0x6C, 0x58, 0x16, 0x7F, 0x0C, 0x78, 0xE3, 0x10, 0x1E, 0x26, + 0x7B, 0x5C, 0x1A, 0xD6, 0x88, 0xBD, 0x5F, 0xB2, 0x26, 0x1B, 0x86, 0xDB, 0xE2, 0x61, 0x1B, 0xD6, + 0x98, 0x3D, 0xC6, 0xAC, 0x6D, 0x88, 0x5C, 0x87, 0xDA, 0x57, 0x5C, 0x60, 0x1A, 0x7F, 0x81, 0x15, + 0xB2, 0xB3, 0x6A, 0x83, 0xA7, 0xBB, 0x0D, 0xB6, 0xD2, 0xDA, 0x85, 0xF2, 0xB4, 0xE6, 0x33, 0x1E, + 0x10, 0x48, 0x6B, 0xA7, 0x20, 0xC9, 0xC5, 0x94, 0x30, 0xF4, 0x51, 0x71, 0x44, 0x67, 0x29, 0x9E, + 0xA1, 0x03, 0x8E, 0xE8, 0xAD, 0x82, 0x07, 0x9C, 0x65, 0xB1, 0xFC, 0x7C, 0x0D, 0xF9, 0x69, 0xB4, + 0xCC, 0x9A, 0xC4, 0xA0, 0x52, 0x69, 0x80, 0x0B, 0xA4, 0x43, 0xE3, 0xB7, 0xF1, 0x1D, 0x49, 0x7F, + 0x80, 0xDC, 0xA0, 0xD9, 0x02, 0x98, 0xA5, 0x95, 0x16, 0x72, 0x45, 0xBF, 0x6D, 0x5B, 0x13, 0x32, + 0xA3, 0xDF, 0x5A, 0x13, 0xB3, 0x60, 0x8B, 0xB5, 0x3F, 0x97, 0x7A, 0x2B, 0x96, 0x69, 0xB1, 0x0C, + 0x11, 0x45, 0x44, 0xB4, 0x81, 0x5C, 0x1E, 0x00, 0x73, 0xD1, 0x84, 0xA7, 0x7A, 0x12, 0xBC, 0x66, + 0x83, 0x26, 0x9A, 0x7E, 0x41, 0x3A, 0x9C, 0xC0, 0xA0, 0x17, 0xE5, 0x21, 0xBF, 0xA9, 0x5F, 0x6E, + 0x62, 0xEA, 0x86, 0x1A, 0x2F, 0x96, 0x33, 0x22, 0x8A, 0x0D, 0xA7, 0x69, 0x20, 0xC0, 0xFB, 0x2A, + 0x09, 0x44, 0x6C, 0xFF, 0x34, 0xC5, 0x3B, 0xCF, 0xDB, 0x24, 0xBC, 0x4A, 0xAB, 0xE9, 0x6C, 0xE8, + 0xD5, 0x9A, 0x40, 0x1C, 0xD4, 0xD6, 0x41, 0x04, 0x06, 0xD3, 0x60, 0x99, 0x18, 0x47, 0x8C, 0x15, + 0x58, 0xD4, 0xB4, 0x71, 0x09, 0x9F, 0x78, 0x6C, 0x6B, 0xE0, 0x0C, 0x71, 0x4E, 0x0F, 0xB8, 0x0E, + 0x1B, 0xC8, 0x88, 0xC5, 0x21, 0xA2, 0xDA, 0xFF, 0x5C, 0x37, 0xFC, 0xD8, 0xDB, 0xAC, 0x61, 0x0F, + 0x3B, 0x4B, 0x42, 0x5F, 0x85, 0x04, 0x3F, 0x7E, 0xBF, 0x7D, 0x03, 0x7B, 0x27, 0x92, 0xEC, 0x56, + 0x27, 0x88, 0x22, 0x92, 0xFE, 0x78, 0xF3, 0xD3, 0xDB, 0x29, 0x35, 0x50, 0x93, 0x06, 0x6C, 0xF3, + 0x33, 0x35, 0xF8, 0x71, 0x25, 0x47, 0xA5, 0x78, 0x08, 0xB1, 0x87, 0xBE, 0xC1, 0x52, 0xCB, 0xBB, + 0x05, 0x46, 0x45, 0xA3, 0xD4, 0xC7, 0xC3, 0x96, 0xDD, 0x72, 0xD8, 0xEA, 0x68, 0x2A, 0xBD, 0x4C, + 0xBD, 0x57, 0x3E, 0x11, 0xB0, 0x4A, 0x71, 0x17, 0x86, 0x90, 0xA6, 0x69, 0x44, 0x17, 0x56, 0xEB, + 0xE1, 0x38, 0x86, 0x61, 0x11, 0x82, 0x99, 0xE2, 0xAD, 0x45, 0x4C, 0x03, 0xB3, 0x0C, 0x31, 0x25, + 0x01, 0x6C, 0x68, 0xCC, 0xE0, 0x2C, 0x80, 0x09, 0xA9, 0xCC, 0x37, 0xF5, 0x3D, 0x61, 0x77, 0x06, + 0x59, 0x07, 0xE4, 0xA3, 0x4D, 0x69, 0x72, 0x25, 0xD3, 0xAD, 0x5A, 0x75, 0x07, 0x33, 0x69, 0x83, + 0xB2, 0x47, 0x6B, 0xDF, 0x62, 0xC9, 0x00, 0xEA, 0x29, 0x98, 0x9A, 0x10, 0xBC, 0x25, 0x3F, 0xAE, + 0x0B, 0x07, 0xE2, 0x68, 0x4B, 0x6F, 0x5B, 0xA0, 0x57, 0x4E, 0xDF, 0x14, 0xFD, 0x10, 0x75, 0x3B, + 0x78, 0x3A, 0x6D, 0xBD, 0x78, 0xD1, 0x64, 0xCA, 0xBA, 0xF9, 0x30, 0x13, 0x46, 0xC1, 0xB2, 0x6E, + 0x80, 0x18, 0x15, 0x57, 0x14, 0xB8, 0xA9, 0x81, 0x18, 0x6D, 0xF6, 0x89, 0x16, 0x28, 0x63, 0x19, + 0x36, 0xA0, 0x84, 0x61, 0x5B, 0x88, 0x35, 0x36, 0x7E, 0x1E, 0xF2, 0xC7, 0x88, 0xB5, 0x59, 0x88, + 0x0F, 0x6F, 0x2D, 0x5B, 0xBC, 0x5B, 0x1A, 0x0E, 0xB3, 0xCE, 0x40, 0x0C, 0xBC, 0x52, 0xD0, 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, 0xBA, 0x72, 0x89, 0x39, 0xAB, 0x81, 0xE0, 0x64, + 0x0A, 0x56, 0x3D, 0xCE, 0xCA, 0x32, 0xCF, 0xE0, 0x05, 0x6B, 0x3E, 0xE0, 0xD3, 0xAF, 0xF0, 0xE9, + 0x3F, 0x91, 0xCF, 0xB8, 0xC2, 0x67, 0x7C, 0x06, 0x1F, 0x59, 0x3B, 0x60, 0xF9, 0x13, 0x2C, 0xB3, + 0x71, 0x75, 0xF3, 0x52, 0xE4, 0x6A, 0x9F, 0x44, 0xB2, 0xF6, 0xA9, 0x91, 0x57, 0x48, 0x65, 0x19, + 0x3D, 0xB9, 0x77, 0xC0, 0x7C, 0x5D, 0x51, 0x47, 0x6C, 0x80, 0x1F, 0x33, 0xD0, 0x55, 0xCC, 0x04, + 0xED, 0xEC, 0xA2, 0x01, 0x49, 0xB9, 0x9B, 0x82, 0x2B, 0x4F, 0x3F, 0x83, 0x00, 0xD1, 0x97, 0x52, + 0x56, 0x5D, 0x94, 0x23, 0x67, 0x0D, 0x9C, 0xB9, 0x42, 0xCC, 0x75, 0x2E, 0xF2, 0x61, 0xAC, 0x29, + 0xDE, 0xBC, 0x9C, 0x81, 0x6C, 0x5C, 0xCA, 0x8A, 0x3D, 0x8A, 0xB1, 0x7C, 0x84, 0x34, 0x3B, 0xF3, + 0x79, 0x43, 0x9D, 0xEF, 0x93, 0x28, 0x1C, 0x7D, 0xD2, 0x15, 0x57, 0x7E, 0x09, 0xF1, 0x83, 0x92, + 0x26, 0x43, 0xAA, 0xB2, 0xE8, 0x7A, 0xA3, 0x85, 0x99, 0x26, 0x72, 0x56, 0xD3, 0xC5, 0x92, 0x54, + 0x38, 0x1F, 0xF7, 0x49, 0x7C, 0x43, 0x5F, 0x28, 0xFC, 0x2A, 0x04, 0xBF, 0x0A, 0xAB, 0x7E, 0x15, + 0x0A, 0xBF, 0x9A, 0x56, 0xFD, 0x2A, 0xFC, 0x43, 0xFD, 0x4A, 0xF1, 0xAA, 0x4B, 0x1E, 0x9E, 0x2F, + 0x31, 0xD0, 0x42, 0x90, 0x86, 0x78, 0x2C, 0xDE, 0x06, 0x18, 0x72, 0xFB, 0xE8, 0x45, 0x7D, 0xF4, + 0xBB, 0x01, 0x73, 0x3E, 0x9B, 0x0D, 0xC5, 0x07, 0x06, 0x6A, 0x74, 0xC5, 0x1E, 0xA3, 0x1F, 0xB0, + 0x77, 0x9B, 0x7B, 0x22, 0xF4, 0x9F, 0x17, 0xA7, 0x0B, 0xA3, 0xD2, 0x71, 0x5B, 0xCA, 0x27, 0x00, + 0x2D, 0xDF, 0xFE, 0x3C, 0xAB, 0x39, 0xBC, 0x99, 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, 0xAF, 0x12, 0x9A, + 0xFA, 0x0F, 0xFC, 0x83, 0xE6, 0x63, 0x17, 0x8E, 0x89, 0x17, 0xB8, 0x99, 0x3C, 0x3D, 0x00, 0x73, + 0x2C, 0xE5, 0x4F, 0x6C, 0x10, 0x81, 0x0C, 0xF6, 0x80, 0xA7, 0x4C, 0xC0, 0x4E, 0xB1, 0x85, 0x31, + 0xB0, 0x81, 0x71, 0xBA, 0x3D, 0xC1, 0x1B, 0xC6, 0x94, 0xD9, 0x2B, 0x05, 0x60, 0x71, 0x52, 0x4B, + 0x52, 0x88, 0x6C, 0x70, 0xB6, 0x7D, 0xCF, 0xCB, 0x35, 0xEC, 0xA2, 0xA1, 0xE0, 0xCD, 0x0A, 0x46, + 0x90, 0x13, 0x80, 0x6A, 0xA3, 0x4D, 0x18, 0x3E, 0x9B, 0x92, 0xCA, 0x3C, 0x9E, 0x64, 0x08, 0xF3, + 0x74, 0xC0, 0x09, 0xD7, 0xCD, 0x96, 0x32, 0x9D, 0x3A, 0x94, 0x45, 0x53, 0x9E, 0x4A, 0x44, 0xE4, + 0x4E, 0xFB, 0x9F, 0x9F, 0xDE, 0xFE, 0x48, 0x69, 0x22, 0x4E, 0xF0, 0x70, 0xA0, 0xD6, 0xBB, 0xCC, + 0x04, 0xBE, 0xE5, 0x3F, 0x85, 0x98, 0xC2, 0x9A, 0x20, 0x6E, 0x42, 0x26, 0x85, 0xAD, 0xBC, 0x6C, + 0x75, 0x41, 0x22, 0x2F, 0xF6, 0xC9, 0xC7, 0x0F, 0x6F, 0x9A, 0xB4, 0x65, 0xB0, 0x4E, 0x96, 0x34, + 0xA8, 0x1D, 0x6A, 0xE2, 0x72, 0x7C, 0x73, 0x45, 0xD1, 0xB6, 0xD5, 0x61, 0xAE, 0xD2, 0x29, 0x2A, + 0x59, 0xA2, 0xAA, 0x15, 0xC2, 0x9A, 0xA3, 0x4E, 0x1C, 0xC1, 0xE2, 0xFC, 0x2D, 0xA6, 0x4A, 0xC4, + 0x5B, 0xE1, 0xAF, 0x0B, 0xA7, 0x79, 0x6E, 0xD0, 0xDA, 0x41, 0xC6, 0xD9, 0x9F, 0x4E, 0xA3, 0x0E, + 0x1B, 0x83, 0xC9, 0x26, 0x69, 0x41, 0x93, 0x6D, 0x9A, 0xD8, 0xC8, 0xD3, 0x2B, 0xA9, 0xE3, 0xFF, + 0xBA, 0x7E, 0xF7, 0x33, 0x20, 0x7A, 0x0A, 0x09, 0x2E, 0x8E, 0xCF, 0x92, 0x38, 0xCA, 0xC8, 0x0D, + 0xB9, 0xA7, 0x27, 0x0C, 0xF6, 0x84, 0x88, 0xA2, 0xDA, 0x66, 0xD4, 0xA6, 0xC4, 0x7B, 0x12, 0xC2, + 0x36, 0x56, 0xCA, 0x23, 0x7B, 0x5C, 0x4D, 0x42, 0xA2, 0xA6, 0xFE, 0x9F, 0xAF, 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, 0xDF, 0xE0, 0x77, 0x45, 0xCA, 0xFC, 0x1F, 0x90, 0xBD, 0x1E, 0x5F, 0x17, 0xDE, 0xB6, + 0xB4, 0x3A, 0x3C, 0xA3, 0x3D, 0xE1, 0x7E, 0xE5, 0x7B, 0x20, 0x49, 0x20, 0x4A, 0xA1, 0x80, 0x6A, + 0x5A, 0xA7, 0xD3, 0xD1, 0x2F, 0xF0, 0xF0, 0xF0, 0x1A, 0xAF, 0xFF, 0x9B, 0x66, 0x0B, 0xF3, 0xDD, + 0xFD, 0x9E, 0x8B, 0x74, 0x12, 0x06, 0x64, 0xF1, 0xB6, 0xC5, 0x31, 0x07, 0x8F, 0x0E, 0xE6, 0xB3, + 0xA9, 0xAC, 0xB7, 0xB4, 0x76, 0x4F, 0x96, 0x09, 0x45, 0x3A, 0x01, 0x28, 0x6C, 0xE1, 0xC7, 0x0D, + 0xCE, 0x29, 0x7C, 0xE3, 0x75, 0x9C, 0xAE, 0x5F, 0xBA, 0xD4, 0x75, 0xA2, 0x8E, 0x9B, 0x24, 0xB8, + 0x49, 0x1C, 0x8E, 0xD4, 0x3C, 0xBB, 0x9A, 0x72, 0x52, 0x35, 0xD9, 0xDC, 0xF1, 0x90, 0x89, 0x65, + 0x1D, 0xC3, 0x57, 0x33, 0xF7, 0x8B, 0x50, 0xE0, 0xEB, 0xB5, 0x5E, 0x30, 0xF7, 0x8D, 0x90, 0x87, + 0x4B, 0xA3, 0x98, 0xAF, 0x28, 0x69, 0x1B, 0xA1, 0x71, 0xC8, 0xA0, 0xC5, 0x14, 0xED, 0xD6, 0x78, + 0xB2, 0xE3, 0x0A, 0x4B, 0x7B, 0xFF, 0xEE, 0xFA, 0x06, 0x4F, 0x13, 0x8C, 0x8F, 0xCE, 0x2C, 0xCE, + 0xED, 0x70, 0x15, 0x76, 0x20, 0x32, 0xBD, 0xBA, 0x05, 0x8E, 0x6F, 0x01, 0x90, 0x09, 0x00, 0x2C, + 0x6A, 0x87, 0x17, 0x9D, 0x01, 0x46, 0x8C, 0x67, 0x16, 0x0E, 0x8D, 0x23, 0x1C, 0x5A, 0xF1, 0x38, + 0xE6, 0x5E, 0x53, 0x37, 0xF7, 0xAF, 0xA7, 0xED, 0xD5, 0x13, 0x76, 0x29, 0xF7, 0xB9, 0xF3, 0x2C, + 0x4B, 0x4C, 0xA7, 0x3B, 0x35, 0x3E, 0xAA, 0xA0, 0x81, 0x5B, 0x46, 0x03, 0xE1, 0xBC, 0xEC, 0x5F, + 0x24, 0x34, 0xF5, 0xEF, 0xC0, 0xF9, 0xD8, 0x6F, 0xE1, 0xF1, 0x4C, 0x07, 0x1B, 0xE0, 0x3F, 0x83, + 0x63, 0x36, 0x28, 0x86, 0x79, 0x6D, 0xD4, 0xDA, 0x17, 0x7E, 0xAB, 0x5E, 0x10, 0xFC, 0xA9, 0x9E, + 0xBB, 0xB8, 0x3B, 0xC7, 0x77, 0xF1, 0x92, 0x40, 0x8D, 0x9C, 0x0F, 0x7B, 0x2D, 0x48, 0x79, 0x10, + 0xEB, 0x2A, 0x17, 0x37, 0xDA, 0xB7, 0x7A, 0x4B, 0xCA, 0x7D, 0x7C, 0x13, 0xEE, 0x7E, 0xA7, 0x73, + 0x17, 0xD7, 0x2F, 0x4F, 0x33, 0x02, 0x65, 0xFE, 0x27, 0x90, 0x73, 0xC5, 0x9D, 0x88, 0x49, 0xE7, + 0xAB, 0xFC, 0xC4, 0x58, 0xF1, 0x73, 0x01, 0x39, 0x91, 0xF8, 0x51, 0xD5, 0x54, 0xC7, 0x5F, 0x55, + 0x9D, 0xF6, 0x0C, 0xB6, 0xFD, 0x27, 0x10, 0x4C, 0x22, 0x52, 0x19, 0xC5, 0x8C, 0x07, 0x01, 0x4A, + 0xEF, 0xEA, 0x8F, 0x04, 0x26, 0x8E, 0x4B, 0x05, 0xDD, 0x23, 0xF0, 0x88, 0xFF, 0xFE, 0x03, 0x96, + 0xF2, 0xD7, 0x40, 0x92, 0x6A, 0x61, 0xE7, 0x82, 0xD2, 0xC1, 0xEE, 0x9E, 0xB8, 0x67, 0x3B, 0xC1, + 0x46, 0xDE, 0xB8, 0x3D, 0xCD, 0xC4, 0xFE, 0x10, 0x07, 0x39, 0xC6, 0x44, 0x5E, 0x38, 0x27, 0xF7, + 0x67, 0x7A, 0x97, 0x04, 0xD8, 0x3F, 0xCD, 0x17, 0x0F, 0xC9, 0x15, 0x19, 0x9D, 0x83, 0xBC, 0xAE, + 0x82, 0xE4, 0x88, 0x3B, 0x3A, 0x16, 0x7C, 0x64, 0xED, 0xF1, 0xC5, 0x0B, 0xBD, 0x5F, 0xFE, 0xAA, + 0xF6, 0xFE, 0xF6, 0x9B, 0xC0, 0x7C, 0x81, 0x75, 0x0B, 0x17, 0x6C, 0xDC, 0xD7, 0x5B, 0x86, 0x6E, + 0xC3, 0xE1, 0x56, 0x8E, 0x6A, 0x95, 0x07, 0x79, 0x6E, 0xE4, 0x81, 0x84, 0x18, 0x16, 0x1C, 0x16, + 0x37, 0x70, 0xCE, 0x9E, 0x3A, 0x9E, 0xC3, 0xA5, 0x11, 0x4D, 0x4D, 0xF0, 0xC1, 0x87, 0x1C, 0xDB, + 0x09, 0x3A, 0x58, 0x7B, 0xEC, 0x9B, 0x06, 0x9D, 0x66, 0x84, 0xBE, 0x41, 0x53, 0x01, 0x2D, 0x37, + 0x15, 0x6B, 0x8F, 0x2E, 0xA6, 0xD6, 0xC3, 0x00, 0xC1, 0xB7, 0x26, 0x3A, 0xC7, 0x0E, 0x0B, 0x93, + 0xEE, 0x5B, 0xED, 0xC8, 0x88, 0x66, 0x7D, 0x13, 0x8E, 0xEC, 0x1E, 0x58, 0x74, 0x9A, 0xCF, 0x0F, + 0xA9, 0x7E, 0x18, 0xF3, 0xBB, 0x59, 0xD0, 0x31, 0x9A, 0x0F, 0x1E, 0x31, 0x0C, 0x8B, 0xF4, 0xCA, + 0xE1, 0xB2, 0xA4, 0x3A, 0x0C, 0x95, 0x8F, 0x8A, 0xA5, 0x45, 0x30, 0x15, 0xB8, 0xF1, 0x16, 0x6F, + 0x3A, 0x9B, 0x27, 0xDC, 0xF9, 0x9C, 0x04, 0xFD, 0x38, 0xAD, 0xBC, 0xC4, 0x3E, 0x40, 0x61, 0xF6, + 0xDB, 0x73, 0xA5, 0x24, 0x5F, 0xC9, 0xE4, 0x4F, 0xC9, 0x73, 0x94, 0x27, 0x43, 0x76, 0x61, 0xB3, + 0xA7, 0xE9, 0x95, 0x3B, 0x67, 0xB1, 0x91, 0xE2, 0x50, 0x67, 0x9C, 0x88, 0xBA, 0x75, 0x97, 0xC2, + 0x15, 0x6A, 0xBC, 0x35, 0x65, 0xC3, 0xBE, 0xFD, 0x78, 0xFD, 0xEA, 0x83, 0x7A, 0x62, 0xC3, 0xAC, + 0x04, 0xC4, 0x8B, 0x28, 0x64, 0x2B, 0x17, 0xFA, 0x8B, 0xF7, 0xDF, 0x5D, 0x5F, 0xFF, 0xF3, 0xDD, + 0x87, 0x97, 0xF5, 0x43, 0x28, 0x0E, 0xB9, 0xFE, 0xF8, 0xFD, 0x4F, 0x6F, 0x6E, 0xA6, 0x5B, 0xCC, + 0x2A, 0x83, 0x3A, 0xC4, 0x0F, 0x1E, 0x38, 0xB9, 0xC1, 0xB1, 0x2D, 0x50, 0x8E, 0x6D, 0x2F, 0x5E, + 0x00, 0x84, 0x3F, 0x83, 0x26, 0xE9, 0x9A, 0x65, 0x0B, 0xD8, 0x1B, 0x81, 0x7A, 0x76, 0x8A, 0x58, + 0xD8, 0x08, 0xE4, 0xD9, 0x09, 0x95, 0xAA, 0x16, 0xB0, 0x21, 0x24, 0x39, 0x77, 0x41, 0xE4, 0xC7, + 0x77, 0x35, 0xD1, 0xE2, 0xF8, 0xED, 0x89, 0x73, 0xD5, 0x15, 0xD7, 0xD3, 0x57, 0x5D, 0xF1, 0x83, + 0x19, 0xF6, 0xFF, 0xCC, 0xF9, 0x7F, 0xE5, 0xCC, 0x32, 0xCA, 0x3A, 0x47, 0x00, 0x00 +}; + +size_t a_SIZE = 4862; +const char * a_NAME = "ool.html.gz"; diff --git a/embedded/tool.html.gz b/embedded/tool.html.gz index de11ef01..4478b2f1 100644 Binary files a/embedded/tool.html.gz and b/embedded/tool.html.gz differ diff --git a/embedded/www/js/script.js b/embedded/www/js/script.js index f6f7428e..c24253e3 100644 --- a/embedded/www/js/script.js +++ b/embedded/www/js/script.js @@ -131,6 +131,9 @@ var formData = new FormData(); formData.append('path', currentpath); for (var i3 = 0; i3 < files.length; i3++) { var file = files[i3]; +var arg = currentpath + file.name + "S"; + //append file size first to check updload is complete + formData.append(arg, file.size); formData.append('myfiles[]', file, currentpath+file.name);} var xmlhttp = new XMLHttpRequest(); xmlhttp.open('POST', '/files', true); @@ -175,6 +178,9 @@ document.getElementById('prgfw').style.visibility = "visible"; var formData = new FormData(); for (var i4 = 0; i4 < files.length; i4++) { var file = files[i4]; +var arg = "/" + file.name + "S"; + //append file size first to check updload is complete + formData.append(arg, file.size); formData.append('myfile[]', file, "/"+file.name);} var xmlhttp = new XMLHttpRequest(); xmlhttp.open('POST', '/updatefw', true); diff --git a/embedded/www/tool.html b/embedded/www/tool.html index 33c5cecd..ea68774e 100644 --- a/embedded/www/tool.html +++ b/embedded/www/tool.html @@ -6,11 +6,12 @@
- V1.1 + V1.2   

It seems you do not have any index.html neither index.html.gz, please upload it or update your firmware if necessary.

+ you can find one here : https://github.com/luc-github/ESP3D-WEBUI
diff --git a/esp3d/bridge.cpp b/esp3d/bridge.cpp deleted file mode 100644 index 58d6d34d..00000000 --- a/esp3d/bridge.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/* - bridge.cpp - esp3d bridge serial/tcp class - - 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 -*/ - -#include "config.h" -#include "bridge.h" -#include "command.h" -#include "webinterface.h" - -#ifdef TCP_IP_DATA_FEATURE -WiFiServer * data_server; -WiFiClient serverClients[MAX_SRV_CLIENTS]; -#endif - -bool BRIDGE::header_sent = false; -String BRIDGE::buffer_web = ""; -void BRIDGE::print (const __FlashStringHelper *data, tpipe output) -{ - String tmp = data; - BRIDGE::print(tmp.c_str(), output); -} -void BRIDGE::print (String & data, tpipe output) -{ - BRIDGE::print(data.c_str(), output); -} -void BRIDGE::print (const char * data, tpipe output) -{ - switch(output) { - case SERIAL_PIPE: - header_sent = false; - ESP_SERIAL_OUT.print(data); - break; -#ifdef TCP_IP_DATA_FEATURE - case TCP_PIPE: - header_sent = false; - BRIDGE::send2TCP(data); - break; -#endif - case WEB_PIPE: - if (!header_sent) { - 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) { - //send data - web_interface->web_server.sendContent(buffer_web); - //reset buffer - buffer_web=""; - } - break; - default: - break; - } -} -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) -{ - 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) -{ - BRIDGE::print(data,output); -#ifdef TCP_IP_DATA_FEATURE - BRIDGE::print("\r",output); -#endif - BRIDGE::print("\n",output); -} -void BRIDGE::flush (tpipe output) -{ - switch(output) { - case SERIAL_PIPE: - ESP_SERIAL_OUT.flush(); - break; -#ifdef TCP_IP_DATA_FEATURE - case TCP_PIPE: - break; -#endif - case WEB_PIPE: - if(header_sent) { - //send data - web_interface->web_server.sendContent(buffer_web); - //close line - web_interface->web_server.sendContent(""); - } - break; - default: - break; - } - header_sent = false; - buffer_web = String(); -} - - -#ifdef TCP_IP_DATA_FEATURE -void BRIDGE::send2TCP(const __FlashStringHelper *data) -{ - String tmp = data; - BRIDGE::send2TCP(tmp.c_str()); -} -void BRIDGE::send2TCP(String data) -{ - BRIDGE::send2TCP(data.c_str()); -} -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)); - delay(0); - } - } -} -#endif - -bool BRIDGE::processFromSerial2TCP() -{ - uint8_t i; - //check UART for data - if(ESP_SERIAL_OUT.available()) { - size_t len = ESP_SERIAL_OUT.available(); - uint8_t 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()) { - serverClients[i].write(sbuf, len); - delay(0); - } - } - } -#endif - //process data if any - COMMAND::read_buffer_serial(sbuf, len); - return true; - } else { - return false; - } -} -#ifdef TCP_IP_DATA_FEATURE -void BRIDGE::processFromTCP2Serial() -{ - uint8_t i,data; - //check if there are any new clients - if (data_server->hasClient()) { - for(i = 0; i < MAX_SRV_CLIENTS; i++) { - //find free/disconnected spot - if (!serverClients[i] || !serverClients[i].connected()) { - if(serverClients[i]) { - serverClients[i].stop(); - } - serverClients[i] = data_server->available(); - continue; - } - } - //no free/disconnected spot so reject - WiFiClient serverClient = data_server->available(); - serverClient.stop(); - } - //check clients for data - //to avoid any pollution if Uploading file to SDCard - if ((web_interface->blockserial) == false) { - for(i = 0; i < MAX_SRV_CLIENTS; i++) { - if (serverClients[i] && serverClients[i].connected()) { - if(serverClients[i].available()) { - //get data from the tcp client and push it to the UART - while(serverClients[i].available()) { - data = serverClients[i].read(); - ESP_SERIAL_OUT.write(data); - COMMAND::read_buffer_tcp(data); - } - } - } - } - } -} -#endif diff --git a/esp3d/bridge.h b/esp3d/bridge.h deleted file mode 100644 index 6bff0f8f..00000000 --- a/esp3d/bridge.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - bridge.h - esp3d bridge serial/tcp class - - 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 -*/ - -#ifndef BRIDGE_H -#define BRIDGE_H -#include -#include "config.h" -#ifdef TCP_IP_DATA_FEATURE -extern WiFiServer * data_server; -#endif - -class BRIDGE -{ -public: - static bool header_sent; - static String buffer_web; - static bool processFromSerial2TCP(); - static void print (const __FlashStringHelper *data, tpipe output); - static void print (String & data, tpipe output); - static void print (const char * data, tpipe output); - static void println (const __FlashStringHelper *data, tpipe output); - static void println (String & data, tpipe output); - static void println (const char * data, tpipe output); - static void flush (tpipe output); -#ifdef TCP_IP_DATA_FEATURE - static void processFromTCP2Serial(); - static void send2TCP(const __FlashStringHelper *data); - static void send2TCP(String data); - static void send2TCP(const char * data); -#endif -}; -#endif diff --git a/esp3d/command.cpp b/esp3d/command.cpp deleted file mode 100644 index 9b86fe72..00000000 --- a/esp3d/command.cpp +++ /dev/null @@ -1,1600 +0,0 @@ -/* - command.cpp - ESP3D configuration class - - 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 -*/ -#include "config.h" -#include "command.h" -#include "wificonf.h" -#include "webinterface.h" -#ifndef FS_NO_GLOBALS -#define FS_NO_GLOBALS -#endif -#include -#if defined(ARDUINO_ARCH_ESP32) -#include "SPIFFS.h" -#define MAX_GPIO 16 -#else -#define MAX_GPIO 37 -#endif -String COMMAND::buffer_serial; -String COMMAND::buffer_tcp; - -#define ERROR_CMD_MSG (output == WEB_PIPE)?F("Error: Wrong Command"):F("M117 Cmd Error") -#define INCORRECT_CMD_MSG (output == WEB_PIPE)?F("Error: Incorrect Command"):F("M117 Incorrect Cmd") -#define OK_CMD_MSG (output == WEB_PIPE)?F("ok"):F("M117 Cmd Ok") - -String COMMAND::get_param(String & cmd_params, const char * id, bool withspace) -{ - static String parameter; - String sid=id; - int start; - int end = -1; - parameter = ""; - //if no id it means it is first part of cmd - if (strlen(id) == 0) { - start = 0; - } - //else find id position - else { - start = cmd_params.indexOf(id); - } - //if no id found and not first part leave - if (start == -1 ) { - return parameter; - } - //password and SSID can have space so handle it - //if no space expected use space as delimiter - if (!withspace) { - end = cmd_params.indexOf(" ",start); - } -#ifdef AUTHENTICATION_FEATURE - //if space expected only one parameter but additional password may be present - else if (sid != " pwd=") { - end = cmd_params.indexOf(" pwd=",start); - } -#endif - //if no end found - take all - if (end == -1) { - end = cmd_params.length(); - } - //extract parameter - parameter = cmd_params.substring(start+strlen(id),end); - //be sure no extra space - parameter.trim(); - return parameter; -} -#ifdef AUTHENTICATION_FEATURE -//check admin password -bool COMMAND::isadmin(String & cmd_params) -{ - String adminpassword; - String sadminPassword; - if (!CONFIG::read_string(EP_ADMIN_PWD, sadminPassword, MAX_LOCAL_PASSWORD_LENGTH)) { - LOG("ERROR getting admin\r\n") - sadminPassword=FPSTR(DEFAULT_ADMIN_PWD); - } - adminpassword = get_param(cmd_params,"pwd=", true); - if (!sadminPassword.equals(adminpassword)) { - LOG("Not identified from command line\r\n") - return false; - } else { - return true; - } -} -//check user password - admin password is also valid -bool COMMAND::isuser(String & cmd_params) -{ - String userpassword; - String suserPassword; - if (!CONFIG::read_string(EP_USER_PWD, suserPassword, MAX_LOCAL_PASSWORD_LENGTH)) { - LOG("ERROR getting user\r\n") - suserPassword=FPSTR(DEFAULT_USER_PWD); - } - userpassword = get_param(cmd_params,"pwd=", true); - //it is not user password - if (!suserPassword.equals(userpassword)) { - //check admin password - return COMMAND::isadmin(cmd_params); - } else { - return true; - } -} -#endif -bool COMMAND::execute_command(int cmd,String cmd_params, tpipe output, level_authenticate_type auth_level) -{ - bool response = true; - level_authenticate_type auth_type = auth_level; -#ifdef AUTHENTICATION_FEATURE - if (isadmin(cmd_params)) { - auth_type = LEVEL_ADMIN; - LOG("admin identified\r\n"); - } - if (isuser(cmd_params) && (auth_type != LEVEL_ADMIN)) { - auth_type = LEVEL_USER; - LOG("user identified\r\n"); - } -#ifdef DEBUG_ESP3D - if ( auth_type == LEVEL_ADMIN) - { - LOG("admin identified\r\n"); - } - else { - if( auth_type == LEVEL_USER) - { - LOG("user identified\r\n"); - } - else - { - LOG("guest identified\r\n"); - } - } -#endif -#endif - //manage parameters - byte mode = 254; - String parameter; - LOG("Execute Command\r\n") - switch(cmd) { - //STA SSID - //[ESP100][pwd=] - case 100: - parameter = get_param(cmd_params,"", true); - if (!CONFIG::isSSIDValid(parameter.c_str())) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - } -#ifdef AUTHENTICATION_FEATURE - if (auth_type != LEVEL_ADMIN) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - } else -#endif - if(!CONFIG::write_string(EP_STA_SSID,parameter.c_str())) { - BRIDGE::println(ERROR_CMD_MSG, output); - response = false; - } else { - BRIDGE::println(OK_CMD_MSG, output); - } - break; - //STA Password - //[ESP101][pwd=] - case 101: - parameter = get_param(cmd_params,"", true); - if (!CONFIG::isPasswordValid(parameter.c_str())) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } -#ifdef AUTHENTICATION_FEATURE - if (auth_type != LEVEL_ADMIN) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else -#endif - if(!CONFIG::write_string(EP_STA_PASSWORD,parameter.c_str())) { - BRIDGE::println(ERROR_CMD_MSG, output); - response = false; - } else { - BRIDGE::println(OK_CMD_MSG, output); - } - break; - //Hostname - //[ESP102][pwd=] - case 102: - parameter = get_param(cmd_params,"", true); - if (!CONFIG::isHostnameValid(parameter.c_str())) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } -#ifdef AUTHENTICATION_FEATURE - if (auth_type != LEVEL_ADMIN) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else -#endif - if(!CONFIG::write_string(EP_HOSTNAME,parameter.c_str())) { - BRIDGE::println(ERROR_CMD_MSG, output); - response = false; - } else { - BRIDGE::println(OK_CMD_MSG, output); - } - break; - //Wifi mode (STA/AP) - //[ESP103][pwd=] - case 103: - parameter = get_param(cmd_params,"", true); - if (parameter == "STA") { - mode = CLIENT_MODE; - } else if (parameter == "AP") { - mode = AP_MODE; - } else { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } - if ((mode == CLIENT_MODE) || (mode == AP_MODE)) { -#ifdef AUTHENTICATION_FEATURE - if (auth_type != LEVEL_ADMIN) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else -#endif - if(!CONFIG::write_byte(EP_WIFI_MODE,mode)) { - BRIDGE::println(ERROR_CMD_MSG, output); - response = false; - } else { - BRIDGE::println(OK_CMD_MSG, output); - } - } - break; - //STA IP mode (DHCP/STATIC) - //[ESP104][pwd=] - case 104: - parameter = get_param(cmd_params,"", true); - if (parameter == "STATIC") { - mode = STATIC_IP_MODE; - } else if (parameter == "DHCP") { - mode = DHCP_MODE; - } else { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } - if ((mode == STATIC_IP_MODE) || (mode == DHCP_MODE)) { -#ifdef AUTHENTICATION_FEATURE - if (auth_type != LEVEL_ADMIN) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else -#endif - if(!CONFIG::write_byte(EP_STA_IP_MODE,mode)) { - BRIDGE::println(ERROR_CMD_MSG, output); - response = false; - } else { - BRIDGE::println(OK_CMD_MSG, output); - } - } - break; - //AP SSID - //[ESP105][pwd=] - case 105: - parameter = get_param(cmd_params,"", true); - if (!CONFIG::isSSIDValid(parameter.c_str())) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } -#ifdef AUTHENTICATION_FEATURE - if (auth_type != LEVEL_ADMIN) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else -#endif - if(!CONFIG::write_string(EP_AP_SSID,parameter.c_str())) { - BRIDGE::println(ERROR_CMD_MSG, output); - response = false; - } else { - BRIDGE::println(OK_CMD_MSG, output); - } - break; - //AP Password - //[ESP106][pwd=] - case 106: - parameter = get_param(cmd_params,"", true); - if (!CONFIG::isPasswordValid(parameter.c_str())) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } -#ifdef AUTHENTICATION_FEATURE - if (auth_type != LEVEL_ADMIN) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else -#endif - if(!CONFIG::write_string(EP_AP_PASSWORD,parameter.c_str())) { - BRIDGE::println(ERROR_CMD_MSG, output); - response = false; - } else { - BRIDGE::println(OK_CMD_MSG, output); - } - break; - //AP IP mode (DHCP/STATIC) - //[ESP107][pwd=] - case 107: - parameter = get_param(cmd_params,"", true); - if (parameter == "STATIC") { - mode = STATIC_IP_MODE; - } else if (parameter == "DHCP") { - mode = DHCP_MODE; - } else { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } - if ((mode == STATIC_IP_MODE) || (mode == DHCP_MODE)) { -#ifdef AUTHENTICATION_FEATURE - if (auth_type != LEVEL_ADMIN) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else -#endif - if(!CONFIG::write_byte(EP_AP_IP_MODE,mode)) { - BRIDGE::println(ERROR_CMD_MSG, output); - response = false; - } else { - BRIDGE::println(OK_CMD_MSG, output); - } - } - break; - // Set wifi on/off - //[ESP110][pwd=] - case 110: - parameter = get_param(cmd_params,"", true); - if (parameter == "on") { - mode = 1; - } else if (parameter == "off") { - mode = 0; - } else if (parameter == "restart") { - mode = 2; - } else { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } - if (response) { - #ifdef AUTHENTICATION_FEATURE - if (auth_type != LEVEL_ADMIN) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else - #endif - if (mode == 0) { - if (WiFi.getMode() !=WIFI_OFF) { - //disable wifi - ESP_SERIAL_OUT.println("M117 Disabling Wifi"); - WiFi.mode(WIFI_OFF); - wifi_config.Disable_servers(); - return response; - } else BRIDGE::println("M117 Wifi already off", output); - } - else if (mode == 1) { //restart device is the best way to start everything clean - if (WiFi.getMode() == WIFI_OFF) { - ESP_SERIAL_OUT.println("M117 Enabling Wifi"); - CONFIG::esp_restart(); - } else BRIDGE::println("M117 Wifi already on", output); - } else { //restart wifi and restart is the best way to start everything clean - ESP_SERIAL_OUT.println("M117 Enabling Wifi"); - CONFIG::esp_restart(); - } - } - break; - //Get current IP - //[ESP111]
- case 111: { - String currentIP ; - if (WiFi.getMode()==WIFI_STA) { - currentIP=WiFi.localIP().toString(); - } else { - currentIP=WiFi.softAPIP().toString(); - } - BRIDGE::print(cmd_params, output); - BRIDGE::println(currentIP, output); - LOG(cmd_params) - LOG(currentIP) - LOG("\r\n") - } - break; - //Get hostname - //[ESP112]
- case 112: { - String shost ; - if (!CONFIG::read_string(EP_HOSTNAME, shost, MAX_HOSTNAME_LENGTH)) { - shost=wifi_config.get_default_hostname(); - } - BRIDGE::print(cmd_params, output); - BRIDGE::println(shost, output); - LOG(cmd_params) - LOG(shost) - LOG("\r\n") - } - break; -#ifdef DIRECT_PIN_FEATURE - //Get/Set pin value - //[ESP201]P V [PULLUP=YES RAW=YES]pwd= - case 201: - parameter = get_param(cmd_params,"", true); -#ifdef AUTHENTICATION_FEATURE - if (auth_type == LEVEL_GUEST) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else -#endif - { - //check if have pin - parameter = get_param(cmd_params,"P", false); - LOG("Pin:") - LOG(parameter) - LOG("\r\n") - if (parameter == "") { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else { - int pin = parameter.toInt(); - //check pin is valid and not serial used pins - if ((pin >= 0) && (pin <= 16) && !((pin == 1) || (pin == 3))) { - //check if is set or get - parameter = get_param(cmd_params,"V", false); - //it is a get - if (parameter == "") { - //this is to not set pin mode - parameter = get_param(cmd_params,"RAW=", false); - if (parameter !="YES") { - parameter = get_param(cmd_params,"PULLUP=", false); - if (parameter == "YES") { - //GPIO16 is different than others - if (pin < MAX_GPIO) { - LOG("Set as input pull up\r\n") - pinMode(pin, INPUT_PULLUP); - } -#ifdef ARDUINO_ARCH_ESP8266 - else { - LOG("Set as input pull down 16\r\n") - pinMode(pin, INPUT_PULLDOWN_16); - } -#endif - } else { - LOG("Set as input\r\n") - pinMode(pin, INPUT); - } - delay(100); - } - int value = digitalRead(pin); - LOG("Read:"); - LOG(String(value).c_str()) - LOG("\n"); - BRIDGE::println(String(value).c_str(), output); - } else { - //it is a set - int value = parameter.toInt(); - //verify it is a 0 or a 1 - if ((value == 0) || (value == 1)) { - pinMode(pin, OUTPUT); - delay(10); - LOG("Set:") - LOG(String((value == 0)?LOW:HIGH)) - LOG("\r\n") - digitalWrite(pin, (value == 0)?LOW:HIGH); - BRIDGE::println(OK_CMD_MSG, output); - } else { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } - } - } else { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } - } - } - break; -#endif - - //Save data string - //[ESP300]pwd= - case 300: - parameter = get_param(cmd_params,"", true); -#ifdef AUTHENTICATION_FEATURE - if (auth_type == LEVEL_GUEST) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else -#endif - { - if(!CONFIG::write_string(EP_DATA_STRING,parameter.c_str())) { - BRIDGE::println(ERROR_CMD_MSG, output); - response = false; - } else { - BRIDGE::println(OK_CMD_MSG, output); - } - } - break; - //get data string - //[ESP301] pwd= - case 301: - parameter = get_param(cmd_params,"", true); -#ifdef AUTHENTICATION_FEATURE - if (auth_type == LEVEL_GUEST) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else -#endif - { - char sbuf[MAX_DATA_LENGTH+1]; - if (CONFIG::read_string(EP_DATA_STRING, sbuf, MAX_DATA_LENGTH)) { - BRIDGE::println(sbuf, output); - } else { - BRIDGE::println(F("Error reading data"), output); - } - } - break; - //Get full EEPROM settings content - //[ESP400] - case 400: { - char sbuf[MAX_DATA_LENGTH+1]; - uint8_t ipbuf[4]; - byte bbuf=0; - int ibuf=0; - parameter = get_param(cmd_params,"", true); - delay(0); - //Start JSON - BRIDGE::println(F("{\"EEPROM\":["), output); - if (cmd_params == "network" || cmd_params == "") { - - //1- Baud Rate - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_BAUD_RATE), output); - BRIDGE::print(F("\",\"T\":\"I\",\"V\":\""), output); - if (!CONFIG::read_buffer(EP_BAUD_RATE, (byte *)&ibuf, INTEGER_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(ibuf), output); - } - BRIDGE::print(F("\",\"H\":\"Baud Rate\",\"O\":[{\"9600\":\"9600\"},{\"19200\":\"19200\"},{\"38400\":\"38400\"},{\"57600\":\"57600\"},{\"115200\":\"115200\"},{\"230400\":\"230400\"},{\"250000\":\"250000\"}]}"), output); - BRIDGE::println(F(","), output); - - //2-Sleep Mode - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_SLEEP_MODE), output); - BRIDGE::print(F("\",\"T\":\"B\",\"V\":\""), output); - if (!CONFIG::read_byte(EP_SLEEP_MODE, &bbuf )) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(bbuf), output); - } - BRIDGE::print(F("\",\"H\":\"Sleep Mode\",\"O\":[{\"None\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(WIFI_NONE_SLEEP), output); -#ifdef ARDUINO_ARCH_ESP8266 - BRIDGE::print(F("\"},{\"Light\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(WIFI_LIGHT_SLEEP), output); -#endif - BRIDGE::print(F("\"},{\"Modem\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(WIFI_MODEM_SLEEP), output); - BRIDGE::print(F("\"}]}"), output); - BRIDGE::println(F(","), output); - - //3-Web Port - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_WEB_PORT), output); - BRIDGE::print(F("\",\"T\":\"I\",\"V\":\""), output); - if (!CONFIG::read_buffer(EP_WEB_PORT, (byte *)&ibuf, INTEGER_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(ibuf), output); - } - BRIDGE::print(F("\",\"H\":\"Web Port\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MAX_WEB_PORT), output); - BRIDGE::print(F("\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MIN_WEB_PORT), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); - - //4-Data Port - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_DATA_PORT), output); - BRIDGE::print(F("\",\"T\":\"I\",\"V\":\""), output); - if (!CONFIG::read_buffer(EP_DATA_PORT, (byte *)&ibuf, INTEGER_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(ibuf), output); - } - BRIDGE::print(F("\",\"H\":\"Data Port\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MAX_DATA_PORT), output); - BRIDGE::print(F("\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MIN_DATA_PORT), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); -#ifdef AUTHENTICATION_FEATURE - //5-Admin password - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_ADMIN_PWD), output); - BRIDGE::print(F("\",\"T\":\"S\",\"V\":\""), output); - if (!CONFIG::read_string(EP_ADMIN_PWD, sbuf, MAX_LOCAL_PASSWORD_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print("********", output); - } - BRIDGE::print(F("\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MAX_LOCAL_PASSWORD_LENGTH), output); - BRIDGE::print(F("\",\"H\":\"Admin Password\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MIN_LOCAL_PASSWORD_LENGTH), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); - - //6-User password - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_USER_PWD), output); - BRIDGE::print(F("\",\"T\":\"S\",\"V\":\""), output); - if (!CONFIG::read_string(EP_USER_PWD, sbuf, MAX_LOCAL_PASSWORD_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print("********", output); - } - BRIDGE::print(F("\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MAX_LOCAL_PASSWORD_LENGTH), output); - BRIDGE::print(F("\",\"H\":\"User Password\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MIN_LOCAL_PASSWORD_LENGTH), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); -#endif - //7-Hostname - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_HOSTNAME), output); - BRIDGE::print(F("\",\"T\":\"S\",\"V\":\""), output); - if (!CONFIG::read_string(EP_HOSTNAME, sbuf, MAX_HOSTNAME_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print(sbuf, output); - } - BRIDGE::print(F("\",\"H\":\"Hostname\" ,\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MAX_HOSTNAME_LENGTH), output); - BRIDGE::print(F("\", \"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MIN_HOSTNAME_LENGTH), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); - - //8-wifi mode - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_WIFI_MODE), output); - BRIDGE::print(F("\",\"T\":\"B\",\"V\":\""), output); - if (!CONFIG::read_byte(EP_WIFI_MODE, &bbuf )) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(bbuf), output); - } - BRIDGE::print(F("\",\"H\":\"Wifi mode\",\"O\":[{\"AP\":\"1\"},{\"STA\":\"2\"}]}"), output); - BRIDGE::println(F(","), output); - - //9-STA SSID - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_STA_SSID), output); - BRIDGE::print(F("\",\"T\":\"S\",\"V\":\""), output); - if (!CONFIG::read_string(EP_STA_SSID, sbuf, MAX_SSID_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print(sbuf, output); - } - BRIDGE::print(F("\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MAX_SSID_LENGTH), output); - BRIDGE::print(F("\",\"H\":\"Station SSID\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MIN_SSID_LENGTH), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); - - //10-STA password - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_STA_PASSWORD), output); - BRIDGE::print(F("\",\"T\":\"S\",\"V\":\""), output); - if (!CONFIG::read_string(EP_STA_PASSWORD, sbuf, MAX_PASSWORD_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print("********", output); - } - BRIDGE::print(F("\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MAX_PASSWORD_LENGTH), output); - BRIDGE::print(F("\",\"H\":\"Station Password\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MIN_PASSWORD_LENGTH), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); - - //11-Station Network Mode - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_STA_PHY_MODE), output); - BRIDGE::print(F("\",\"T\":\"B\",\"V\":\""), output); - if (!CONFIG::read_byte(EP_STA_PHY_MODE, &bbuf )) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(bbuf), output); - } - BRIDGE::print(F("\",\"H\":\"Station Network Mode\",\"O\":[{\"11b\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(WIFI_PHY_MODE_11B), output); - BRIDGE::print(F("\"},{\"11g\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(WIFI_PHY_MODE_11G), output); - BRIDGE::print(F("\"},{\"11n\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(WIFI_PHY_MODE_11N), output); - BRIDGE::print(F("\"}]}"), output); - BRIDGE::println(F(","), output); - - //12-STA IP mode - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_STA_IP_MODE), output); - BRIDGE::print(F("\",\"T\":\"B\",\"V\":\""), output); - if (!CONFIG::read_byte(EP_STA_IP_MODE, &bbuf )) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(bbuf), output); - } - BRIDGE::print(F("\",\"H\":\"Station IP Mode\",\"O\":[{\"DHCP\":\"1\"},{\"Static\":\"2\"}]}"), output); - BRIDGE::println(F(","), output); - - //13-STA static IP - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_STA_IP_VALUE), output); - BRIDGE::print(F("\",\"T\":\"A\",\"V\":\""), output); - if (!CONFIG::read_buffer(EP_STA_IP_VALUE,(byte *)ipbuf, IP_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print(IPAddress(ipbuf).toString().c_str(), output); - } - BRIDGE::print(F("\",\"H\":\"Station Static IP\"}"), output); - BRIDGE::println(F(","), output); - - //14-STA static Mask - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_STA_MASK_VALUE), output); - BRIDGE::print(F("\",\"T\":\"A\",\"V\":\""), output); - if (!CONFIG::read_buffer(EP_STA_MASK_VALUE,(byte *)ipbuf, IP_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print(IPAddress(ipbuf).toString().c_str(), output); - } - BRIDGE::print(F("\",\"H\":\"Station Static Mask\"}"), output); - BRIDGE::println(F(","), output); - - //15-STA static Gateway - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_STA_GATEWAY_VALUE), output); - BRIDGE::print(F("\",\"T\":\"A\",\"V\":\""), output); - if (!CONFIG::read_buffer(EP_STA_GATEWAY_VALUE,(byte *)ipbuf, IP_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print(IPAddress(ipbuf).toString().c_str(), output); - } - BRIDGE::print(F("\",\"H\":\"Station Static Gateway\"}"), output); - BRIDGE::println(F(","), output); - - //16-AP SSID - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_AP_SSID), output); - BRIDGE::print(F("\",\"T\":\"S\",\"V\":\""), output); - if (!CONFIG::read_string(EP_AP_SSID, sbuf, MAX_SSID_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print(sbuf, output); - } - BRIDGE::print(F("\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MAX_SSID_LENGTH), output); - BRIDGE::print(F("\",\"H\":\"AP SSID\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MIN_SSID_LENGTH), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); - - //17-AP password - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_AP_PASSWORD), output); - BRIDGE::print(F("\",\"T\":\"S\",\"V\":\""), output); - if (!CONFIG::read_string(EP_AP_PASSWORD, sbuf, MAX_PASSWORD_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print("********", output); - } - BRIDGE::print(F("\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MAX_PASSWORD_LENGTH), output); - BRIDGE::print(F("\",\"H\":\"AP Password\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MIN_PASSWORD_LENGTH), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); - - //18 - AP Network Mode - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_AP_PHY_MODE), output); - BRIDGE::print(F("\",\"T\":\"B\",\"V\":\""), output); - if (!CONFIG::read_byte(EP_AP_PHY_MODE, &bbuf )) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(bbuf), output); - } - BRIDGE::print(F("\",\"H\":\"AP Network Mode\",\"O\":[{\"11b\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(WIFI_PHY_MODE_11B), output); - BRIDGE::print(F("\"},{\"11g\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(WIFI_PHY_MODE_11G), output); - BRIDGE::print(F("\"}]}"), output); - BRIDGE::println(F(","), output); - - //19-AP SSID visibility - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_SSID_VISIBLE), output); - BRIDGE::print(F("\",\"T\":\"B\",\"V\":\""), output); - if (!CONFIG::read_byte(EP_SSID_VISIBLE, &bbuf )) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(bbuf), output); - } - BRIDGE::print(F("\",\"H\":\"SSID Visible\",\"O\":[{\"No\":\"0\"},{\"Yes\":\"1\"}]}"), output); - BRIDGE::println(F(","), output); - - //20-AP Channel - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_CHANNEL), output); - BRIDGE::print(F("\",\"T\":\"B\",\"V\":\""), output); - if (!CONFIG::read_byte(EP_CHANNEL, &bbuf )) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(bbuf), output); - } - BRIDGE::print(F("\",\"H\":\"AP Channel\",\"O\":["), output); - for (int i=1; i < 12 ; i++) { - BRIDGE::print(F("{\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(i), output); - BRIDGE::print(F("\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(i), output); - BRIDGE::print(F("\"}"), output); - if (i<11) { - BRIDGE::print(F(","), output); - } - } - BRIDGE::print(F("]}"), output); - BRIDGE::println(F(","), output); - - //21-AP Authentication - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_AUTH_TYPE), output); - BRIDGE::print(F("\",\"T\":\"B\",\"V\":\""), output); - if (!CONFIG::read_byte(EP_AUTH_TYPE, &bbuf )) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(bbuf), output); - } - BRIDGE::print(F("\",\"H\":\"Authentication\",\"O\":[{\"Open\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(AUTH_OPEN), output); - BRIDGE::print(F("\"},{\"WPA\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(AUTH_WPA_PSK), output); - BRIDGE::print(F("\"},{\"WPA2\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(AUTH_WPA2_PSK), output); - BRIDGE::print(F("\"},{\"WPA/WPA2\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(AUTH_WPA_WPA2_PSK), output); - BRIDGE::print(F("\"}]}"), output); - BRIDGE::println(F(","), output); - - //22-AP IP mode - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_AP_IP_MODE), output); - BRIDGE::print(F("\",\"T\":\"B\",\"V\":\""), output); - if (!CONFIG::read_byte(EP_AP_IP_MODE, &bbuf )) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(bbuf), output); - } - BRIDGE::print(F("\",\"H\":\"AP IP Mode\",\"O\":[{\"DHCP\":\"1\"},{\"Static\":\"2\"}]}"), output); - BRIDGE::println(F(","), output); - - //23-AP static IP - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_AP_IP_VALUE), output); - BRIDGE::print(F("\",\"T\":\"A\",\"V\":\""), output); - if (!CONFIG::read_buffer(EP_AP_IP_VALUE,(byte *)ipbuf, IP_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print(IPAddress(ipbuf).toString().c_str(), output); - } - BRIDGE::print(F("\",\"H\":\"AP Static IP\"}"), output); - BRIDGE::println(F(","), output); - - //24-AP static Mask - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_AP_MASK_VALUE), output); - BRIDGE::print(F("\",\"T\":\"A\",\"V\":\""), output); - if (!CONFIG::read_buffer(EP_AP_MASK_VALUE,(byte *)ipbuf, IP_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print(IPAddress(ipbuf).toString().c_str(), output); - } - BRIDGE::print(F("\",\"H\":\"AP Static Mask\"}"), output); - BRIDGE::println(F(","), output); - - //25-AP static Gateway - BRIDGE::print(F("{\"F\":\"network\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_AP_GATEWAY_VALUE), output); - BRIDGE::print(F("\",\"T\":\"A\",\"V\":\""), output); - if (!CONFIG::read_buffer(EP_AP_GATEWAY_VALUE,(byte *)ipbuf, IP_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print(IPAddress(ipbuf).toString().c_str(), output); - } - BRIDGE::print(F("\",\"H\":\"AP Static Gateway\"}"), output); - delay(0); - } - - if (cmd_params == "printer" || cmd_params == "") { - if (cmd_params == "") { - BRIDGE::println(F(","), output); - } - //Target FW - BRIDGE::print(F("{\"F\":\"printer\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_TARGET_FW), output); - BRIDGE::print(F("\",\"T\":\"B\",\"V\":\""), output); - if (!CONFIG::read_byte(EP_TARGET_FW, &bbuf )) { - BRIDGE::print("Unknown", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(bbuf), output); - } - BRIDGE::print(F("\",\"H\":\"Target FW\",\"O\":[{\"Repetier\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(REPETIER), output); - BRIDGE::print(F("\"},{\"Repetier for Davinci\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(REPETIER4DV), output); - BRIDGE::print(F("\"},{\"Marlin\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MARLIN), output); - BRIDGE::print(F("\"},{\"Marlin Kimbra\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MARLINKIMBRA), output); - BRIDGE::print(F("\"},{\"Smoothieware\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(SMOOTHIEWARE), output); - BRIDGE::print(F("\"},{\"Unknown\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(UNKNOWN_FW), output); - BRIDGE::print(F("\"}]}"), output); - BRIDGE::println(F(","), output); - - //Refresh time 1 - BRIDGE::print(F("{\"F\":\"printer\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_REFRESH_PAGE_TIME), output); - BRIDGE::print(F("\",\"T\":\"B\",\"V\":\""), output); - if (!CONFIG::read_byte(EP_REFRESH_PAGE_TIME, &bbuf )) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(bbuf), output); - } - BRIDGE::print(F("\",\"H\":\"Temperature Refresh Time\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MAX_REFRESH), output); - BRIDGE::print(F("\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MIN_REFRESH), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); - - //Refresh time 2 - BRIDGE::print(F("{\"F\":\"printer\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_REFRESH_PAGE_TIME2), output); - BRIDGE::print(F("\",\"T\":\"B\",\"V\":\""), output); - if (!CONFIG::read_byte(EP_REFRESH_PAGE_TIME2, &bbuf )) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(bbuf), output); - } - BRIDGE::print(F("\",\"H\":\"Position Refresh Time\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MAX_REFRESH), output); - BRIDGE::print(F("\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MIN_REFRESH), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); - - //XY feedrate - BRIDGE::print(F("{\"F\":\"printer\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_XY_FEEDRATE), output); - BRIDGE::print(F("\",\"T\":\"I\",\"V\":\""), output); - if (!CONFIG::read_buffer(EP_XY_FEEDRATE, (byte *)&ibuf, INTEGER_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(ibuf), output); - } - BRIDGE::print(F("\",\"H\":\"XY feedrate\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MAX_XY_FEEDRATE), output); - BRIDGE::print(F("\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MIN_XY_FEEDRATE), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); - - //Z feedrate - BRIDGE::print(F("{\"F\":\"printer\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_Z_FEEDRATE), output); - BRIDGE::print(F("\",\"T\":\"I\",\"V\":\""), output); - if (!CONFIG::read_buffer(EP_Z_FEEDRATE, (byte *)&ibuf, INTEGER_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(ibuf), output); - } - BRIDGE::print(F("\",\"H\":\"Z feedrate\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MAX_Z_FEEDRATE), output); - BRIDGE::print(F("\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MIN_Z_FEEDRATE), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); - - //E feedrate - BRIDGE::print(F("{\"F\":\"printer\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_E_FEEDRATE), output); - BRIDGE::print(F("\",\"T\":\"I\",\"V\":\""), output); - if (!CONFIG::read_buffer(EP_E_FEEDRATE, (byte *)&ibuf, INTEGER_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print((const char *)CONFIG::intTostr(ibuf), output); - } - BRIDGE::print(F("\",\"H\":\"E feedrate\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MAX_E_FEEDRATE), output); - BRIDGE::print(F("\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(DEFAULT_MIN_E_FEEDRATE), output); - BRIDGE::print(F("\"}"), output); - BRIDGE::println(F(","), output); - - //Camera address, data string - BRIDGE::print(F("{\"F\":\"printer\",\"P\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(EP_DATA_STRING), output); - BRIDGE::print(F("\",\"T\":\"S\",\"V\":\""), output); - if (!CONFIG::read_string(EP_DATA_STRING, sbuf, MAX_DATA_LENGTH)) { - BRIDGE::print("???", output); - } else { - BRIDGE::print(sbuf, output); - } - BRIDGE::print(F("\",\"S\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MAX_DATA_LENGTH), output); - BRIDGE::print(F("\",\"H\":\"Camera address\",\"M\":\""), output); - BRIDGE::print((const char *)CONFIG::intTostr(MIN_DATA_LENGTH), output); - BRIDGE::print(F("\"}"), output); - } - - //end JSON - BRIDGE::println(F("\n]}"), output); - delay(0); - } - break; - - //Set EEPROM setting - //[ESP401]P= T= V= pwd= - case 401: { - //check validity of parameters - String spos = get_param(cmd_params,"P=", false); - String styp = get_param(cmd_params,"T=", false); - String sval = get_param(cmd_params,"V=", true); - sval.trim(); - int pos = spos.toInt(); - if ((pos == 0 && spos != "0") || (pos > LAST_EEPROM_ADDRESS || pos < 0)) { - response = false; - } - if (!(styp == "B" || styp == "S" || styp == "A" || styp == "I")) { - response = false; - } - if (sval.length() == 0) { - response = false; - } - - -#ifdef AUTHENTICATION_FEATURE - if (response) { - //check authentication - level_authenticate_type auth_need = LEVEL_ADMIN; - for (int i = 0; i < AUTH_ENTRY_NB; i++) { - if (Setting[i][0] == pos ) { - auth_need = (level_authenticate_type)(Setting[i][1]); - i = AUTH_ENTRY_NB; - } - } - if ((auth_need == LEVEL_ADMIN && auth_type == LEVEL_USER) || (auth_type == LEVEL_GUEST)) { - response = false; - } - } -#endif - if (response) { - if (styp == "B") { - byte bbuf = sval.toInt(); - if(!CONFIG::write_byte(pos,bbuf)) { - response = false; - } else { - //dynamique refresh is better than restart the board - if (pos == EP_TARGET_FW)CONFIG::InitFirmwareTarget(); - if (pos == EP_IS_DIRECT_SD){ - CONFIG::InitDirectSD(); - if (CONFIG::is_direct_sd) CONFIG::InitPins(); - } - } - } - if (styp == "I") { - int ibuf = sval.toInt(); - if(!CONFIG::write_buffer(pos,(const byte *)&ibuf,INTEGER_LENGTH)) { - response = false; - } - } - if (styp == "S") { - if(!CONFIG::write_string(pos,sval.c_str())) { - response = false; - } - } - if (styp == "A") { - byte ipbuf[4]; - if (CONFIG::split_ip(sval.c_str(),ipbuf) < 4) { - response = false; - } else if(!CONFIG::write_buffer(pos,ipbuf,IP_LENGTH)) { - response = false; - } - } - } - if(!response) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - } else { - BRIDGE::println(OK_CMD_MSG, output); - } - - } - break; - - //Get available AP list (limited to 30) - //output is JSON or plain text according parameter - //[ESP410] - case 410: { - parameter = get_param(cmd_params,"", true); - int n = WiFi.scanNetworks(); - bool plain = parameter == "plain"; - if (!plain)BRIDGE::print(F("{\"AP_LIST\":["), output); - for (int i = 0; i < n; ++i) { - if (i>0) { - if (!plain) BRIDGE::print(F(","), output); - else BRIDGE::print(F("\n"), output); - } - if (!plain)BRIDGE::print(F("{\"SSID\":\""), output); - BRIDGE::print(WiFi.SSID(i).c_str(), output); - if (!plain)BRIDGE::print(F("\",\"SIGNAL\":\""), output); - else BRIDGE::print(F("\t"), output); - BRIDGE::print(CONFIG::intTostr(wifi_config.getSignal(WiFi.RSSI(i))), output);; - //BRIDGE::print(F("%"), output); - if (!plain)BRIDGE::print(F("\",\"IS_PROTECTED\":\""), output); - if (WiFi.encryptionType(i) == ENC_TYPE_NONE) { - if (!plain)BRIDGE::print(F("0"), output); - else BRIDGE::print(F("\tOpen"), output); - } else { - if (!plain)BRIDGE::print(F("1"), output); - else BRIDGE::print(F("\tSecure"), output); - } - if (!plain)BRIDGE::print(F("\"}"), output); - } - if (!plain)BRIDGE::print(F("]}"), output); - else BRIDGE::print(F("\n"), output); - WiFi.scanDelete(); - } - break; - //Get ESP current status in plain or JSON - //[ESP420] - case 420: { - parameter = get_param(cmd_params,"", true); - CONFIG::print_config(output, (parameter == "plain")); - } - break; - //Set ESP mode - //cmd is RESET, SAFEMODE, RESTART - //[ESP444]pwd= - case 444: - parameter = get_param(cmd_params,"", true); -#ifdef AUTHENTICATION_FEATURE - if (auth_type != LEVEL_ADMIN) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else -#endif - { - if (parameter=="RESET") { - CONFIG::reset_config(); - BRIDGE::println(F("Reset done - restart needed"), output); - } else if (parameter=="SAFEMODE") { - wifi_config.Safe_Setup(); - BRIDGE::println(F("Set Safe Mode - restart needed"), output); - } else if (parameter=="RESTART") { - BRIDGE::println(F("Restart started"), output); - BRIDGE::flush( output); - CONFIG::esp_restart(); - } else { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } - } - break; -#ifdef AUTHENTICATION_FEATURE - //Change / Reset user password - //[ESP555]pwd= - case 555: { - if (auth_type == LEVEL_ADMIN) { - parameter = get_param(cmd_params,"", true); - if (parameter.length() == 0) { - if(CONFIG::write_string(EP_USER_PWD,FPSTR(DEFAULT_USER_PWD))) { - BRIDGE::println(OK_CMD_MSG, output); - } else { - BRIDGE::println(ERROR_CMD_MSG, output); - response = false; - } - } else { - if (CONFIG::isLocalPasswordValid(parameter.c_str())) { - if(CONFIG::write_string(EP_USER_PWD,parameter.c_str())) { - BRIDGE::println(OK_CMD_MSG, output); - } else { - BRIDGE::println(ERROR_CMD_MSG, output); - response = false; - } - } else { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } - } - } else { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } - break; - } -#endif - //[ESP700] - case 700: { //read local file - //be sure serial is locked - if ((web_interface->blockserial)) { - break; - } - cmd_params.trim() ; - if ((cmd_params.length() > 0) && (cmd_params[0] != '/')) { - cmd_params = "/" + cmd_params; - } - FS_FILE currentfile = SPIFFS.open(cmd_params, SPIFFS_FILE_READ); - if (currentfile) {//if file open success - //flush to be sure send buffer is empty - ESP_SERIAL_OUT.flush(); - //until no line in file - while (currentfile.available()) { - String currentline = currentfile.readStringUntil('\n'); - currentline.replace("\n",""); - currentline.replace("\r",""); - if (currentline.length() > 0) { - int ESPpos = currentline.indexOf("[ESP"); - if (ESPpos>-1) { - //is there the second part? - int ESPpos2 = currentline.indexOf("]",ESPpos); - if (ESPpos2>-1) { - //Split in command and parameters - String cmd_part1=currentline.substring(ESPpos+4,ESPpos2); - String cmd_part2=""; - //is there space for parameters? - if (ESPpos2 - case 710: - parameter = get_param(cmd_params,"", true); -#ifdef AUTHENTICATION_FEATURE - if (auth_type != LEVEL_ADMIN) { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } else -#endif - { - if (parameter=="FORMAT") { - BRIDGE::print(F("Formating"), output); - //SPIFFS.end(); - delay(0); - SPIFFS.format(); - //SPIFFS.begin(); - BRIDGE::println(F("...Done"), output); - } else { - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } - } - break; - //SPIFFS total size and used size - //[ESP720]
- case 720: - BRIDGE::print(cmd_params, output); -#ifdef ARDUINO_ARCH_ESP8266 - fs::FSInfo info; - SPIFFS.info(info); - BRIDGE::print("SPIFFS Total:", output); - BRIDGE::print(CONFIG::formatBytes(info.totalBytes).c_str(), output); - BRIDGE::print(" Used:", output); - BRIDGE::println(CONFIG::formatBytes(info.usedBytes).c_str(), output); -#else - BRIDGE::print("SPIFFS Total:", output); - BRIDGE::print(CONFIG::formatBytes(SPIFFS.totalBytes()).c_str(), output); - BRIDGE::print(" Used:", output); - BRIDGE::println(CONFIG::formatBytes(SPIFFS.usedBytes()).c_str(), output); -#endif - break; - //get fw version firmare target and fw version - //[ESP800]
- case 800: - { - byte sd_dir = 0; - BRIDGE::print(cmd_params, output); - BRIDGE::print(F("FW version:"), output); - BRIDGE::print(FW_VERSION, output); - BRIDGE::print(F(" # FW target:"), output); - BRIDGE::print(CONFIG::GetFirmwareTargetShortName(), output); - BRIDGE::print(F(" # FW HW:"), output); - if (CONFIG::is_direct_sd) BRIDGE::print(F("Direct SD"), output); - else BRIDGE::print(F("Serial SD"), output); - BRIDGE::print(F(" # primary sd:"), output); - if (!CONFIG::read_byte(EP_PRIMARY_SD, &sd_dir )) sd_dir = DEFAULT_PRIMARY_SD; - if (sd_dir == SD_DIRECTORY) BRIDGE::print(F("/sd/"), output); - else if (sd_dir == EXT_DIRECTORY) BRIDGE::print(F("/ext/"), output); - else BRIDGE::print(F("none"), output); - BRIDGE::print(F(" # secondary sd:"), output); - if (!CONFIG::read_byte(EP_SECONDARY_SD, &sd_dir )) sd_dir = DEFAULT_SECONDARY_SD; - if (sd_dir == SD_DIRECTORY) BRIDGE::print(F("/sd/"), output); - else if (sd_dir == EXT_DIRECTORY) BRIDGE::print(F("/ext/"), output); - else BRIDGE::print(F("none"), output); - BRIDGE::print(F(" # authentication:"), output); -#ifdef AUTHENTICATION_FEATURE - BRIDGE::print(F("yes"), output); -#else - BRIDGE::print(F("no"), output); -#endif - BRIDGE::println("", output); - } - break; - //get fw target - //[ESP801]
- case 801: - BRIDGE::print(cmd_params, output); - BRIDGE::println(CONFIG::GetFirmwareTargetShortName(), output); - break; - //clear status/error/info list - case 802: - if (CONFIG::check_update_presence( )) BRIDGE::println("yes", output); - else BRIDGE::println("no", output); - break; - //[ESP999] - case 999: - cmd_params.trim(); -#ifdef ERROR_MSG_FEATURE - if (cmd_params=="ERROR") { - web_interface->error_msg.clear(); - BRIDGE::println(OK_CMD_MSG, output); - break; - } -#endif -#ifdef INFO_MSG_FEATURE - if (cmd_params=="INFO") { - web_interface->info_msg.clear(); - BRIDGE::println(OK_CMD_MSG, output); - break; - } -#endif -#ifdef STATUS_MSG_FEATURE - if (cmd_params=="STATUS") { - web_interface->status_msg.clear(); - BRIDGE::println(OK_CMD_MSG, output); - break; - } -#endif - if (cmd_params=="ALL") { -#ifdef ERROR_MSG_FEATURE - web_interface->error_msg.clear(); -#endif -#ifdef STATUS_MSG_FEATURE - web_interface->status_msg.clear(); -#endif -#ifdef INFO_MSG_FEATURE - web_interface->info_msg.clear(); -#endif - BRIDGE::println(OK_CMD_MSG, output); - break; - } - default: - BRIDGE::println(INCORRECT_CMD_MSG, output); - response = false; - } - return response; -} - -bool COMMAND::check_command(String buffer, tpipe output, bool handlelockserial) -{ - String buffer2; - LOG("Check Command:") - LOG(buffer) - LOG("\r\n") - bool is_temp = false; - if ((buffer.indexOf("T:") > -1 ) || (buffer.indexOf("B:") > -1 )) is_temp = true; - //feed the WD for safety - delay(0); - if (( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER)) { - //save time no need to continue - if ((buffer.indexOf("busy:") > -1) || (buffer.startsWith("wait"))) { - return false; - } - if (buffer.startsWith("ok")) { - return false; - } - } -#ifdef ERROR_MSG_FEATURE - int Errorpos = -1; -#endif -#ifdef INFO_MSG_FEATURE - int Infopos = -1; -#endif -#ifdef STATUS_MSG_FEATURE - int Statuspos = -1; -#endif -if (CONFIG::GetFirmwareTarget() == SMOOTHIEWARE) { -#ifdef ERROR_MSG_FEATURE - Errorpos= buffer.indexOf("error:"); -#endif -#ifdef INFO_MSG_FEATURE - Infopos= buffer.indexOf("info:"); -#endif -#ifdef STATUS_MSG_FEATURE - Statuspos= buffer.indexOf("warning:"); -#endif -} else { -#ifdef ERROR_MSG_FEATURE - Errorpos= buffer.indexOf("Error:"); -#endif -#ifdef INFO_MSG_FEATURE - Infopos= buffer.indexOf("Info:"); -#endif -#ifdef STATUS_MSG_FEATURE - Statuspos= -1; - if (CONFIG::GetFirmwareTarget() == MARLIN){ - Statuspos= buffer.indexOf("echo:"); - } - else { - Statuspos= buffer.indexOf("Status:"); - } - -#endif -} - -#ifdef SERIAL_COMMAND_FEATURE - String ESP_Command; - int ESPpos = buffer.indexOf("[ESP"); - if (ESPpos>-1) { - //is there the second part? - int ESPpos2 = buffer.indexOf("]",ESPpos); - if (ESPpos2>-1) { - //Split in command and parameters - String cmd_part1=buffer.substring(ESPpos+4,ESPpos2); - String cmd_part2=""; - //is there space for parameters? - if (ESPpos2-1 && !(buffer.indexOf("Format error")!=-1 || buffer.indexOf("wait")==Errorpos+6) ) { - String ss = buffer.substring(Errorpos+6); - ss.replace("\"",""); - ss.replace("'",""); - (web_interface->error_msg).add(ss.c_str()); - } -#endif -#ifdef INFO_MSG_FEATURE - //Info - if (Infopos>-1) { - String ss = buffer.substring(Errorpos+5); - ss.replace("\"",""); - ss.replace("'",""); - (web_interface->info_msg).add(ss.c_str()); - } -#endif -#ifdef STATUS_MSG_FEATURE - //Status - if (Statuspos>-1) { - String ss ; - if (CONFIG::GetFirmwareTarget() == SMOOTHIEWARE)ss = buffer.substring(Errorpos+8); - else if (CONFIG::GetFirmwareTarget() == MARLIN) ss = buffer.substring(Errorpos+5); - else ss = buffer.substring(Errorpos+7); - ss.replace("\"",""); - ss.replace("'",""); - (web_interface->info_msg).add(ss.c_str()); - } -#endif - - return is_temp; -} - -//read a buffer in an array -void COMMAND::read_buffer_serial(uint8_t *b, size_t len) -{ - for (long i = 0; i< len; i++) { - read_buffer_serial(b[i]); - //*b++; - } -} - -#ifdef TCP_IP_DATA_FEATURE -//read buffer as char -void COMMAND::read_buffer_tcp(uint8_t b) -{ - static bool previous_was_char=false; - static bool iscomment=false; -//to ensure it is continuous string, no char separated by binaries - if (!previous_was_char) { - buffer_tcp=""; - iscomment = false; - } -//is comment ? - if (char(b) == ';') { - iscomment = true; - } -//it is a char so add it to buffer - if (isPrintable(b)) { - previous_was_char=true; - //add char if not a comment - if (!iscomment) { - buffer_tcp+=char(b); - } - } else { - previous_was_char=false; //next call will reset the buffer - } -//this is not printable but end of command check if need to handle it - if (b==13 || b==10) { - //reset comment flag - iscomment = false; - //Minimum is something like M10 so 3 char - if (buffer_tcp.length()>3) { - check_command(buffer_tcp, TCP_PIPE); - } - } -} -#endif -//read buffer as char -void COMMAND::read_buffer_serial(uint8_t b) -{ - static bool previous_was_char=false; - static bool iscomment=false; -//to ensure it is continuous string, no char separated by binaries - if (!previous_was_char) { - buffer_serial=""; - iscomment = false; - } -//is comment ? - if (char(b) == ';') { - iscomment = true; - } -//it is a char so add it to buffer - if (isPrintable(b)) { - previous_was_char=true; - if (!iscomment) { - buffer_serial+=char(b); - } - } else { - previous_was_char=false; //next call will reset the buffer - } -//this is not printable but end of command check if need to handle it - if (b==13 || b==10) { - //reset comment flag - iscomment = false; - //Minimum is something like M10 so 3 char - if (buffer_serial.length()>3) { - check_command(buffer_serial, SERIAL_PIPE); - } - } -} diff --git a/esp3d/config.cpp b/esp3d/config.cpp deleted file mode 100644 index 9b55b265..00000000 --- a/esp3d/config.cpp +++ /dev/null @@ -1,1865 +0,0 @@ -/* - config.cpp- ESP3D configuration class - - 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 -*/ -#include "config.h" -#include -#ifndef FS_NO_GLOBALS -#define FS_NO_GLOBALS -#endif -#include -#include -#include "wificonf.h" -#ifdef ARDUINO_ARCH_ESP8266 -extern "C" { -#include "user_interface.h" -} -#else -#include "Update.h" -#include "esp_wifi.h" -#endif -#include "bridge.h" - - -uint8_t CONFIG::FirmwareTarget = UNKNOWN_FW; - -bool CONFIG::SetFirmwareTarget(uint8_t fw){ - if ( fw <= MAX_FW_ID) { - FirmwareTarget = fw; - return true; - } else return false; -} -uint8_t CONFIG::GetFirmwareTarget() { - return FirmwareTarget; -} -const char* CONFIG::GetFirmwareTargetName() { - static String response; - if ( CONFIG::FirmwareTarget == REPETIER4DV)response = F("Repetier for Davinci"); - else if ( CONFIG::FirmwareTarget == REPETIER)response = F("Repetier"); - else if ( CONFIG::FirmwareTarget == MARLIN) response = F("Marlin"); - else if ( CONFIG::FirmwareTarget == MARLINKIMBRA) response = F("MarlinKimbra"); - else if ( CONFIG::FirmwareTarget == SMOOTHIEWARE) response = F("Smoothieware"); - else response = F("???"); - return response.c_str(); -} - -const char* CONFIG::GetFirmwareTargetShortName() { - static String response; - if ( CONFIG::FirmwareTarget == REPETIER4DV)response = F("repetier4davinci"); - else if ( CONFIG::FirmwareTarget == REPETIER)response = F("repetier"); - else if ( CONFIG::FirmwareTarget == MARLIN) response = F("marlin"); - else if ( CONFIG::FirmwareTarget == MARLINKIMBRA) response = F("marlinkimbra"); - else if ( CONFIG::FirmwareTarget == SMOOTHIEWARE) response = F("smoothieware"); - else response = F("???"); - return response.c_str(); -} - -void CONFIG::InitFirmwareTarget(){ - uint8_t b = UNKNOWN_FW; - if (!CONFIG::read_byte(EP_TARGET_FW, &b )) { - b = UNKNOWN_FW; - } - if (!SetFirmwareTarget(b))SetFirmwareTarget(UNKNOWN_FW) ; -} - -void CONFIG::InitDirectSD(){ - CONFIG::is_direct_sd = false; - -} - -bool CONFIG::InitBaudrate(){ - long baud_rate=0; - if ( !CONFIG::read_buffer(EP_BAUD_RATE, (byte *)&baud_rate, INTEGER_LENGTH)) return false; - if ( ! (baud_rate==9600 || baud_rate==19200 ||baud_rate==38400 ||baud_rate==57600 ||baud_rate==115200 ||baud_rate==230400 ||baud_rate==250000) ) return false; - //setup serial - if (ESP_SERIAL_OUT.baudRate() != baud_rate)ESP_SERIAL_OUT.begin(baud_rate); -#ifdef ARDUINO_ARCH_ESP8266 - ESP_SERIAL_OUT.setRxBufferSize(SERIAL_RX_BUFFER_SIZE); -#endif - wifi_config.baud_rate=baud_rate; - delay(1000); - return true; -} - -bool CONFIG::InitExternalPorts(){ - if (!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)) return false; - if (wifi_config.iweb_port < DEFAULT_MIN_WEB_PORT ||wifi_config.iweb_port > DEFAULT_MAX_WEB_PORT || wifi_config.idata_port < DEFAULT_MIN_DATA_PORT || wifi_config.idata_port > DEFAULT_MAX_DATA_PORT) return false; - return true; -} - -#ifdef SDCARD_CONFIG_FEATURE -bool CONFIG::update_settings(){ - String filename = SDCARD_CONFIG_FILENAME; - filename.toLowerCase(); - //there is a config file - LOG("Check ") - LOG(filename) - LOG("\r\n") - bool answer = true; - if(SD.exists((const char *)filename.c_str())) { - bool success = true; - bool localerror = false; - const size_t bufferLen = 250; - char buffer[bufferLen]; - byte ip_sav[4]; - String stmp; - int itmp; - long baud_rate=0; - byte bflag; - LOG("Found config file\r\n") - String newfilename = filename; - IniFile espconfig((char *)filename.c_str()); - //validate file is correct - if (espconfig.open()) { - if (!espconfig.validate(buffer, bufferLen)) { - success = false; - LOG("Invalid config file\r\n") - } else { //file format is correct - let parse the settings - //Section [wifi] - //Hostname - //hostname = myesp - if (espconfig.getValue("network", "hostname", buffer, bufferLen)) { - if (!CONFIG::isHostnameValid(buffer)) { - success = false; - } else if(!CONFIG::write_string(EP_HOSTNAME,buffer)) { - success = false; - } - } - //baudrate = 115200 - if (espconfig.getValue("network", "baudrate", buffer, bufferLen, baud_rate)) { - if(!CONFIG::write_buffer(EP_BAUD_RATE,(const byte *)&baud_rate,INTEGER_LENGTH)) { - success = false; - } - } - //webport = 80 - if (espconfig.getValue("network", "webport", buffer, bufferLen, itmp)) { - if(!CONFIG::write_buffer(EP_WEB_PORT,(const byte *)&itmp,INTEGER_LENGTH)) { - success = false; - } - } - //dataport = 8888 - if (espconfig.getValue("network", "dataport", buffer, bufferLen, itmp)) { - if(!CONFIG::write_buffer(EP_DATA_PORT,(const byte *)&itmp,INTEGER_LENGTH)) { - success = false; - } - } - //adminpwd = admin - if (espconfig.getValue("network", "adminpwd", buffer, bufferLen)) { - if (!CONFIG::isLocalPasswordValid(buffer)) { - success = false; - } else if(!CONFIG::write_string(EP_ADMIN_PWD,buffer)) { - success = false; - } - } - //userpwd = user - if (espconfig.getValue("network", "userpwd", buffer, bufferLen)) { - if (!CONFIG::isLocalPasswordValid(buffer)) { - success = false; - } else if(!CONFIG::write_string(EP_USER_PWD,buffer)) { - success = false; - } - } - //sleep = none - if (espconfig.getValue("network", "sleep", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="none") { - bflag = WIFI_NONE_SLEEP; -#ifdef ARDUINO_ARCH_ESP8266 - } else if (stmp =="light") { - bflag = WIFI_LIGHT_SLEEP; -#endif - } else if (stmp =="modem") { - bflag = WIFI_MODEM_SLEEP; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_SLEEP_MODE,bflag)) { - success = false; - } - } - } - //wifimode = STA - if (espconfig.getValue("network", "wifimode", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="sta") { - bflag = CLIENT_MODE; - } else if (stmp =="ap") { - bflag = AP_MODE; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_WIFI_MODE,bflag)) { - success = false; - } - } - } - //STAssid = NETGEAR_2GEXT_OFFICE2 - if (espconfig.getValue("network", "STAssid", buffer, bufferLen)) { - if (!CONFIG::isSSIDValid(buffer)) { - success = false; - } else if(!CONFIG::write_string(EP_STA_SSID,buffer)) { - success = false; - } - } - //STApassword = mypassword - if (espconfig.getValue("network", "STApassword", buffer, bufferLen)) { - if (!CONFIG::isPasswordValid(buffer)) { - success = false; - } else if(!CONFIG::write_string(EP_STA_PASSWORD,buffer)) { - success = false; - } - } - //STAipmode = DHCP - if (espconfig.getValue("network", "STAipmode", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="dhcp") { - bflag = DHCP_MODE; - } else if (stmp =="static") { - bflag = STATIC_IP_MODE; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_STA_IP_MODE,bflag)) { - success = false; - } - } - } - //STAstatic_ip = 192.168.0.1 - if (espconfig.getValue("network", "STAstatic_ip", buffer, bufferLen)) { - if (!CONFIG::isIPValid(buffer)) { - success = false; - } else { - CONFIG::split_ip(buffer,ip_sav); - if(!CONFIG::write_buffer(EP_STA_IP_VALUE,ip_sav,IP_LENGTH)) { - success = false; - } - } - } - //STAstatic_mask = 255.255.255.0 - if (espconfig.getValue("network", "STAstatic_mask", buffer, bufferLen)) { - if (!CONFIG::isIPValid(buffer)) { - success = false; - } else { - CONFIG::split_ip(buffer,ip_sav); - if(!CONFIG::write_buffer(EP_STA_MASK_VALUE,ip_sav,IP_LENGTH)) { - success = false; - } - } - } - //STAstatic_gateway = 192.168.0.1 - if (espconfig.getValue("network", "STAstatic_gateway", buffer, bufferLen)) { - if (!CONFIG::isIPValid(buffer)) { - success = false; - } else { - CONFIG::split_ip(buffer,ip_sav); - if(!CONFIG::write_buffer(EP_STA_GATEWAY_VALUE,ip_sav,IP_LENGTH)) { - success = false; - } - } - } - //STANetwork_mode = 11g - if (espconfig.getValue("network", "STANetwork_mode", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="11b") { - bflag = WIFI_PHY_MODE_11B; - } else if (stmp =="11g") { - bflag = WIFI_PHY_MODE_11G; - } else if (stmp =="11n") { - bflag = WIFI_PHY_MODE_11N; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_STA_PHY_MODE,bflag)) { - success = false; - } - } - } - //APssid = NETGEAR_2GEXT_OFFICE2 - if (espconfig.getValue("network", "APssid", buffer, bufferLen)) { - if (!CONFIG::isSSIDValid(buffer)) { - success = false; - } else if(!CONFIG::write_string(EP_AP_SSID,buffer)) { - success = false; - } - } - //APpassword = 12345678 - if (espconfig.getValue("network", "APpassword", buffer, bufferLen)) { - if (!CONFIG::isPasswordValid(buffer)) { - success = false; - } else if(!CONFIG::write_string(EP_AP_PASSWORD,buffer)) { - success = false; - } - } - //APipmode = STATIC - if (espconfig.getValue("network", "APipmode", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="dhcp") { - bflag = DHCP_MODE; - } else if (stmp =="static") { - bflag = STATIC_IP_MODE; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_AP_IP_MODE,bflag)) { - success = false; - } - } - } - //APstatic_ip = 192.168.0.1 - if (espconfig.getValue("network", "APstatic_ip", buffer, bufferLen)) { - if (!CONFIG::isIPValid(buffer)) { - success = false; - } else { - CONFIG::split_ip(buffer,ip_sav); - if(!CONFIG::write_buffer(EP_AP_IP_VALUE,ip_sav,IP_LENGTH)) { - success = false; - } - } - } - //APstatic_mask = 255.255.255.0 - if (espconfig.getValue("network", "APstatic_mask", buffer, bufferLen)) { - if (!CONFIG::isIPValid(buffer)) { - success = false; - } else { - CONFIG::split_ip(buffer,ip_sav); - if(!CONFIG::write_buffer(EP_AP_MASK_VALUE,ip_sav,IP_LENGTH)) { - success = false; - } - } - } - //APstatic_gateway = 192.168.0.1 - if (espconfig.getValue("network", "APstatic_gateway", buffer, bufferLen)) { - if (!CONFIG::isIPValid(buffer)) { - success = false; - } else { - CONFIG::split_ip(buffer,ip_sav); - if(!CONFIG::write_buffer(EP_AP_GATEWAY_VALUE,ip_sav,IP_LENGTH)) { - success = false; - } - } - } - //AP_channel = 1 - if (espconfig.getValue("network", "AP_channel", buffer, bufferLen, itmp)) { - bflag = itmp; - if(!CONFIG::write_byte(EP_CHANNEL,bflag)) { - success = false; - } - } - //AP_visible = yes - if (espconfig.getValue("network", "AP_visible", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="yes") { - bflag = 1; - } else if (stmp =="no") { - bflag = 0; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_SSID_VISIBLE,bflag)) { - success = false; - } - } - } - //AP_auth = WPA - if (espconfig.getValue("network", "AP_auth", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="open") { - bflag = AUTH_OPEN; - } else if (stmp =="wpa") { - bflag = AUTH_WPA_PSK; - } else if (stmp =="wpa2") { - bflag = AUTH_WPA2_PSK; - } else if (stmp =="wpa_wpa2") { - bflag = AUTH_WPA_WPA2_PSK; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_AUTH_TYPE,bflag)) { - success = false; - } - } - } - //APNetwork_mode = 11g - if (espconfig.getValue("network", "APNetwork_mode", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="11b") { - bflag = WIFI_PHY_MODE_11B; - } else if (stmp =="11g") { - bflag = WIFI_PHY_MODE_11G; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_AP_PHY_MODE,bflag)) { - success = false; - } - } - } - - //Timezone - if (espconfig.getValue("network", "time_zone", buffer, bufferLen, itmp)) { - bflag = itmp; - if(!CONFIG::write_byte(EP_TIMEZONE,bflag)) { - success = false; - } - } - - //day saving time - if (espconfig.getValue("network", "day_st", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="yes") { - bflag = 1; - } else if (stmp =="no") { - bflag = 0; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_TIME_ISDST,bflag)) { - success = false; - } - } - } - //time server 1 - if (espconfig.getValue("network", "time_server_1", buffer, bufferLen)) { - if (strlen(buffer) > 128) { - success = false; - } else if(!CONFIG::write_string(EP_TIME_SERVER1,buffer)) { - success = false; - } - } - - //time server 2 - if (espconfig.getValue("network", "time_server_2", buffer, bufferLen)) { - if (strlen(buffer) > 128) { - success = false; - } else if(!CONFIG::write_string(EP_TIME_SERVER2,buffer)) { - success = false; - } - } - - //time server 3 - if (espconfig.getValue("network", "time_server_3", buffer, bufferLen)) { - if (strlen(buffer) > 128) { - success = false; - } else if(!CONFIG::write_string(EP_TIME_SERVER3,buffer)) { - success = false; - } - } - - //Section [printer] - //data = 192.168.1.1/video.cgi?user=luc&pwd=mypassword - if (espconfig.getValue("printer", "data", buffer, bufferLen)) { - if (strlen(buffer) > 128) { - success = false; - } else if(!CONFIG::write_string(EP_DATA_STRING,buffer)) { - success = false; - } - } - //refreshrate = 3 - if (espconfig.getValue("printer", "refreshrate", buffer, bufferLen, itmp)) { - bflag = itmp; - if(!CONFIG::write_byte(EP_REFRESH_PAGE_TIME,bflag)) { - success = false; - } - } - //refreshrate2 = 3 - if (espconfig.getValue("printer", "refreshrate2", buffer, bufferLen, itmp)) { - bflag = itmp; - if(!CONFIG::write_byte(EP_REFRESH_PAGE_TIME2,bflag)) { - success = false; - } - } - //XY_feedrate = 1000 - if (espconfig.getValue("printer", "XY_feedrate", buffer, bufferLen, itmp)) { - if(!CONFIG::write_buffer(EP_XY_FEEDRATE,(const byte *)&itmp,INTEGER_LENGTH)) { - success = false; - } - } - //Z_feedrate = 100 - if (espconfig.getValue("printer", "Z_feedrate", buffer, bufferLen, itmp)) { - if(!CONFIG::write_buffer(EP_Z_FEEDRATE,(const byte *)&itmp,INTEGER_LENGTH)) { - success = false; - } - } - //E_feedrate = 400 - if (espconfig.getValue("printer", "E_feedrate", buffer, bufferLen, itmp)) { - if(!CONFIG::write_buffer(EP_E_FEEDRATE,(const byte *)&itmp,INTEGER_LENGTH)) { - success = false; - } - } - - //target_printer= smoothieware - if (espconfig.getValue("printer", "target_printer", buffer, bufferLen)) { - stmp = buffer; - bflag = UNKNOWN_FW; - if ( stmp.equalsIgnoreCase(String(F("repetier"))))bflag = REPETIER; - else if ( stmp.equalsIgnoreCase(String(F("repetier4davinci"))))bflag = REPETIER4DV; - else if ( stmp.equalsIgnoreCase(String(F("marlin"))))bflag = MARLIN; - else if ( stmp.equalsIgnoreCase(String(F("marlinkimbra"))))bflag = MARLINKIMBRA; - else if ( stmp.equalsIgnoreCase(String(F("smoothieware"))))bflag = SMOOTHIEWARE; - else success = false; - if(!(success && CONFIG::write_byte(EP_TARGET_FW,bflag))) { - success = false; - } - } - - //direct sd connection - if (espconfig.getValue("printer", "direct_sd", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="yes") { - bflag = 1; - } else if (stmp =="no") { - bflag = 0; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_IS_DIRECT_SD,bflag)) { - success = false; - } - } - } - //sd check update at boot - if (espconfig.getValue("printer", "sd_check_update", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="yes") { - bflag = 1; - } else if (stmp =="no") { - bflag = 0; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_SD_CHECK_UPDATE_AT_BOOT,bflag)) { - success = false; - } - } - } - //direct sd boot check - if (espconfig.getValue("printer", "direct_sd_boot_check", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="yes") { - bflag = 1; - } else if (stmp =="no") { - bflag = 0; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_DIRECT_SD_CHECK,bflag)) { - success = false; - } - } - } - - //primary sd directory - if (espconfig.getValue("printer", "primary_sd ", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="none") { - bflag = 0; - } else if (stmp =="sd") { - bflag = 1; - } else if (stmp =="ext") { - bflag = 2; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_PRIMARY_SD,bflag)) { - success = false; - } - } - } - - //secondary sd directory - if (espconfig.getValue("printer", "secondary_sd ", buffer, bufferLen)) { - stmp = buffer; - localerror = false; - stmp.toLowerCase(); - bflag = 0; - if (stmp =="none") { - bflag = 0; - } else if (stmp =="sd") { - bflag = 1; - } else if (stmp =="ext") { - bflag = 2; - } else { - localerror = true; - success = false; - } - if (!localerror) { - if(!CONFIG::write_byte(EP_SECONDARY_SD,bflag)) { - success = false; - } - } - } - - } - espconfig.close(); - if(success) { - newfilename.replace(String(".txt"), String(".ok")); - } else { - newfilename.replace(String(".txt"), String(".bad")); - answer = false; - } - //rename if name is already used - if (!CONFIG::renameFile (newfilename)) { - LOG("Failed to rename previous file\r\n") - } - if (SD.rename((const char *)filename.c_str(),(const char *)newfilename.c_str())) { - LOG("File renamed, restarted file\r\n") - } else { - return false; - LOG("Failed to rename file\r\n") - } - } else { - return false; - LOG("Failed to open config file\r\n") - } - } else { - return false; - LOG("No config file\r\n") - } - return answer; -} -#endif -void CONFIG::esp_restart() -{ - LOG("Restarting\r\n") - ESP_SERIAL_OUT.flush(); - delay(500); -#ifdef ARDUINO_ARCH_ESP8266 - ESP_SERIAL_OUT.swap(); -#endif - delay(100); - ESP.restart(); - while (1) { - delay(1); - }; -} - -void CONFIG::InitPins(){ -#ifdef RECOVERY_FEATURE - pinMode(RESET_CONFIG_PIN, INPUT); -#endif -} - -bool CONFIG::is_direct_sd = false; - -bool CONFIG::isHostnameValid(const char * hostname) -{ - //limited size - char c; - if (strlen(hostname)>MAX_HOSTNAME_LENGTH || strlen(hostname) < MIN_HOSTNAME_LENGTH) { - return false; - } - //only letter and digit - for (int i=0; i < strlen(hostname); i++) { - c = hostname[i]; - if (!(isdigit(c) || isalpha(c) || c=='_')) { - return false; - } - if (c==' ') { - return false; - } - } - return true; -} - -bool CONFIG::isSSIDValid(const char * ssid) -{ - //limited size - //char c; - if (strlen(ssid)>MAX_SSID_LENGTH || strlen(ssid)MAX_PASSWORD_LENGTH) { - return false; - } - #if MIN_PASSWORD_LENGTH > 0 - if (strlen(password)MAX_LOCAL_PASSWORD_LENGTH)|| (strlen(password)15 || strlen(IP)==0) { - return false; - } - //cannot start with . - if (IP[0]=='.') { - return false; - } - //only letter and digit - for (int i=0; i < strlen(IP); i++) { - c = IP[i]; - if (isdigit(c)) { - //only 3 digit at once - internalcount++; - previouswasdot=false; - if (internalcount>3) { - return false; - } - } else if(c=='.') { - //cannot have 2 dots side by side - if (previouswasdot) { - return false; - } - previouswasdot=true; - internalcount=0; - dotcount++; - }//if not a dot neither a digit it is wrong - else { - return false; - } - } - //if not 3 dots then it is wrong - if (dotcount!=3) { - return false; - } - //cannot have the last dot as last char - if (IP[strlen(IP)-1]=='.') { - return false; - } - return true; -} - -char * CONFIG::intTostr(int value) -{ - static char result [12]; - sprintf(result,"%d",value); - return result; -} - -String CONFIG::formatBytes(uint32_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"; - } -} - -//helper to convert string to IP -//do not use IPAddress.fromString() because lack of check point and error result -//return number of parts -byte CONFIG::split_ip (const char * ptr,byte * part) -{ - if (strlen(ptr)>15 || strlen(ptr)< 7) { - part[0]=0; - part[1]=0; - part[2]=0; - part[3]=0; - return 0; - } - - char pstart [16]; - char * ptr2; - strcpy(pstart,ptr); - ptr2 = pstart; - byte i = strlen(pstart); - byte pos = 0; - for (byte j=0; jsprintf(macstr,"%02X:%02X:%02X:%02X:%02X:%02X",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5])) { - strcpy (macstr, "00:00:00:00:00:00"); - } - return macstr; -} - - -//read a string -//a string is multibyte + \0, this is won't work if 1 char is multibyte like chinese char -bool CONFIG::read_string(int pos, char byte_buffer[], int size_max) -{ - //check if parameters are acceptable - if (size_max==0 || pos+size_max+1 > EEPROM_SIZE || byte_buffer== NULL) { - LOG("Error read string\r\n") - return false; - } - EEPROM.begin(EEPROM_SIZE); - byte b = 13; // non zero for the while loop below - int i=0; - - //read until max size is reached or \0 is found - while (i < size_max && b != 0) { - b = EEPROM.read(pos+i); - byte_buffer[i]=b; - i++; - } - - // Be sure there is a 0 at the end. - if (b!=0) { - byte_buffer[i-1]=0x00; - } - EEPROM.end(); - - return true; -} - -bool CONFIG::read_string(int pos, String & sbuffer, int size_max) -{ - //check if parameters are acceptable - if (size_max==0 || pos+size_max+1 > EEPROM_SIZE ) { - LOG("Error read string\r\n") - return false; - } - byte b = 13; // non zero for the while loop below - int i=0; - sbuffer=""; - - EEPROM.begin(EEPROM_SIZE); - //read until max size is reached or \0 is found - while (i < size_max && b != 0) { - b = EEPROM.read(pos+i); - if (b!=0) { - sbuffer+=char(b); - } - i++; - } - EEPROM.end(); - - return true; -} - -//read a buffer of size_buffer -bool CONFIG::read_buffer(int pos, byte byte_buffer[], int size_buffer) -{ - //check if parameters are acceptable - if (size_buffer==0 || pos+size_buffer > EEPROM_SIZE || byte_buffer== NULL) { - LOG("Error read buffer\r\n") - return false; - } - int i=0; - EEPROM.begin(EEPROM_SIZE); - //read until max size is reached - while (i EEPROM_SIZE) { - LOG("Error read byte\r\n") - return false; - } - EEPROM.begin(EEPROM_SIZE); - value[0] = EEPROM.read(pos); - EEPROM.end(); - return true; -} - -bool CONFIG::write_string(int pos, const __FlashStringHelper *str) -{ - String stmp = str; - return write_string(pos,stmp.c_str()); -} - -bool CONFIG::check_update_presence( ){ - bool result = false; - if (CONFIG::is_direct_sd) { - long baud_rate=0; - if (!CONFIG::read_buffer(EP_BAUD_RATE, (byte *)&baud_rate, INTEGER_LENGTH)) return false; - if (ESP_SERIAL_OUT.baudRate() != baud_rate)ESP_SERIAL_OUT.begin(baud_rate); - CONFIG::InitFirmwareTarget(); - delay(500); - String cmd = "M20"; - //By default M20 should be applied - //if (CONFIG::FirmwareTarget == UNKNOWN_FW) return false; - if (CONFIG::FirmwareTarget == SMOOTHIEWARE) { - byte sd_dir = 0; - if (!CONFIG::read_byte(EP_PRIMARY_SD, &sd_dir )) sd_dir = DEFAULT_PRIMARY_SD; - if (sd_dir == SD_DIRECTORY) cmd = "ls /sd"; - else if (sd_dir == EXT_DIRECTORY) cmd = "ls /ext"; - else return false; - } - String tmp; - int count ; - //send command to serial as no need to transfer ESP command - //to avoid any pollution if Uploading file to SDCard - //block every query - //empty the serial buffer and incoming data - if(ESP_SERIAL_OUT.available()) { - BRIDGE::processFromSerial2TCP(); - delay(1); - } - //Send command - ESP_SERIAL_OUT.println(cmd); - count = 0; - String current_buffer; - String current_line; - //int pos; - int temp_counter = 0; - - //pickup the list - while (count < MAX_TRY) { - //give some time between each buffer - if (ESP_SERIAL_OUT.available()) { - count = 0; - size_t len = ESP_SERIAL_OUT.available(); - uint8_t sbuf[len+1]; - //read buffer - ESP_SERIAL_OUT.readBytes(sbuf, len); - //change buffer as string - sbuf[len]='\0'; - //add buffer to current one if any - current_buffer += (char * ) sbuf; - while (current_buffer.indexOf("\n") !=-1) { - //remove the possible "\r" - current_buffer.replace("\r",""); - //pos = current_buffer.indexOf("\n"); - //get line - current_line = current_buffer.substring(0,current_buffer.indexOf("\n")); - //if line is command ack - just exit so save the time out period - if ((current_line == "ok") || (current_line == "wait")) { - count = MAX_TRY; - break; - } - //check line - //save time no need to continue - if (current_line.indexOf("busy:") > -1 || current_line.indexOf("T:") > -1 || current_line.indexOf("B:") > -1) { - temp_counter++; - } else { - } - if (temp_counter > 5) { - break; - } - //current remove line from buffer - tmp = current_buffer.substring(current_buffer.indexOf("\n")+1,current_buffer.length()); - current_buffer = tmp; - delay(0); - } - delay (0); - } else { - delay(1); - } - //it is sending too many temp status should be heating so let's exit the loop - if (temp_counter > 5) { - count = MAX_TRY; - } - count++; - } - if(ESP_SERIAL_OUT.available()) { - BRIDGE::processFromSerial2TCP(); - delay(1); - } - } - return result; -} - - -//write a string (array of byte with a 0x00 at the end) -bool CONFIG::write_string(int pos, const char * byte_buffer) -{ - int size_buffer; - int maxsize = EEPROM_SIZE; - size_buffer= strlen(byte_buffer); - //check if parameters are acceptable - switch (pos) { - case EP_ADMIN_PWD: - case EP_USER_PWD: - maxsize = MAX_LOCAL_PASSWORD_LENGTH; - break; - case EP_AP_SSID: - case EP_STA_SSID: - maxsize = MAX_SSID_LENGTH; - break; - case EP_AP_PASSWORD: - case EP_STA_PASSWORD: - maxsize = MAX_PASSWORD_LENGTH; - break; - case EP_HOSTNAME: - maxsize = MAX_HOSTNAME_LENGTH; - break; - case EP_TIME_SERVER1: - case EP_TIME_SERVER2: - case EP_TIME_SERVER3: - case EP_DATA_STRING: - maxsize = MAX_DATA_LENGTH; - break; - default: - maxsize = EEPROM_SIZE; - break; - } - if ((size_buffer==0 && !(pos == EP_DATA_STRING)) || pos+size_buffer+1 > EEPROM_SIZE || size_buffer > maxsize || byte_buffer== NULL) { - LOG("Error write string\r\n") - return false; - } - //copy the value(s) - EEPROM.begin(EEPROM_SIZE); - for (int i = 0; i < size_buffer; i++) { - EEPROM.write(pos + i, byte_buffer[i]); - } - - //0 terminal - EEPROM.write(pos + size_buffer, 0x00); - EEPROM.commit(); - EEPROM.end(); - return true; -} - -//write a buffer -bool CONFIG::write_buffer(int pos, const byte * byte_buffer, int size_buffer) -{ - //check if parameters are acceptable - if (size_buffer==0 || pos+size_buffer > EEPROM_SIZE || byte_buffer== NULL) { - LOG("Error write buffer\r\n") - return false; - } - EEPROM.begin(EEPROM_SIZE); - //copy the value(s) - for (int i = 0; i < size_buffer; i++) { - EEPROM.write(pos + i, byte_buffer[i]); - } - EEPROM.commit(); - EEPROM.end(); - return true; -} - -//read a flag / byte -bool CONFIG::write_byte(int pos, const byte value) -{ - //check if parameters are acceptable - if (pos+1 > EEPROM_SIZE) { - LOG("Error write byte\r\n") - return false; - } - EEPROM.begin(EEPROM_SIZE); - EEPROM.write(pos, value); - EEPROM.commit(); - EEPROM.end(); - return true; -} - -bool CONFIG::reset_config() -{ - if(!CONFIG::write_string(EP_DATA_STRING,"")) { - return false; - } - if(!CONFIG::write_byte(EP_WIFI_MODE,DEFAULT_WIFI_MODE)) { - return false; - } - if(!CONFIG::write_buffer(EP_BAUD_RATE,(const byte *)&DEFAULT_BAUD_RATE,INTEGER_LENGTH)) { - return false; - } - if(!CONFIG::write_string(EP_AP_SSID,FPSTR(DEFAULT_AP_SSID))) { - return false; - } - if(!CONFIG::write_string(EP_AP_PASSWORD,FPSTR(DEFAULT_AP_PASSWORD))) { - return false; - } - if(!CONFIG::write_string(EP_STA_SSID,FPSTR(DEFAULT_STA_SSID))) { - return false; - } - if(!CONFIG::write_string(EP_STA_PASSWORD,FPSTR(DEFAULT_STA_PASSWORD))) { - return false; - } - if(!CONFIG::write_byte(EP_AP_IP_MODE,DEFAULT_AP_IP_MODE)) { - return false; - } - if(!CONFIG::write_byte(EP_STA_IP_MODE,DEFAULT_STA_IP_MODE)) { - return false; - } - if(!CONFIG::write_buffer(EP_STA_IP_VALUE,DEFAULT_IP_VALUE,IP_LENGTH)) { - return false; - } - if(!CONFIG::write_buffer(EP_STA_MASK_VALUE,DEFAULT_MASK_VALUE,IP_LENGTH)) { - return false; - } - if(!CONFIG::write_buffer(EP_STA_GATEWAY_VALUE,DEFAULT_GATEWAY_VALUE,IP_LENGTH)) { - return false; - } - if(!CONFIG::write_byte(EP_STA_PHY_MODE,DEFAULT_PHY_MODE)) { - return false; - } - if(!CONFIG::write_buffer(EP_AP_IP_VALUE,DEFAULT_IP_VALUE,IP_LENGTH)) { - return false; - } - if(!CONFIG::write_buffer(EP_AP_MASK_VALUE,DEFAULT_MASK_VALUE,IP_LENGTH)) { - return false; - } - if(!CONFIG::write_buffer(EP_AP_GATEWAY_VALUE,DEFAULT_GATEWAY_VALUE,IP_LENGTH)) { - return false; - } - if(!CONFIG::write_byte(EP_AP_PHY_MODE,DEFAULT_PHY_MODE)) { - return false; - } - if(!CONFIG::write_byte(EP_SLEEP_MODE,DEFAULT_SLEEP_MODE)) { - return false; - } - if(!CONFIG::write_byte(EP_CHANNEL,DEFAULT_CHANNEL)) { - return false; - } - if(!CONFIG::write_byte(EP_AUTH_TYPE,DEFAULT_AUTH_TYPE)) { - return false; - } - if(!CONFIG::write_byte(EP_SSID_VISIBLE,DEFAULT_SSID_VISIBLE)) { - return false; - } - if(!CONFIG::write_buffer(EP_WEB_PORT,(const byte *)&DEFAULT_WEB_PORT,INTEGER_LENGTH)) { - return false; - } - if(!CONFIG::write_buffer(EP_DATA_PORT,(const byte *)&DEFAULT_DATA_PORT,INTEGER_LENGTH)) { - return false; - } - if(!CONFIG::write_byte(EP_REFRESH_PAGE_TIME,DEFAULT_REFRESH_PAGE_TIME)) { - return false; - } - if(!CONFIG::write_byte(EP_REFRESH_PAGE_TIME2,DEFAULT_REFRESH_PAGE_TIME)) { - return false; - } - if(!CONFIG::write_string(EP_HOSTNAME,wifi_config.get_default_hostname())) { - return false; - } - if(!CONFIG::write_buffer(EP_XY_FEEDRATE,(const byte *)&DEFAULT_XY_FEEDRATE,INTEGER_LENGTH)) { - return false; - } - if(!CONFIG::write_buffer(EP_Z_FEEDRATE,(const byte *)&DEFAULT_Z_FEEDRATE,INTEGER_LENGTH)) { - return false; - } - if(!CONFIG::write_buffer(EP_E_FEEDRATE,(const byte *)&DEFAULT_E_FEEDRATE,INTEGER_LENGTH)) { - return false; - } - if(!CONFIG::write_string(EP_ADMIN_PWD,FPSTR(DEFAULT_ADMIN_PWD))) { - return false; - } - if(!CONFIG::write_string(EP_USER_PWD,FPSTR(DEFAULT_USER_PWD))) { - return false; - } - - if(!CONFIG::write_byte(EP_TARGET_FW, UNKNOWN_FW)) { - return false; - } - - if(!CONFIG::write_byte(EP_TIMEZONE, DEFAULT_TIME_ZONE)) { - return false; - } - - if(!CONFIG::write_byte(EP_TIME_ISDST, DEFAULT_TIME_DST)) { - return false; - } - - if(!CONFIG::write_byte(EP_PRIMARY_SD, DEFAULT_PRIMARY_SD)) { - return false; - } - - if(!CONFIG::write_byte(EP_SECONDARY_SD, DEFAULT_SECONDARY_SD)) { - return false; - } - - if(!CONFIG::write_byte(EP_IS_DIRECT_SD, DEFAULT_IS_DIRECT_SD)) { - return false; - } - - if(!CONFIG::write_byte(EP_DIRECT_SD_CHECK, DEFAULT_DIRECT_SD_CHECK)) { - return false; - } - - if(!CONFIG::write_byte(EP_SD_CHECK_UPDATE_AT_BOOT, DEFAULT_SD_CHECK_UPDATE_AT_BOOT)) { - return false; - } - - if(!CONFIG::write_string(EP_TIME_SERVER1,FPSTR(DEFAULT_TIME_SERVER1))) { - return false; - } - - if(!CONFIG::write_string(EP_TIME_SERVER2,FPSTR(DEFAULT_TIME_SERVER2))) { - return false; - } - - if(!CONFIG::write_string(EP_TIME_SERVER3,FPSTR(DEFAULT_TIME_SERVER3))) { - return false; - } - return true; -} - -void CONFIG::print_config(tpipe output, bool plaintext) -{ - if (!plaintext)BRIDGE::print(F("{\"chip_id\":\""), output); - else BRIDGE::print(F("Chip ID: "), output); -#ifdef ARDUINO_ARCH_ESP8266 - BRIDGE::print(String(ESP.getChipId()).c_str(), output); -#else - BRIDGE::print(String((uint16_t)(ESP.getEfuseMac()>>32)).c_str(), output); -#endif - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"cpu\":\""), output); - else BRIDGE::print(F("CPU Frequency: "), output); - BRIDGE::print(String(ESP.getCpuFreqMHz()).c_str(), output); - if (plaintext)BRIDGE::print(F("Mhz"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); -#ifdef ARDUINO_ARCH_ESP32 - if (!plaintext)BRIDGE::print(F("\"cpu_temp\":\""), output); - else BRIDGE::print(F("CPU Temperature: "), output); - BRIDGE::print(String(temperatureRead(),1).c_str(), output); - if (plaintext)BRIDGE::print(F("C"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); -#endif - if (!plaintext)BRIDGE::print(F("\"freemem\":\""), output); - else BRIDGE::print(F("Free memory: "), output); - BRIDGE::print(formatBytes(ESP.getFreeHeap()).c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\""), output); - BRIDGE::print(F("SDK"), output); - if (!plaintext)BRIDGE::print(F("\":\""), output); - else BRIDGE::print(F(": "), output); - BRIDGE::print(ESP.getSdkVersion(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"flash_size\":\""), output); - else BRIDGE::print(F("Flash Size: "), output); - BRIDGE::print(formatBytes(ESP.getFlashChipSize()).c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); -#ifdef ARDUINO_ARCH_ESP8266 - fs::FSInfo info; - SPIFFS.info(info); - if (!plaintext)BRIDGE::print(F("\"update_size\":\""), output); - else BRIDGE::print(F("Available Size for update: "), output); - uint32_t flashsize = ESP.getFlashChipSize(); - if (flashsize > 1024 * 1024) flashsize = 1024 * 1024; - BRIDGE::print(formatBytes(flashsize - ESP.getSketchSize()-info.totalBytes).c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else { - if ((flashsize - ESP.getSketchSize()-info.totalBytes) > (ESP.getSketchSize())) BRIDGE::println(F("(Ok)"), output); - else BRIDGE::println(F("(Not enough)"), output); - } - - if (!plaintext)BRIDGE::print(F("\"spiffs_size\":\""), output); - else BRIDGE::print(F("Available Size for SPIFFS: "), output); - - BRIDGE::print(formatBytes(info.totalBytes).c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); -#else - if (!plaintext)BRIDGE::print(F("\"update_size\":\""), output); - else BRIDGE::print(F("Available Size for update: "), output); - uint32_t flashsize = ESP.getFlashChipSize(); -//Not OTA on 2Mb board per spec - if (flashsize > 0x20000) flashsize = 0x140000; - else flashsize = 0x0; - BRIDGE::print(formatBytes(flashsize).c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else { - if (flashsize > 0x0) BRIDGE::println(F("(Ok)"), output); - else BRIDGE::print(F("(Not enough)"), output); - } - if (!plaintext)BRIDGE::print(F("\"spiffs_size\":\""), output); - else BRIDGE::print(F("Available Size for SPIFFS: "), output); - BRIDGE::print(formatBytes(SPIFFS.totalBytes()).c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); -#endif - if (!plaintext)BRIDGE::print(F("\"baud_rate\":\""), output); - else BRIDGE::print(F("Baud rate: "), output); - uint32_t br = ESP_SERIAL_OUT.baudRate(); -#ifdef ARDUINO_ARCH_ESP32 - //workaround for ESP32 - if(br == 115201)br = 115200; - if(br == 230423)br = 230400; -#endif - BRIDGE::print(String(br).c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"sleep_mode\":\""), output); - else BRIDGE::print(F("Sleep mode: "), output); -#ifdef ARDUINO_ARCH_ESP32 - wifi_ps_type_t ps_type; - esp_wifi_get_ps(&ps_type); -#else - WiFiSleepType_t ps_type; - ps_type = WiFi.getSleepMode(); -#endif - if (ps_type == WIFI_NONE_SLEEP) { - BRIDGE::print(F("None"), output); -#ifdef ARDUINO_ARCH_ESP8266 - } else if (ps_type == WIFI_LIGHT_SLEEP) { - BRIDGE::print(F("Light"), output); -#endif - } else if (ps_type == WIFI_MODEM_SLEEP) { - BRIDGE::print(F("Modem"), output); - } else { - BRIDGE::print(F("???"), output); - } - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"channel\":\""), output); - else BRIDGE::print(F("Channel: "), output); - BRIDGE::print(String(WiFi.channel()).c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); -#ifdef ARDUINO_ARCH_ESP32 - uint8_t PhyMode; - if (WiFi.getMode() == WIFI_STA)esp_wifi_get_protocol(ESP_IF_WIFI_STA, &PhyMode); - else esp_wifi_get_protocol(ESP_IF_WIFI_AP, &PhyMode); -#else - WiFiPhyMode_t PhyMode = WiFi.getPhyMode(); -#endif - if (!plaintext)BRIDGE::print(F("\"phy_mode\":\""), output); - else BRIDGE::print(F("Phy Mode: "), output); - if (PhyMode == (WIFI_PHY_MODE_11G))BRIDGE::print(F("11g"), output); - else if (PhyMode == (WIFI_PHY_MODE_11B))BRIDGE::print(F("11b"), output); - else if (PhyMode == (WIFI_PHY_MODE_11N))BRIDGE::print(F("11n"), output); - else BRIDGE::print(F("???"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"web_port\":\""), output); - else BRIDGE::print(F("Web port: "), output); - BRIDGE::print(String(wifi_config.iweb_port).c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"data_port\":\""), output); - else BRIDGE::print(F("Data port: "), output); -#ifdef TCP_IP_DATA_FEATURE - BRIDGE::print(String(wifi_config.idata_port).c_str(), output); -#else - BRIDGE::print(F("Disabled"), output); -#endif - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (WiFi.getMode() == WIFI_STA || WiFi.getMode() == WIFI_AP_STA) { - if (!plaintext)BRIDGE::print(F("\"hostname\":\""), output); - else BRIDGE::print(F("Hostname: "), output); -#ifdef ARDUINO_ARCH_ESP32 - BRIDGE::print(WiFi.getHostname(), output); -#else - BRIDGE::print(WiFi.hostname().c_str(), output); -#endif - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - } - - if (!plaintext)BRIDGE::print(F("\"active_mode\":\""), output); - else BRIDGE::print(F("Active Mode: "), output); - if (WiFi.getMode() == WIFI_STA) { - BRIDGE::print(F("Station ("), output); - BRIDGE::print(WiFi.macAddress().c_str(), output); - BRIDGE::print(F(")"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - if (WiFi.isConnected()) { - if (!plaintext)BRIDGE::print(F("\"connected_ssid\":\""), output); - else BRIDGE::print(F("Connected to: "), output); - BRIDGE::print(WiFi.SSID().c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - if (!plaintext)BRIDGE::print(F("\"connected_signal\":\""), output); - else BRIDGE::print(F("Signal: "), output); - BRIDGE::print(String(wifi_config.getSignal(WiFi.RSSI())).c_str(), output); - BRIDGE::print(F("%"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - } - else { - if (!plaintext)BRIDGE::print(F("\"connection_status\":\""), output); - else BRIDGE::print(F("Connection Status: "), output); - BRIDGE::print(F("Connection Status: "), output); - if (WiFi.status() == WL_DISCONNECTED) { - BRIDGE::print(F("Disconnected"), output); - } else if (WiFi.status() == WL_CONNECTION_LOST) { - BRIDGE::print(F("Connection lost"), output); - } else if (WiFi.status() == WL_CONNECT_FAILED) { - BRIDGE::print(F("Connection failed"), output); - } else if (WiFi.status() == WL_NO_SSID_AVAIL) { - BRIDGE::print(F("No connection"), output); - } else if (WiFi.status() == WL_IDLE_STATUS ) { - BRIDGE::print(F("Idle"), output); - } else BRIDGE::print(F("Unknown"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - } - if (!plaintext)BRIDGE::print(F("\"ip_mode\":\""), output); - else BRIDGE::print(F("IP Mode: "), output); -#ifdef ARDUINO_ARCH_ESP32 - tcpip_adapter_dhcp_status_t dhcp_status; - tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_STA, &dhcp_status); - if (dhcp_status == TCPIP_ADAPTER_DHCP_STARTED) -#else - if (wifi_station_dhcpc_status()==DHCP_STARTED) -#endif - { - BRIDGE::print(F("DHCP"), output); - } - else BRIDGE::print(F("Static"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"ip\":\""), output); - else BRIDGE::print(F("IP: "), output); - BRIDGE::print(WiFi.localIP().toString().c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"gw\":\""), output); - else BRIDGE::print(F("Gateway: "), output); - BRIDGE::print(WiFi.gatewayIP().toString().c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"msk\":\""), output); - else BRIDGE::print(F("Mask: "), output); - BRIDGE::print(WiFi.subnetMask().toString().c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"dns\":\""), output); - else BRIDGE::print(F("DNS: "), output); - BRIDGE::print(WiFi.dnsIP().toString().c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"disabled_mode\":\""), output); - else BRIDGE::print(F("Disabled Mode: "), output); - BRIDGE::print(F("Access Point ("), output); - BRIDGE::print(WiFi.softAPmacAddress().c_str(), output); - BRIDGE::print(F(")"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - } else if (WiFi.getMode() == WIFI_AP) { - BRIDGE::print(F("Access Point ("), output); - BRIDGE::print(WiFi.softAPmacAddress().c_str(), output); - BRIDGE::print(F(")"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - //get current config -#ifdef ARDUINO_ARCH_ESP32 - wifi_ap_config_t apconfig; - wifi_config_t conf; - esp_wifi_get_config(ESP_IF_WIFI_AP, &conf); - apconfig.ssid_hidden = conf.ap.ssid_hidden; - apconfig.authmode = conf.ap.authmode; - apconfig.max_connection = conf.ap.max_connection; -#else - struct softap_config apconfig; - wifi_softap_get_config(&apconfig); -#endif - if (!plaintext)BRIDGE::print(F("\"ap_ssid\":\""), output); - else BRIDGE::print(F("SSID: "), output); -#ifdef ARDUINO_ARCH_ESP32 - BRIDGE::print((const char*)conf.ap.ssid, output); -#else - BRIDGE::print((const char*)apconfig.ssid, output); -#endif - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"ssid_visible\":\""), output); - else BRIDGE::print(F("Visible: "), output); - BRIDGE::print((apconfig.ssid_hidden == 0)?F("Yes"):F("No"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"ssid_authentication\":\""), output); - else BRIDGE::print(F("Authentication: "), output); - if (apconfig.authmode==AUTH_OPEN) { - BRIDGE::print(F("None"), output); - } else if (apconfig.authmode==AUTH_WEP) { - BRIDGE::print(F("WEP"), output); - } else if (apconfig.authmode==AUTH_WPA_PSK) { - BRIDGE::print(F("WPA"), output); - } else if (apconfig.authmode==AUTH_WPA2_PSK) { - BRIDGE::print(F("WPA2"), output); - } else { - BRIDGE::print(F("WPA/WPA2"), output); - } - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"ssid_max_connections\":\""), output); - else BRIDGE::print(F("Max Connections: "), output); - BRIDGE::print(String(apconfig.max_connection).c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"ssid_dhcp\":\""), output); - else BRIDGE::print(F("DHCP Server: "), output); -#ifdef ARDUINO_ARCH_ESP32 - tcpip_adapter_dhcp_status_t dhcp_status; - tcpip_adapter_dhcps_get_status(TCPIP_ADAPTER_IF_AP, &dhcp_status); - if (dhcp_status == TCPIP_ADAPTER_DHCP_STARTED) -#else - if(wifi_softap_dhcps_status() == DHCP_STARTED) -#endif - { - BRIDGE::print(F("Started"), output); - } - else BRIDGE::print(F("Stopped"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"ip\":\""), output); - else BRIDGE::print(F("IP: "), output); - BRIDGE::print(WiFi.softAPIP().toString().c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); -#ifdef ARDUINO_ARCH_ESP32 - tcpip_adapter_ip_info_t ip; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip); -#else - struct ip_info ip; - wifi_get_ip_info(SOFTAP_IF, &ip); -#endif - if (!plaintext)BRIDGE::print(F("\"gw\":\""), output); - else BRIDGE::print(F("Gateway: "), output); - BRIDGE::print(IPAddress(ip.gw.addr).toString().c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"msk\":\""), output); - else BRIDGE::print(F("Mask: "), output); - BRIDGE::print(IPAddress(ip.netmask.addr).toString().c_str(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - - if (!plaintext)BRIDGE::print(F("\"connected_clients\":["), output); - else BRIDGE::print(F("Connected clients: "), output); - int client_counter=0; -#ifdef ARDUINO_ARCH_ESP32 - wifi_sta_list_t station; - tcpip_adapter_sta_list_t tcpip_sta_list; - esp_wifi_ap_get_sta_list(&station); - tcpip_adapter_get_sta_list(&station, &tcpip_sta_list); -#else - struct station_info * station; - station = wifi_softap_get_station_info(); -#endif - String stmp =""; -#ifdef ARDUINO_ARCH_ESP32 - for (int i=0; i < station.num; i++){ -#else - while(station) { -#endif - if(stmp.length() > 0) { - if (!plaintext)stmp+=F(","); - else stmp+=F("\n"); - - } - if (!plaintext)stmp+=F("{\"bssid\":\""); - //BSSID -#ifdef ARDUINO_ARCH_ESP32 - stmp += CONFIG::mac2str(tcpip_sta_list.sta[i].mac); -#else - stmp += CONFIG::mac2str(station->bssid); -#endif - if (!plaintext)stmp+=F("\",\"ip\":\""); - else stmp += F(" "); - //IP -#ifdef ARDUINO_ARCH_ESP32 - stmp += IPAddress(tcpip_sta_list.sta[i].ip.addr).toString().c_str(); -#else - stmp += IPAddress((const uint8_t *)&station->ip).toString().c_str(); -#endif - if (!plaintext)stmp+=F("\"}"); - //increment counter - client_counter++; -#ifdef ARDUINO_ARCH_ESP32 - } -#else - //go next record - station =STAILQ_NEXT(station, next); - } - wifi_softap_free_station_info(); -#endif - if (!plaintext) { - BRIDGE::print(stmp.c_str(), output); - BRIDGE::print(F("],"), output); - } - else { - //display number of client - BRIDGE::println(String(client_counter).c_str(), output); - //display list if any - if(stmp.length() > 0)BRIDGE::println(stmp.c_str(), output); - } - - if (!plaintext)BRIDGE::print(F("\"disabled_mode\":\""), output); - else BRIDGE::print(F("Disabled Mode: "), output); - BRIDGE::print(F("Station ("), output); - BRIDGE::print(WiFi.macAddress().c_str(), output); - BRIDGE::print(F(") is disabled"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - } else if (WiFi.getMode() == WIFI_AP_STA) { - BRIDGE::print(F("Mixed"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - if (!plaintext)BRIDGE::print(F("\"active_mode\":\""), output); - else BRIDGE::print(F("Active Mode: "), output); - BRIDGE::print(F("Access Point ("), output); - BRIDGE::print(WiFi.softAPmacAddress().c_str(), output); - BRIDGE::println(F(")"), output); - BRIDGE::print(F("Station ("), output); - BRIDGE::print(WiFi.macAddress().c_str(), output); - BRIDGE::print(F(")"), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - } else { - BRIDGE::print("Wifi Off", output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - } - - if (!plaintext)BRIDGE::print(F("\"captive_portal\":\""), output); - else BRIDGE::print(F("Captive portal: "), output); -#ifdef CAPTIVE_PORTAL_FEATURE - BRIDGE::print(F("Enabled"), output); -#else - BRIDGE::print(F("Disabled"), output); -#endif - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"ssdp\":\""), output); - else BRIDGE::print(F("SSDP: "), output); -#ifdef SSDP_FEATURE - BRIDGE::print(F("Enabled"), output); -#else - BRIDGE::print(F("Disabled"), output); -#endif - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"netbios\":\""), output); - else BRIDGE::print(F("NetBios: "), output); -#ifdef NETBIOS_FEATURE - BRIDGE::print(F("Enabled"), output); -#else - BRIDGE::print(F("Disabled"), output); -#endif - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"mdns\":\""), output); - else BRIDGE::print(F("mDNS: "), output); -#ifdef MDNS_FEATURE - BRIDGE::print(F("Enabled"), output); -#else - BRIDGE::print(F("Disabled"), output); -#endif - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"web_update\":\""), output); - else BRIDGE::print(F("Web Update: "), output); -#ifdef WEB_UPDATE_FEATURE - BRIDGE::print(F("Enabled"), output); -#else - BRIDGE::print(F("Disabled"), output); -#endif - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"pin recovery\":\""), output); - else BRIDGE::print(F("Pin Recovery: "), output); -#ifdef RECOVERY_FEATURE - BRIDGE::print(F("Enabled"), output); -#else - BRIDGE::print(F("Disabled"), output); -#endif - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"autentication\":\""), output); - else BRIDGE::print(F("Authentication: "), output); -#ifdef AUTHENTICATION_FEATURE - BRIDGE::print(F("Enabled"), output); -#else - BRIDGE::print(F("Disabled"), output); -#endif - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); - - if (!plaintext)BRIDGE::print(F("\"target_fw\":\""), output); - else BRIDGE::print(F("Target Firmware: "), output); - BRIDGE::print(CONFIG::GetFirmwareTargetName(), output); - if (!plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); -#ifdef DEBUG_ESP3D - if (!plaintext)BRIDGE::print(F("\"debug\":\""), output); - else BRIDGE::print(F("Debug: "), output); - BRIDGE::print(F("Debug Enabled :"), output); -#ifdef DEBUG_OUTPUT_SPIFFS - BRIDGE::print(F("SPIFFS"), output); -#endif -#ifdef DEBUG_OUTPUT_SD - BRIDGE::print(F("SD"), output); -#endif -#ifdef DEBUG_OUTPUT_SERIAL - BRIDGE::print(F("serial"), output); -#endif -#ifdef DEBUG_OUTPUT_TCP - BRIDGE::print(F("TCP"), output); -#endif - if (plaintext)BRIDGE::print(F("\","), output); - else BRIDGE::print(F("\n"), output); -#endif - if (!plaintext)BRIDGE::print(F("\"fw\":\""), output); - else BRIDGE::print(F("FW version: "), output); - BRIDGE::print(FW_VERSION, output); - if (!plaintext)BRIDGE::print(F("\"}"), output); - else BRIDGE::print(F("\n"), output); -} diff --git a/esp3d/data/index.html.gz b/esp3d/data/index.html.gz deleted file mode 100644 index 8ed88ab7..00000000 Binary files a/esp3d/data/index.html.gz and /dev/null differ diff --git a/esp3d/esp3d.ino b/esp3d/esp3d.ino deleted file mode 100644 index 24bec6aa..00000000 --- a/esp3d/esp3d.ino +++ /dev/null @@ -1,189 +0,0 @@ -/* - This file is part of ESP3D Firmware for 3D printer. - - ESP3D Firmware for 3D printer is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ESP3D Firmware for 3D printer 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this Firmware. If not, see . - - This firmware is using the standard arduino IDE with module to support ESP8266: - https://github.com/esp8266/Arduino from Bootmanager - - Latest version of the code and documentation can be found here : - https://github.com/luc-github/ESP3D - - Main author: luc lebosse - -*/ -//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 -#include "config.h" -#include "wificonf.h" -#include "bridge.h" -#include "webinterface.h" -#include "command.h" -#ifdef ARDUINO_ARCH_ESP8266 -#include "ESP8266WiFi.h" -#ifdef MDNS_FEATURE -#include -#endif -#include -#else -#include -#ifdef MDNS_FEATURE -#include -#endif -#include "esp_wifi.h" -#include -#include "FS.h" -#include "SPIFFS.h" -#include "Update.h" -#endif -#include - - -#ifdef CAPTIVE_PORTAL_FEATURE -#include -extern DNSServer dnsServer; -#endif -#ifdef SSDP_FEATURE -#ifdef ARDUINO_ARCH_ESP8266 -#include -#else -//#include -#endif -#endif -#ifdef NETBIOS_FEATURE -#ifdef ARDUINO_ARCH_ESP8266 -#include -#else -//#include -#endif -#endif -#ifndef FS_NO_GLOBALS -#define FS_NO_GLOBALS -#endif -#include - -void setup() -{ - bool breset_config=false; - bool directsd_check = false; - web_interface = NULL; -#ifdef TCP_IP_DATA_FEATURE - data_server = NULL; -#endif - // init: -#ifdef DEBUG_ESP3D - 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 - if (digitalRead(RESET_CONFIG_PIN)==0) { - breset_config=true; //if requested =>reset settings - } -#endif - //check if EEPROM has value - if ( !CONFIG::InitBaudrate() || !CONFIG::InitExternalPorts()) { - breset_config=true; //cannot access to config settings=> reset settings - LOG("Error no EEPROM access\r\n") - } - - //reset is requested - if(breset_config) { - //update EEPROM with default settings - 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); - ESP_SERIAL_OUT.println(F("M117 ESP EEPROM reset")); -#ifdef DEBUG_ESP3D - 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); - ESP_SERIAL_OUT.flush(); -#endif - //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()) { - ESP_SERIAL_OUT.println(F("M117 Safe mode 1")); - //try again in AP mode - if (!wifi_config.Setup(true)) { - ESP_SERIAL_OUT.println(F("M117 Safe mode 2")); - wifi_config.Safe_Setup(); - } - } - delay(1000); - //setup servers - if (!wifi_config.Enable_servers()) { - ESP_SERIAL_OUT.println(F("M117 Error enabling servers")); - } - 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->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(); - } -} diff --git a/esp3d/nofile.h b/esp3d/nofile.h deleted file mode 100644 index e2fff90d..00000000 --- a/esp3d/nofile.h +++ /dev/null @@ -1,326 +0,0 @@ -/* - 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 -}; diff --git a/esp3d/storestrings.cpp b/esp3d/storestrings.cpp deleted file mode 100644 index 9f306118..00000000 --- a/esp3d/storestrings.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - storestrings.cpp - rolling storage class - - 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 -*/ -#include "storestrings.h" -//Constructor -STORESTRINGS_CLASS::STORESTRINGS_CLASS (int maxsize, int maxstringlength) -{ -//for rolling buffer -//if max size is reached then remove oldest one and add the new one - _maxsize=maxsize; -//to limit the storage space - _maxstringlength=maxstringlength; -//need space for the "..." - if (_maxstringlength<4 && _maxstringlength!=-1) { - _maxstringlength=4; - } -} -//Destructor -STORESTRINGS_CLASS::~STORESTRINGS_CLASS () -{ - // clear list and content - clear(); -} - -bool STORESTRINGS_CLASS::setsize(int size) -{ - _maxsize=size; - return true; -} -bool STORESTRINGS_CLASS::setlength(int len) -{ - if (len < 4) { - return false; - } - _maxstringlength = len; - return true; -} - -//Clear list and content -void STORESTRINGS_CLASS::clear() -{ - //while list is not empty - while(_charlist.size()) { - //remove element - char * str = _charlist.pop(); - //destroy it - delete str; - } -} - -bool STORESTRINGS_CLASS::add (const __FlashStringHelper *str) -{ - String stmp; - stmp=str; - return add(stmp.c_str()); -} -//Add element in storage -bool STORESTRINGS_CLASS::add (const char * string) -{ - //if we reach max size - if (_maxsize==_charlist.size()) { - //remove oldest one - char * str = _charlist.shift(); - delete str; - } - //add new one - //get size including \0 at the end - size_t size = strlen(string)+1; - bool need_resize=false; - if ( (_maxstringlength!=-1) && (size >_maxstringlength+1 )) { - need_resize = true; - size=_maxstringlength+1; - } - //reserve memory - char * ptr = new char[size*sizeof(char)]; - //copy string to storage - if (need_resize) { - //copy maximum length minus 3 - strncpy(ptr,string,_maxstringlength-3); - strcpy(ptr+_maxstringlength-3,"..."); - } else { - //copy as it is - strcpy(ptr,string); - } - //add storage to list - _charlist.add(ptr); - return true; -} -//Remove element at pos position -bool STORESTRINGS_CLASS::remove(int pos) -{ - //be sure index is in range - if (pos<0 && pos>(_charlist.size()-1)) { - return false; - } - //remove item from list - char * str = _charlist.remove(pos); - //destroy item - delete str; - return true; -} -//Get element at pos position -const char * STORESTRINGS_CLASS::get(int pos) -{ - //be sure index is in range - if (pos<0 && pos>(_charlist.size()-1)) { - return NULL; - } - return (const char *) _charlist.get(pos); -} -//Get index for defined string -int STORESTRINGS_CLASS::get_index(const char * string) -{ - //parse the list until it is found - for (int p=0; p<_charlist.size(); p++) { - if (strcmp ( _charlist.get(p), string)==0) { - return p; - } - } - //if not found return -1 - return -1; -} - diff --git a/esp3d/storestrings.h b/esp3d/storestrings.h deleted file mode 100644 index ca89df0a..00000000 --- a/esp3d/storestrings.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - storestrings.h - rolling storage class - - 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 -*/ - -#ifndef STORESTRINGS_h -#define STORESTRINGS_h -#include -#include "GenLinkedList.h" -class STORESTRINGS_CLASS -{ -public: - STORESTRINGS_CLASS (int maxsize = -1, int maxstringlength=-1); - ~STORESTRINGS_CLASS (); - bool add (const char * string); - inline bool add (String & string) - { - return add(string.c_str()); - }; - bool add (const __FlashStringHelper *str); - bool remove(int pos); - const char * get(int pos); - int get_index(const char * string); - void clear(); - inline int size() - { - return _charlist.size(); - }; - bool setsize(int size); - bool setlength(int len); - inline int getsize() - { - return _maxsize; - }; - inline int getlength() - { - return _maxstringlength; - }; - -private: - int _maxsize; - int _maxstringlength; - GenLinkedList _charlist; -}; - -#endif diff --git a/esp3d/wificonf.cpp b/esp3d/wificonf.cpp deleted file mode 100644 index de45a921..00000000 --- a/esp3d/wificonf.cpp +++ /dev/null @@ -1,505 +0,0 @@ -/* - wificonf.cpp - ESP3D configuration class - - 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 -*/ -#include "config.h" -#include "wificonf.h" -#include "bridge.h" -#include "webinterface.h" - -#ifdef ARDUINO_ARCH_ESP8266 -#include "ESP8266WiFi.h" -#ifdef MDNS_FEATURE -#include -#endif -#else -#include -#include "esp_wifi.h" -#ifdef MDNS_FEATURE -#include -#endif -#endif -#include "IPAddress.h" -#ifdef CAPTIVE_PORTAL_FEATURE -#include -DNSServer dnsServer; -const byte DNS_PORT = 53; -#endif -#ifdef ARDUINO_ARCH_ESP8266 -#include -#else -#include -#endif -#ifdef SSDP_FEATURE -#include -#endif -#ifdef NETBIOS_FEATURE -#ifdef ARDUINO_ARCH_ESP8266 -#include -#else -#include -#endif -#endif -#ifdef ARDUINO_ARCH_ESP8266 -extern "C" { -#include "user_interface.h" -} -#endif -#ifdef TIMESTAMP_FEATURE -#include -#endif - -WIFI_CONFIG::WIFI_CONFIG() -{ - iweb_port=DEFAULT_WEB_PORT; - idata_port=DEFAULT_DATA_PORT; - baud_rate=DEFAULT_BAUD_RATE; - sleep_mode=DEFAULT_SLEEP_MODE; - _hostname[0]=0; -} - -int32_t WIFI_CONFIG::getSignal(int32_t RSSI) -{ - if (RSSI <= -100) { - return 0; - } - if (RSSI >= -50) { - return 100; - } - return (2* (RSSI+100)); -} - -const char * WIFI_CONFIG::get_hostname() -{ - 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,hname.c_str()); - } - return _hostname; -} - -const char * WIFI_CONFIG::get_default_hostname() -{ - static char hostname[13]; - uint8_t mac [WL_MAC_ADDR_LENGTH]; - WiFi.macAddress(mac); - if (0>sprintf(hostname,"ESP_%02X%02X%02X",mac[3],mac[4],mac[5])) { - strcpy (hostname, "ESP8266"); - } - return hostname; -} - -//safe setup if no connection -void WIFI_CONFIG::Safe_Setup() -{ -#ifdef CAPTIVE_PORTAL_FEATURE - dnsServer.stop(); - delay(100); -#endif - - WiFi.disconnect(); - //setup Soft AP - WiFi.mode(WIFI_AP); - IPAddress local_ip (DEFAULT_IP_VALUE[0],DEFAULT_IP_VALUE[1],DEFAULT_IP_VALUE[2],DEFAULT_IP_VALUE[3]); - IPAddress gateway (DEFAULT_GATEWAY_VALUE[0],DEFAULT_GATEWAY_VALUE[1],DEFAULT_GATEWAY_VALUE[2],DEFAULT_GATEWAY_VALUE[3]); - IPAddress subnet (DEFAULT_MASK_VALUE[0],DEFAULT_MASK_VALUE[1],DEFAULT_MASK_VALUE[2],DEFAULT_MASK_VALUE[3]); - String ssid = FPSTR(DEFAULT_AP_SSID); - String pwd = FPSTR(DEFAULT_AP_PASSWORD); - WiFi.softAP(ssid.c_str(),pwd.c_str()); - delay(500); - WiFi.softAPConfig( local_ip, gateway, subnet); - delay(1000); - ESP_SERIAL_OUT.println(F("M117 Safe mode started")); -} - -//Read configuration settings and apply them -bool WIFI_CONFIG::Setup(bool force_ap) -{ - char pwd[MAX_PASSWORD_LENGTH+1]; - char sbuf[MAX_SSID_LENGTH+1]; - char hostname [MAX_HOSTNAME_LENGTH+1]; - //int wstatus; - IPAddress currentIP; - byte bflag=0; - byte bmode=0; - //system_update_cpu_freq(SYS_CPU_160MHZ); - //set the sleep mode - if (!CONFIG::read_byte(EP_SLEEP_MODE, &bflag )) { - 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; - } else { - //AP or client ? - if (!CONFIG::read_byte(EP_WIFI_MODE, &bmode ) ) { - LOG("Error read wifi mode\r\n") - return false; - } - } - if (!CONFIG::read_string(EP_HOSTNAME, hostname, MAX_HOSTNAME_LENGTH)) { - strcpy(hostname,get_default_hostname()); - } - //this is AP mode - if (bmode==AP_MODE) { - LOG("Set AP mode\r\n") - if(!CONFIG::read_string(EP_AP_SSID, sbuf, MAX_SSID_LENGTH)) { - return false; - } - if(!CONFIG::read_string(EP_AP_PASSWORD, pwd, MAX_PASSWORD_LENGTH)) { - return false; - } - ESP_SERIAL_OUT.print(FPSTR(M117_)); - ESP_SERIAL_OUT.print(F("SSID ")); - ESP_SERIAL_OUT.println(sbuf); - LOG("SSID ") - LOG(sbuf) - LOG("\r\n") - //DHCP or Static IP ? - if (!CONFIG::read_byte(EP_AP_IP_MODE, &bflag )) { - LOG("Error IP mode\r\n") - return false; - } - LOG("IP Mode: ") - LOG(CONFIG::intTostr(bflag)) - LOG("\r\n") - if (bflag==STATIC_IP_MODE) { - byte ip_buf[4]; - LOG("Static mode\r\n") - //get the IP - LOG("IP value:") - if (!CONFIG::read_buffer(EP_AP_IP_VALUE,ip_buf, IP_LENGTH)) { - LOG("Error\r\n") - return false; - } - IPAddress local_ip (ip_buf[0],ip_buf[1],ip_buf[2],ip_buf[3]); - LOG(local_ip.toString()) - LOG("\r\nGW value:") - //get the gateway - if (!CONFIG::read_buffer(EP_AP_GATEWAY_VALUE,ip_buf, IP_LENGTH)) { - LOG("Error\r\n") - return false; - } - IPAddress gateway (ip_buf[0],ip_buf[1],ip_buf[2],ip_buf[3]); - LOG(gateway.toString()) - LOG("\r\nMask value:") - //get the mask - if (!CONFIG::read_buffer(EP_AP_MASK_VALUE,ip_buf, IP_LENGTH)) { - LOG("Error Mask value\r\n") - return false; - } - IPAddress subnet (ip_buf[0],ip_buf[1],ip_buf[2],ip_buf[3]); - LOG(subnet.toString()) - LOG("\r\n") - //apply according active wifi mode - LOG("Set IP\r\n") - WiFi.softAPConfig( local_ip, gateway, subnet); - delay(100); - } - 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); -#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)) { - 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)) { - return false; - } - if(!CONFIG::read_string(EP_STA_PASSWORD, pwd, MAX_PASSWORD_LENGTH)) { - return false; - } - ESP_SERIAL_OUT.print(FPSTR(M117_)); - ESP_SERIAL_OUT.print(F("SSID ")); - ESP_SERIAL_OUT.println(sbuf); - LOG("SSID ") - LOG(sbuf) - LOG("\r\n") - if (!CONFIG::read_byte(EP_STA_IP_MODE, &bflag )) { - return false; - } - if (bflag==STATIC_IP_MODE) { - byte ip_buf[4]; - //get the IP - if (!CONFIG::read_buffer(EP_STA_IP_VALUE,ip_buf, IP_LENGTH)) { - return false; - } - IPAddress local_ip (ip_buf[0],ip_buf[1],ip_buf[2],ip_buf[3]); - //get the gateway - if (!CONFIG::read_buffer(EP_STA_GATEWAY_VALUE,ip_buf, IP_LENGTH)) { - return false; - } - IPAddress gateway (ip_buf[0],ip_buf[1],ip_buf[2],ip_buf[3]); - //get the mask - if (!CONFIG::read_buffer(EP_STA_MASK_VALUE,ip_buf, IP_LENGTH)) { - return false; - } - IPAddress subnet (ip_buf[0],ip_buf[1],ip_buf[2],ip_buf[3]); - //apply according active wifi mode - WiFi.config( local_ip, gateway, subnet); - } - 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); -#ifdef ARDUINO_ARCH_ESP8266 - WiFi.setPhyMode((WiFiPhyMode_t)bflag); -#endif - WiFi.begin(sbuf, pwd); - 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: - ESP_SERIAL_OUT.print(FPSTR(M117_)); - ESP_SERIAL_OUT.println(F("No SSID found!")); - break; - - case 4: - ESP_SERIAL_OUT.print(FPSTR(M117_)); - ESP_SERIAL_OUT.println(F("No Connection!")); - break; - - default: - 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); -#else - WiFi.setHostname(hostname); -#endif - } - - //Get IP - if (WiFi.getMode()==WIFI_STA) { - currentIP=WiFi.localIP(); - } else { - currentIP=WiFi.softAPIP(); - } - 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; diff --git a/libraries/WebServer/src/ESP8266WebServer.h b/examples/basicesp3d/basicesp3d.ino similarity index 66% rename from libraries/WebServer/src/ESP8266WebServer.h rename to examples/basicesp3d/basicesp3d.ino index d4330064..13f28c3b 100644 --- a/libraries/WebServer/src/ESP8266WebServer.h +++ b/examples/basicesp3d/basicesp3d.ino @@ -1,8 +1,7 @@ /* - ESP8266WebServer.h - Dead simple web-server. - Supports only one simultaneous client, knows how to handle GET and POST. + basic esp3d sample - Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + 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 @@ -17,13 +16,21 @@ 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) */ +//library include +#include "esp3d.h" +//global variable +Esp3D myesp3d; -#ifndef ESP8266WEBSERVER_H -#define ESP8266WEBSERVER_H +//Setup +void setup() +{ + myesp3d.begin(); +} -#include - -#endif //ESP8266WEBSERVER_H +//main loop +void loop() +{ + myesp3d.process(); +} diff --git a/libraries/AsyncTCP/.travis.yml b/libraries/AsyncTCP/.travis.yml new file mode 100644 index 00000000..89808806 --- /dev/null +++ b/libraries/AsyncTCP/.travis.yml @@ -0,0 +1,37 @@ +sudo: false +language: bash +os: + - linux + +script: + - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16 + - sleep 3 + - export DISPLAY=:1.0 + - wget http://downloads.arduino.cc/arduino-1.6.5-linux64.tar.xz + - tar xf arduino-1.6.5-linux64.tar.xz + - mv arduino-1.6.5 $HOME/arduino_ide + - export PATH="$HOME/arduino_ide:$PATH" + - which arduino + - mkdir -p $HOME/Arduino/libraries + - cp -r $TRAVIS_BUILD_DIR $HOME/Arduino/libraries/ESPAsyncTCP + - cd $HOME/arduino_ide/hardware + - mkdir esp8266com + - cd esp8266com + - git clone https://github.com/esp8266/Arduino.git esp8266 + - cd esp8266/tools + - python get.py + - source $TRAVIS_BUILD_DIR/travis/common.sh + - arduino --board esp8266com:esp8266:generic --save-prefs + - arduino --get-pref sketchbook.path + - build_sketches arduino $HOME/Arduino/libraries/ESPAsyncTCP esp8266 + +notifications: + email: + on_success: change + on_failure: change + webhooks: + urls: + - https://webhooks.gitter.im/e/60e65d0c78ea0a920347 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: false # default: false diff --git a/libraries/AsyncTCP/LICENSE b/libraries/AsyncTCP/LICENSE new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/libraries/AsyncTCP/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + 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 that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU 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 as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/libraries/AsyncTCP/README.md b/libraries/AsyncTCP/README.md new file mode 100644 index 00000000..023849bf --- /dev/null +++ b/libraries/AsyncTCP/README.md @@ -0,0 +1,11 @@ +# AsyncTCP +Async TCP Library for ESP32 Arduino + +[![Join the chat at https://gitter.im/me-no-dev/ESPAsyncWebServer](https://badges.gitter.im/me-no-dev/ESPAsyncWebServer.svg)](https://gitter.im/me-no-dev/ESPAsyncWebServer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +This is a fully asynchronous TCP library, aimed at enabling trouble-free, multi-connection network environment for Espressif's ESP32 MCUs. + +This library is the base for [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) + +## AsyncClient and AsyncServer +The base classes on which everything else is built. They expose all possible scenarios, but are really raw and require more skills to use. diff --git a/libraries/AsyncTCP/component.mk b/libraries/AsyncTCP/component.mk new file mode 100644 index 00000000..bb5bb161 --- /dev/null +++ b/libraries/AsyncTCP/component.mk @@ -0,0 +1,3 @@ +COMPONENT_ADD_INCLUDEDIRS := src +COMPONENT_SRCDIRS := src +CXXFLAGS += -fno-rtti diff --git a/libraries/AsyncTCP/library.json b/libraries/AsyncTCP/library.json new file mode 100644 index 00000000..0644061e --- /dev/null +++ b/libraries/AsyncTCP/library.json @@ -0,0 +1,19 @@ +{ + "name":"AsyncTCP", + "description":"Asynchronous TCP Library for ESP32", + "keywords":"async,tcp", + "authors": + { + "name": "Hristo Gochkov", + "maintainer": true + }, + "repository": + { + "type": "git", + "url": "https://github.com/me-no-dev/AsyncTCP.git" + }, + "version": "1.0.0", + "license": "LGPL-3.0", + "frameworks": "arduino", + "platforms": ["espressif32", "espressif32_stage"] +} diff --git a/libraries/AsyncTCP/library.properties b/libraries/AsyncTCP/library.properties new file mode 100644 index 00000000..d9e8e47d --- /dev/null +++ b/libraries/AsyncTCP/library.properties @@ -0,0 +1,9 @@ +name=AsyncTCP +version=1.0.0 +author=Me-No-Dev +maintainer=Me-No-Dev +sentence=Async TCP Library for ESP32 +paragraph=Async TCP Library for ESP32 +category=Other +url=https://github.com/me-no-dev/AsyncTCP +architectures=* diff --git a/libraries/AsyncTCP/src/AsyncTCP.cpp b/libraries/AsyncTCP/src/AsyncTCP.cpp new file mode 100644 index 00000000..ba1929b8 --- /dev/null +++ b/libraries/AsyncTCP/src/AsyncTCP.cpp @@ -0,0 +1,1055 @@ +/* + Asynchronous TCP library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 + */ + +#include "Arduino.h" + +#include "AsyncTCP.h" +extern "C"{ +#include "lwip/opt.h" +#include "lwip/tcp.h" +#include "lwip/inet.h" +#include "lwip/dns.h" +} + +/* + * TCP/IP Event Task + * */ + +typedef enum { + LWIP_TCP_SENT, LWIP_TCP_RECV, LWIP_TCP_ERROR, LWIP_TCP_POLL +} lwip_event_t; + +typedef struct { + lwip_event_t event; + void *arg; + union { + struct { + void * pcb; + int8_t err; + } connected; + struct { + int8_t err; + } error; + struct { + tcp_pcb * pcb; + uint16_t len; + } sent; + struct { + tcp_pcb * pcb; + pbuf * pb; + int8_t err; + } recv; + struct { + tcp_pcb * pcb; + } poll; + struct { + tcp_pcb * pcb; + int8_t err; + } accept; + struct { + const char * name; + ip_addr_t addr; + } dns; + }; +} lwip_event_packet_t; + +static xQueueHandle _async_queue; +static TaskHandle_t _async_service_task_handle = NULL; + +static void _handle_async_event(lwip_event_packet_t * e){ + + if(e->event == LWIP_TCP_RECV){ + AsyncClient::_s_recv(e->arg, e->recv.pcb, e->recv.pb, e->recv.err); + } else if(e->event == LWIP_TCP_SENT){ + AsyncClient::_s_sent(e->arg, e->sent.pcb, e->sent.len); + } else if(e->event == LWIP_TCP_POLL){ + AsyncClient::_s_poll(e->arg, e->poll.pcb); + } else if(e->event == LWIP_TCP_ERROR){ + AsyncClient::_s_error(e->arg, e->error.err); + } + free((void*)(e)); +} + +static void _async_service_task(void *pvParameters){ + lwip_event_packet_t * packet = NULL; + for (;;) { + if(xQueueReceive(_async_queue, &packet, 0) == pdTRUE){ + //dispatch packet + _handle_async_event(packet); + } else { + vTaskDelay(1); + } + } + vTaskDelete(NULL); + _async_service_task_handle = NULL; +} +/* +static void _stop_async_task(){ + if(_async_service_task_handle){ + vTaskDelete(_async_service_task_handle); + _async_service_task_handle = NULL; + } +} +*/ +static bool _start_async_task(){ + if(!_async_queue){ + _async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *)); + if(!_async_queue){ + return false; + } + } + if(!_async_service_task_handle){ + xTaskCreatePinnedToCore(_async_service_task, "async_tcp", 8192, NULL, 3, &_async_service_task_handle, 1); + if(!_async_service_task_handle){ + return false; + } + } + return true; +} + +/* + * LwIP Callbacks + * */ + +static int8_t _tcp_poll(void * arg, struct tcp_pcb * pcb) { + if(!_async_queue){ + return ERR_OK; + } + lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); + e->event = LWIP_TCP_POLL; + e->arg = arg; + e->poll.pcb = pcb; + if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) { + free((void*)(e)); + } + return ERR_OK; +} + +static int8_t _tcp_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) { + if(!_async_queue){ + return ERR_OK; + } + lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); + e->event = LWIP_TCP_RECV; + e->arg = arg; + e->recv.pcb = pcb; + e->recv.pb = pb; + e->recv.err = err; + if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) { + free((void*)(e)); + } + return ERR_OK; +} + +static int8_t _tcp_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) { + if(!_async_queue){ + return ERR_OK; + } + lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); + e->event = LWIP_TCP_SENT; + e->arg = arg; + e->sent.pcb = pcb; + e->sent.len = len; + if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) { + free((void*)(e)); + } + return ERR_OK; +} + +static void _tcp_error(void * arg, int8_t err) { + if(!_async_queue){ + return; + } + lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); + e->event = LWIP_TCP_ERROR; + e->arg = arg; + e->error.err = err; + if (xQueueSend(_async_queue, &e, portMAX_DELAY) != pdPASS) { + free((void*)(e)); + } +} + +/* + * TCP/IP API Calls + * */ + +#include "lwip/priv/tcpip_priv.h" + +typedef struct { + struct tcpip_api_call call; + tcp_pcb * pcb; + int8_t err; + union { + struct { + const char* data; + size_t size; + uint8_t apiflags; + } write; + size_t received; + struct { + ip_addr_t * addr; + uint16_t port; + tcp_connected_fn cb; + } connect; + struct { + ip_addr_t * addr; + uint16_t port; + } bind; + uint8_t backlog; + }; +} tcp_api_call_t; + +static err_t _tcp_output_api(struct tcpip_api_call *api_call_msg){ + tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; + msg->err = tcp_output(msg->pcb); + return msg->err; +} + +static esp_err_t _tcp_output(tcp_pcb * pcb) { + tcp_api_call_t msg; + msg.pcb = pcb; + tcpip_api_call(_tcp_output_api, (struct tcpip_api_call*)&msg); + return msg.err; +} + +static err_t _tcp_write_api(struct tcpip_api_call *api_call_msg){ + tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; + msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags); + return msg->err; +} + +static esp_err_t _tcp_write(tcp_pcb * pcb, const char* data, size_t size, uint8_t apiflags) { + tcp_api_call_t msg; + msg.pcb = pcb; + msg.write.data = data; + msg.write.size = size; + msg.write.apiflags = apiflags; + tcpip_api_call(_tcp_write_api, (struct tcpip_api_call*)&msg); + return msg.err; +} + +static err_t _tcp_recved_api(struct tcpip_api_call *api_call_msg){ + tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; + msg->err = 0; + tcp_recved(msg->pcb, msg->received); + return msg->err; +} + +static esp_err_t _tcp_recved(tcp_pcb * pcb, size_t len) { + tcp_api_call_t msg; + msg.pcb = pcb; + msg.received = len; + tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call*)&msg); + return msg.err; +} + +static err_t _tcp_connect_api(struct tcpip_api_call *api_call_msg){ + tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; + msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, msg->connect.cb); + return msg->err; +} + +static esp_err_t _tcp_connect(tcp_pcb * pcb, ip_addr_t * addr, uint16_t port, tcp_connected_fn cb) { + tcp_api_call_t msg; + msg.pcb = pcb; + msg.connect.addr = addr; + msg.connect.port = port; + msg.connect.cb = cb; + tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call*)&msg); + return msg.err; +} + +static err_t _tcp_close_api(struct tcpip_api_call *api_call_msg){ + tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; + msg->err = tcp_close(msg->pcb); + return msg->err; +} + +static esp_err_t _tcp_close(tcp_pcb * pcb) { + tcp_api_call_t msg; + msg.pcb = pcb; + tcpip_api_call(_tcp_close_api, (struct tcpip_api_call*)&msg); + return msg.err; +} + +static err_t _tcp_abort_api(struct tcpip_api_call *api_call_msg){ + tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; + msg->err = 0; + tcp_abort(msg->pcb); + return msg->err; +} + +static esp_err_t _tcp_abort(tcp_pcb * pcb) { + tcp_api_call_t msg; + msg.pcb = pcb; + tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call*)&msg); + return msg.err; +} + +static err_t _tcp_bind_api(struct tcpip_api_call *api_call_msg){ + tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; + msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port); + return msg->err; +} + +static esp_err_t _tcp_bind(tcp_pcb * pcb, ip_addr_t * addr, uint16_t port) { + tcp_api_call_t msg; + msg.pcb = pcb; + msg.bind.addr = addr; + msg.bind.port = port; + tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call*)&msg); + return msg.err; +} + +static err_t _tcp_listen_api(struct tcpip_api_call *api_call_msg){ + tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; + msg->err = 0; + msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog); + return msg->err; +} + +static tcp_pcb * _tcp_listen_with_backlog(tcp_pcb * pcb, uint8_t backlog) { + tcp_api_call_t msg; + msg.pcb = pcb; + msg.backlog = backlog?backlog:0xFF; + tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call*)&msg); + return msg.pcb; +} +#define _tcp_listen(p) _tcp_listen_with_backlog(p, 0xFF); + + + +/* + Async TCP Client + */ + +AsyncClient::AsyncClient(tcp_pcb* pcb) +: _connect_cb(0) +, _connect_cb_arg(0) +, _discard_cb(0) +, _discard_cb_arg(0) +, _sent_cb(0) +, _sent_cb_arg(0) +, _error_cb(0) +, _error_cb_arg(0) +, _recv_cb(0) +, _recv_cb_arg(0) +, _timeout_cb(0) +, _timeout_cb_arg(0) +, _pcb_busy(false) +, _pcb_sent_at(0) +, _close_pcb(false) +, _ack_pcb(true) +, _rx_last_packet(0) +, _rx_since_timeout(0) +, _ack_timeout(ASYNC_MAX_ACK_TIME) +, _connect_port(0) +, prev(NULL) +, next(NULL) +, _in_lwip_thread(false) +{ + _pcb = pcb; + if(_pcb){ + _rx_last_packet = millis(); + tcp_arg(_pcb, this); + tcp_recv(_pcb, &_tcp_recv); + tcp_sent(_pcb, &_tcp_sent); + tcp_err(_pcb, &_tcp_error); + tcp_poll(_pcb, &_tcp_poll, 1); + } +} + +AsyncClient::~AsyncClient(){ + if(_pcb) + _close(); +} + +bool AsyncClient::connect(IPAddress ip, uint16_t port){ + if (_pcb){ + log_w("already connected, state %d", _pcb->state); + return false; + } + if(!_start_async_task()){ + log_e("failed to start task"); + return false; + } + + ip_addr_t addr; + addr.type = IPADDR_TYPE_V4; + addr.u_addr.ip4.addr = ip; + + tcp_pcb* pcb = tcp_new_ip_type(IPADDR_TYPE_V4); + if (!pcb){ + log_e("pcb == NULL"); + return false; + } + + tcp_arg(pcb, this); + tcp_err(pcb, &_tcp_error); + if(_in_lwip_thread){ + tcp_connect(pcb, &addr, port,(tcp_connected_fn)&_s_connected); + } else { + _tcp_connect(pcb, &addr, port,(tcp_connected_fn)&_s_connected); + } + return true; +} + +AsyncClient& AsyncClient::operator=(const AsyncClient& other){ + if (_pcb) + _close(); + + _pcb = other._pcb; + if (_pcb) { + _rx_last_packet = millis(); + tcp_arg(_pcb, this); + tcp_recv(_pcb, &_tcp_recv); + tcp_sent(_pcb, &_tcp_sent); + tcp_err(_pcb, &_tcp_error); + tcp_poll(_pcb, &_tcp_poll, 1); + } + return *this; +} + +int8_t AsyncClient::_connected(void* pcb, int8_t err){ + _pcb = reinterpret_cast(pcb); + if(_pcb){ + _rx_last_packet = millis(); + _pcb_busy = false; + tcp_recv(_pcb, &_tcp_recv); + tcp_sent(_pcb, &_tcp_sent); + tcp_poll(_pcb, &_tcp_poll, 1); + } + _in_lwip_thread = true; + if(_connect_cb) + _connect_cb(_connect_cb_arg, this); + _in_lwip_thread = false; + return ERR_OK; +} + +int8_t AsyncClient::_close(){ + int8_t err = ERR_OK; + if(_pcb) { + //log_i(""); + tcp_arg(_pcb, NULL); + tcp_sent(_pcb, NULL); + tcp_recv(_pcb, NULL); + tcp_err(_pcb, NULL); + tcp_poll(_pcb, NULL, 0); + if(_in_lwip_thread){ + err = tcp_close(_pcb); + } else { + err = _tcp_close(_pcb); + } + if(err != ERR_OK) { + err = abort(); + } + _pcb = NULL; + if(_discard_cb) + _discard_cb(_discard_cb_arg, this); + } + return err; +} + +void AsyncClient::_error(int8_t err) { + if(_pcb){ + tcp_arg(_pcb, NULL); + tcp_sent(_pcb, NULL); + tcp_recv(_pcb, NULL); + tcp_err(_pcb, NULL); + tcp_poll(_pcb, NULL, 0); + _pcb = NULL; + } + if(_error_cb) + _error_cb(_error_cb_arg, this, err); + if(_discard_cb) + _discard_cb(_discard_cb_arg, this); +} + +int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) { + _rx_last_packet = millis(); + //log_i("%u", len); + _pcb_busy = false; + if(_sent_cb) + _sent_cb(_sent_cb_arg, this, len, (millis() - _pcb_sent_at)); + return ERR_OK; +} + +int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) { + if(pb == NULL){ + return _close(); + } + + while(pb != NULL){ + _rx_last_packet = millis(); + //we should not ack before we assimilate the data + //log_i("%u", pb->len); + //Serial.write((const uint8_t *)pb->payload, pb->len); + _ack_pcb = true; + pbuf *b = pb; + if(_recv_cb) + _recv_cb(_recv_cb_arg, this, b->payload, b->len); + if(!_ack_pcb) + _rx_ack_len += b->len; + else + _tcp_recved(pcb, b->len); + pb = b->next; + b->next = NULL; + pbuf_free(b); + } + return ERR_OK; +} + +int8_t AsyncClient::_poll(tcp_pcb* pcb){ + // Close requested + if(_close_pcb){ + _close_pcb = false; + _close(); + return ERR_OK; + } + uint32_t now = millis(); + + // ACK Timeout + if(_pcb_busy && _ack_timeout && (now - _pcb_sent_at) >= _ack_timeout){ + _pcb_busy = false; + log_w("ack timeout %d", pcb->state); + if(_timeout_cb) + _timeout_cb(_timeout_cb_arg, this, (now - _pcb_sent_at)); + return ERR_OK; + } + // RX Timeout + if(_rx_since_timeout && (now - _rx_last_packet) >= (_rx_since_timeout * 1000)){ + log_w("rx timeout %d", pcb->state); + _close(); + return ERR_OK; + } + // Everything is fine + if(_poll_cb) + _poll_cb(_poll_cb_arg, this); + return ERR_OK; +} + +void AsyncClient::_dns_found(ip_addr_t *ipaddr){ + _in_lwip_thread = true; + if(ipaddr){ + connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port); + } else { + log_e("dns fail"); + if(_error_cb) + _error_cb(_error_cb_arg, this, -55); + if(_discard_cb) + _discard_cb(_discard_cb_arg, this); + } + _in_lwip_thread = false; +} + +bool AsyncClient::operator==(const AsyncClient &other) { + return _pcb == other._pcb; +} + +bool AsyncClient::connect(const char* host, uint16_t port){ + ip_addr_t addr; + err_t err = dns_gethostbyname(host, &addr, (dns_found_callback)&_s_dns_found, this); + if(err == ERR_OK) { + return connect(IPAddress(addr.u_addr.ip4.addr), port); + } else if(err == ERR_INPROGRESS) { + _connect_port = port; + return true; + } + log_e("error: %d", err); + return false; +} + +int8_t AsyncClient::abort(){ + if(_pcb) { + log_w("state %d", _pcb->state); + if(_in_lwip_thread){ + tcp_abort(_pcb); + } else { + _tcp_abort(_pcb); + } + _pcb = NULL; + } + return ERR_ABRT; +} + +void AsyncClient::close(bool now){ + if(_in_lwip_thread){ + tcp_recved(_pcb, _rx_ack_len); + } else { + _tcp_recved(_pcb, _rx_ack_len); + } + if(now) + _close(); + else + _close_pcb = true; +} + +void AsyncClient::stop() { + close(false); +} + +bool AsyncClient::free(){ + if(!_pcb) + return true; + if(_pcb->state == 0 || _pcb->state > 4) + return true; + return false; +} + +size_t AsyncClient::space(){ + if((_pcb != NULL) && (_pcb->state == 4)){ + return tcp_sndbuf(_pcb); + } + return 0; +} + +size_t AsyncClient::write(const char* data) { + if(data == NULL) + return 0; + return write(data, strlen(data)); +} + +size_t AsyncClient::write(const char* data, size_t size, uint8_t apiflags) { + size_t will_send = add(data, size, apiflags); + if(!will_send || !send()) + return 0; + return will_send; +} + + +size_t AsyncClient::add(const char* data, size_t size, uint8_t apiflags) { + if(!_pcb || size == 0 || data == NULL) + return 0; + size_t room = space(); + if(!room) + return 0; + size_t will_send = (room < size) ? room : size; + int8_t err = ERR_OK; + if(_in_lwip_thread){ + err = tcp_write(_pcb, data, will_send, apiflags); + } else { + err = _tcp_write(_pcb, data, will_send, apiflags); + } + if(err != ERR_OK) + return 0; + return will_send; +} + +bool AsyncClient::send(){ + int8_t err = ERR_OK; + if(_in_lwip_thread){ + err = tcp_output(_pcb); + } else { + err = _tcp_output(_pcb); + } + if(err == ERR_OK){ + _pcb_busy = true; + _pcb_sent_at = millis(); + return true; + } + return false; +} + +size_t AsyncClient::ack(size_t len){ + if(len > _rx_ack_len) + len = _rx_ack_len; + if(len){ + if(_in_lwip_thread){ + tcp_recved(_pcb, len); + } else { + _tcp_recved(_pcb, len); + } + } + _rx_ack_len -= len; + return len; +} + +// Operators + +AsyncClient & AsyncClient::operator+=(const AsyncClient &other) { + if(next == NULL){ + next = (AsyncClient*)(&other); + next->prev = this; + } else { + AsyncClient *c = next; + while(c->next != NULL) c = c->next; + c->next =(AsyncClient*)(&other); + c->next->prev = c; + } + return *this; +} + +void AsyncClient::setRxTimeout(uint32_t timeout){ + _rx_since_timeout = timeout; +} + +uint32_t AsyncClient::getRxTimeout(){ + return _rx_since_timeout; +} + +uint32_t AsyncClient::getAckTimeout(){ + return _ack_timeout; +} + +void AsyncClient::setAckTimeout(uint32_t timeout){ + _ack_timeout = timeout; +} + +void AsyncClient::setNoDelay(bool nodelay){ + if(!_pcb) + return; + if(nodelay) + tcp_nagle_disable(_pcb); + else + tcp_nagle_enable(_pcb); +} + +bool AsyncClient::getNoDelay(){ + if(!_pcb) + return false; + return tcp_nagle_disabled(_pcb); +} + +uint16_t AsyncClient::getMss(){ + if(_pcb) + return tcp_mss(_pcb); + return 0; +} + +uint32_t AsyncClient::getRemoteAddress() { + if(!_pcb) + return 0; + return _pcb->remote_ip.u_addr.ip4.addr; +} + +uint16_t AsyncClient::getRemotePort() { + if(!_pcb) + return 0; + return _pcb->remote_port; +} + +uint32_t AsyncClient::getLocalAddress() { + if(!_pcb) + return 0; + return _pcb->local_ip.u_addr.ip4.addr; +} + +uint16_t AsyncClient::getLocalPort() { + if(!_pcb) + return 0; + return _pcb->local_port; +} + +IPAddress AsyncClient::remoteIP() { + return IPAddress(getRemoteAddress()); +} + +uint16_t AsyncClient::remotePort() { + return getRemotePort(); +} + +IPAddress AsyncClient::localIP() { + return IPAddress(getLocalAddress()); +} + +uint16_t AsyncClient::localPort() { + return getLocalPort(); +} + +uint8_t AsyncClient::state() { + if(!_pcb) + return 0; + return _pcb->state; +} + +bool AsyncClient::connected(){ + if (!_pcb) + return false; + return _pcb->state == 4; +} + +bool AsyncClient::connecting(){ + if (!_pcb) + return false; + return _pcb->state > 0 && _pcb->state < 4; +} + +bool AsyncClient::disconnecting(){ + if (!_pcb) + return false; + return _pcb->state > 4 && _pcb->state < 10; +} + +bool AsyncClient::disconnected(){ + if (!_pcb) + return true; + return _pcb->state == 0 || _pcb->state == 10; +} + +bool AsyncClient::freeable(){ + if (!_pcb) + return true; + return _pcb->state == 0 || _pcb->state > 4; +} + +bool AsyncClient::canSend(){ + return space() > 0; +} + + +// Callback Setters + +void AsyncClient::onConnect(AcConnectHandler cb, void* arg){ + _connect_cb = cb; + _connect_cb_arg = arg; +} + +void AsyncClient::onDisconnect(AcConnectHandler cb, void* arg){ + _discard_cb = cb; + _discard_cb_arg = arg; +} + +void AsyncClient::onAck(AcAckHandler cb, void* arg){ + _sent_cb = cb; + _sent_cb_arg = arg; +} + +void AsyncClient::onError(AcErrorHandler cb, void* arg){ + _error_cb = cb; + _error_cb_arg = arg; +} + +void AsyncClient::onData(AcDataHandler cb, void* arg){ + _recv_cb = cb; + _recv_cb_arg = arg; +} + +void AsyncClient::onTimeout(AcTimeoutHandler cb, void* arg){ + _timeout_cb = cb; + _timeout_cb_arg = arg; +} + +void AsyncClient::onPoll(AcConnectHandler cb, void* arg){ + _poll_cb = cb; + _poll_cb_arg = arg; +} + + +void AsyncClient::_s_dns_found(const char * name, ip_addr_t * ipaddr, void * arg){ + reinterpret_cast(arg)->_dns_found(ipaddr); +} + +int8_t AsyncClient::_s_poll(void * arg, struct tcp_pcb * pcb) { + reinterpret_cast(arg)->_poll(pcb); + return ERR_OK; +} + +int8_t AsyncClient::_s_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) { + reinterpret_cast(arg)->_recv(pcb, pb, err); + return ERR_OK; +} + +int8_t AsyncClient::_s_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) { + reinterpret_cast(arg)->_sent(pcb, len); + return ERR_OK; +} + +void AsyncClient::_s_error(void * arg, int8_t err) { + reinterpret_cast(arg)->_error(err); +} + +int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){ + reinterpret_cast(arg)->_connected(pcb, err); + return ERR_OK; +} + +const char * AsyncClient::errorToString(int8_t error){ + switch(error){ + case 0: return "OK"; + case -1: return "Out of memory error"; + case -2: return "Buffer error"; + case -3: return "Timeout"; + case -4: return "Routing problem"; + case -5: return "Operation in progress"; + case -6: return "Illegal value"; + case -7: return "Operation would block"; + case -8: return "Connection aborted"; + case -9: return "Connection reset"; + case -10: return "Connection closed"; + case -11: return "Not connected"; + case -12: return "Illegal argument"; + case -13: return "Address in use"; + case -14: return "Low-level netif error"; + case -15: return "Already connected"; + case -55: return "DNS failed"; + default: return "UNKNOWN"; + } +} + +const char * AsyncClient::stateToString(){ + switch(state()){ + case 0: return "Closed"; + case 1: return "Listen"; + case 2: return "SYN Sent"; + case 3: return "SYN Received"; + case 4: return "Established"; + case 5: return "FIN Wait 1"; + case 6: return "FIN Wait 2"; + case 7: return "Close Wait"; + case 8: return "Closing"; + case 9: return "Last ACK"; + case 10: return "Time Wait"; + default: return "UNKNOWN"; + } +} + +/* + Async TCP Server + */ +struct pending_pcb { + tcp_pcb* pcb; + pbuf *pb; + struct pending_pcb * next; +}; + +AsyncServer::AsyncServer(IPAddress addr, uint16_t port) +: _port(port) +, _addr(addr) +, _noDelay(false) +, _in_lwip_thread(false) +, _pcb(0) +, _connect_cb(0) +, _connect_cb_arg(0) +{} + +AsyncServer::AsyncServer(uint16_t port) +: _port(port) +, _addr((uint32_t) IPADDR_ANY) +, _noDelay(false) +, _in_lwip_thread(false) +, _pcb(0) +, _connect_cb(0) +, _connect_cb_arg(0) +{} + +AsyncServer::~AsyncServer(){ + end(); +} + +void AsyncServer::onClient(AcConnectHandler cb, void* arg){ + _connect_cb = cb; + _connect_cb_arg = arg; +} + +int8_t AsyncServer::_s_accept(void * arg, tcp_pcb * pcb, int8_t err){ + reinterpret_cast(arg)->_accept(pcb, err); + return ERR_OK; +} + +int8_t AsyncServer::_accept(tcp_pcb* pcb, int8_t err){ + tcp_accepted(_pcb); + if(_connect_cb){ + + if (_noDelay) + tcp_nagle_disable(pcb); + else + tcp_nagle_enable(pcb); + + AsyncClient *c = new AsyncClient(pcb); + if(c){ + _in_lwip_thread = true; + c->_in_lwip_thread = true; + _connect_cb(_connect_cb_arg, c); + c->_in_lwip_thread = false; + _in_lwip_thread = false; + return ERR_OK; + } + } + if(tcp_close(pcb) != ERR_OK){ + tcp_abort(pcb); + } + log_e("FAIL"); + return ERR_OK; +} + +void AsyncServer::begin(){ + if(_pcb) + return; + + if(!_start_async_task()){ + log_e("failed to start task"); + return; + } + int8_t err; + _pcb = tcp_new_ip_type(IPADDR_TYPE_V4); + if (!_pcb){ + log_e("_pcb == NULL"); + return; + } + + ip_addr_t local_addr; + local_addr.type = IPADDR_TYPE_V4; + local_addr.u_addr.ip4.addr = (uint32_t) _addr; + err = _tcp_bind(_pcb, &local_addr, _port); + + if (err != ERR_OK) { + _tcp_close(_pcb); + log_e("bind error: %d", err); + return; + } + + static uint8_t backlog = 5; + _pcb = _tcp_listen_with_backlog(_pcb, backlog); + //_pcb = _tcp_listen(_pcb); + if (!_pcb) { + log_e("listen_pcb == NULL"); + return; + } + tcp_arg(_pcb, (void*) this); + tcp_accept(_pcb, &_s_accept); +} + +void AsyncServer::end(){ + if(_pcb){ + tcp_arg(_pcb, NULL); + tcp_accept(_pcb, NULL); + if(_in_lwip_thread){ + tcp_abort(_pcb); + } else { + _tcp_abort(_pcb); + } + _pcb = NULL; + } +} + +void AsyncServer::setNoDelay(bool nodelay){ + _noDelay = nodelay; +} + +bool AsyncServer::getNoDelay(){ + return _noDelay; +} + +uint8_t AsyncServer::status(){ + if (!_pcb) + return 0; + return _pcb->state; +} diff --git a/libraries/AsyncTCP/src/AsyncTCP.h b/libraries/AsyncTCP/src/AsyncTCP.h new file mode 100644 index 00000000..1c24ac92 --- /dev/null +++ b/libraries/AsyncTCP/src/AsyncTCP.h @@ -0,0 +1,191 @@ +/* + Asynchronous TCP library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ + +#ifndef ASYNCTCP_H_ +#define ASYNCTCP_H_ + +#include "IPAddress.h" +#include +extern "C" { +#include "freertos/semphr.h" +} + +class AsyncClient; + +#define ASYNC_MAX_ACK_TIME 5000 +#define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given) +#define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react. + +typedef std::function AcConnectHandler; +typedef std::function AcAckHandler; +typedef std::function AcErrorHandler; +typedef std::function AcDataHandler; +typedef std::function AcTimeoutHandler; + +struct tcp_pcb; +struct pbuf; +struct _ip_addr; + +class AsyncClient { + protected: + tcp_pcb* _pcb; + + AcConnectHandler _connect_cb; + void* _connect_cb_arg; + AcConnectHandler _discard_cb; + void* _discard_cb_arg; + AcAckHandler _sent_cb; + void* _sent_cb_arg; + AcErrorHandler _error_cb; + void* _error_cb_arg; + AcDataHandler _recv_cb; + void* _recv_cb_arg; + AcTimeoutHandler _timeout_cb; + void* _timeout_cb_arg; + AcConnectHandler _poll_cb; + void* _poll_cb_arg; + + bool _pcb_busy; + uint32_t _pcb_sent_at; + bool _close_pcb; + bool _ack_pcb; + uint32_t _rx_ack_len; + uint32_t _rx_last_packet; + uint32_t _rx_since_timeout; + uint32_t _ack_timeout; + uint16_t _connect_port; + + int8_t _close(); + int8_t _connected(void* pcb, int8_t err); + void _error(int8_t err); + int8_t _poll(tcp_pcb* pcb); + int8_t _sent(tcp_pcb* pcb, uint16_t len); + void _dns_found(struct _ip_addr *ipaddr); + + + public: + AsyncClient* prev; + AsyncClient* next; + + AsyncClient(tcp_pcb* pcb = 0); + ~AsyncClient(); + + AsyncClient & operator=(const AsyncClient &other); + AsyncClient & operator+=(const AsyncClient &other); + + bool operator==(const AsyncClient &other); + + bool operator!=(const AsyncClient &other) { + return !(*this == other); + } + bool connect(IPAddress ip, uint16_t port); + bool connect(const char* host, uint16_t port); + void close(bool now = false); + void stop(); + int8_t abort(); + bool free(); + + bool canSend();//ack is not pending + size_t space(); + size_t add(const char* data, size_t size, uint8_t apiflags=0);//add for sending + bool send();//send all data added with the method above + size_t ack(size_t len); //ack data that you have not acked using the method below + void ackLater(){ _ack_pcb = false; } //will not ack the current packet. Call from onData + + size_t write(const char* data); + size_t write(const char* data, size_t size, uint8_t apiflags=0); //only when canSend() == true + + uint8_t state(); + bool connecting(); + bool connected(); + bool disconnecting(); + bool disconnected(); + bool freeable();//disconnected or disconnecting + + uint16_t getMss(); + uint32_t getRxTimeout(); + void setRxTimeout(uint32_t timeout);//no RX data timeout for the connection in seconds + uint32_t getAckTimeout(); + void setAckTimeout(uint32_t timeout);//no ACK timeout for the last sent packet in milliseconds + void setNoDelay(bool nodelay); + bool getNoDelay(); + uint32_t getRemoteAddress(); + uint16_t getRemotePort(); + uint32_t getLocalAddress(); + uint16_t getLocalPort(); + + IPAddress remoteIP(); + uint16_t remotePort(); + IPAddress localIP(); + uint16_t localPort(); + + void onConnect(AcConnectHandler cb, void* arg = 0); //on successful connect + void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected + void onAck(AcAckHandler cb, void* arg = 0); //ack received + void onError(AcErrorHandler cb, void* arg = 0); //unsuccessful connect or error + void onData(AcDataHandler cb, void* arg = 0); //data received + void onTimeout(AcTimeoutHandler cb, void* arg = 0); //ack timeout + void onPoll(AcConnectHandler cb, void* arg = 0); //every 125ms when connected + + const char * errorToString(int8_t error); + const char * stateToString(); + + int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err); + + static int8_t _s_poll(void *arg, struct tcp_pcb *tpcb); + static int8_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, int8_t err); + static void _s_error(void *arg, int8_t err); + static int8_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len); + static int8_t _s_connected(void* arg, void* tpcb, int8_t err); + static void _s_dns_found(const char *name, struct _ip_addr *ipaddr, void *arg); + + bool _in_lwip_thread; +}; + +class AsyncServer { + protected: + uint16_t _port; + IPAddress _addr; + bool _noDelay; + bool _in_lwip_thread; + tcp_pcb* _pcb; + AcConnectHandler _connect_cb; + void* _connect_cb_arg; + + public: + + AsyncServer(IPAddress addr, uint16_t port); + AsyncServer(uint16_t port); + ~AsyncServer(); + void onClient(AcConnectHandler cb, void* arg); + void begin(); + void end(); + void setNoDelay(bool nodelay); + bool getNoDelay(); + uint8_t status(); + + static int8_t _s_accept(void *arg, tcp_pcb* newpcb, int8_t err); + protected: + int8_t _accept(tcp_pcb* newpcb, int8_t err); +}; + + +#endif /* ASYNCTCP_H_ */ diff --git a/libraries/AsyncTCP/travis/common.sh b/libraries/AsyncTCP/travis/common.sh new file mode 100644 index 00000000..57bede34 --- /dev/null +++ b/libraries/AsyncTCP/travis/common.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +function build_sketches() +{ + local arduino=$1 + local srcpath=$2 + local platform=$3 + local sketches=$(find $srcpath -name *.ino) + for sketch in $sketches; do + local sketchdir=$(dirname $sketch) + if [[ -f "$sketchdir/.$platform.skip" ]]; then + echo -e "\n\n ------------ Skipping $sketch ------------ \n\n"; + continue + fi + echo -e "\n\n ------------ Building $sketch ------------ \n\n"; + $arduino --verify $sketch; + local result=$? + if [ $result -ne 0 ]; then + echo "Build failed ($1)" + return $result + fi + done +} diff --git a/libraries/DHT_sensor_library_for_ESPx/DHTesp.cpp b/libraries/DHT_sensor_library_for_ESPx/DHTesp.cpp new file mode 100644 index 00000000..49d6ff6d --- /dev/null +++ b/libraries/DHT_sensor_library_for_ESPx/DHTesp.cpp @@ -0,0 +1,424 @@ +/****************************************************************** + DHT Temperature & Humidity Sensor library for Arduino & ESP32. + + Features: + - Support for DHT11 and DHT22/AM2302/RHT03 + - Auto detect sensor model + - Very low memory footprint + - Very small code + + https://github.com/beegee-tokyo/arduino-DHTesp + + Written by Mark Ruys, mark@paracas.nl. + Updated to work with ESP32 by Bernd Giesecke, bernd@giesecke.tk + + GNU General Public License, check LICENSE for more information. + All text above must be included in any redistribution. + + Datasheets: + - http://www.micro4you.com/files/sensor/DHT11.pdf + - http://www.adafruit.com/datasheets/DHT22.pdf + - http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Weather/RHT03.pdf + - http://meteobox.tk/files/AM2302.pdf + + Changelog: + 2013-06-10: Initial version + 2013-06-12: Refactored code + 2013-07-01: Add a resetTimer method + 2017-12-12: Added task switch disable + Added computeHeatIndex function from Adafruit DNT library + 2017-12-14: Added computeDewPoint function from idDHTLib Library + Added getComfortRatio function from libDHT Library + 2017-12-15: Added computePerception function + 2018-01-02: Added example for multiple sensors usage. + 2018-01-03: Added function getTempAndHumidity which returns temperature and humidity in one call. + 2018-01-03: Added retry in case the reading from the sensor fails with a timeout. + 2018-01-08: Added ESP8266 (and probably AVR) compatibility. + 2018-03-11: Updated DHT example +******************************************************************/ + +#include "DHTesp.h" + +void DHTesp::setup(uint8_t pin, DHT_MODEL_t model) +{ + DHTesp::pin = pin; + DHTesp::model = model; + DHTesp::resetTimer(); // Make sure we do read the sensor in the next readSensor() + + if ( model == AUTO_DETECT) { + DHTesp::model = DHT22; + readSensor(); + if ( error == ERROR_TIMEOUT ) { + DHTesp::model = DHT11; + // Warning: in case we auto detect a DHT11, you should wait at least 1000 msec + // before your first read request. Otherwise you will get a time out error. + } + } + + //Set default comfort profile. + + //In computing these constants the following reference was used + //http://epb.apogee.net/res/refcomf.asp + //It was simplified as 4 straight lines and added very little skew on + //the vertical lines (+0.1 on x for C,D) + //The for points used are(from top left, clock wise) + //A(30%, 30*C) B(70%, 26.2*C) C(70.1%, 20.55*C) D(30.1%, 22.22*C) + //On the X axis we have the rel humidity in % and on the Y axis the temperature in *C + + //Too hot line AB + m_comfort.m_tooHot_m = -0.095; + m_comfort.m_tooHot_b = 32.85; + //Too humid line BC + m_comfort.m_tooHumid_m = -56.5; + m_comfort.m_tooHumid_b = 3981.2; + //Too cold line DC + m_comfort.m_tooCold_m = -0.04175; + m_comfort.m_tooHCold_b = 23.476675; + //Too dry line AD + m_comfort.m_tooDry_m = -77.8; + m_comfort.m_tooDry_b = 2364; +} + +void DHTesp::resetTimer() +{ + DHTesp::lastReadTime = millis() - 3000; +} + +float DHTesp::getHumidity() +{ + readSensor(); + if ( error == ERROR_TIMEOUT ) { // Try a second time to read + readSensor(); + } + return humidity; +} + +float DHTesp::getTemperature() +{ + readSensor(); + if ( error == ERROR_TIMEOUT ) { // Try a second time to read + readSensor(); + } + return temperature; +} + +TempAndHumidity DHTesp::getTempAndHumidity() +{ + readSensor(); + if ( error == ERROR_TIMEOUT ) { // Try a second time to read + readSensor(); + } + values.temperature = temperature; + values.humidity = humidity; + return values; +} + +#ifndef OPTIMIZE_SRAM_SIZE + +const char* DHTesp::getStatusString() +{ + switch ( error ) { + case DHTesp::ERROR_TIMEOUT: + return "TIMEOUT"; + + case DHTesp::ERROR_CHECKSUM: + return "CHECKSUM"; + + default: + return "OK"; + } +} + +#else + +// At the expense of 26 bytes of extra PROGMEM, we save 11 bytes of +// SRAM by using the following method: + +prog_char P_OK[] PROGMEM = "OK"; +prog_char P_TIMEOUT[] PROGMEM = "TIMEOUT"; +prog_char P_CHECKSUM[] PROGMEM = "CHECKSUM"; + +const char *DHTesp::getStatusString() { + prog_char *c; + switch ( error ) { + case DHTesp::ERROR_CHECKSUM: + c = P_CHECKSUM; break; + + case DHTesp::ERROR_TIMEOUT: + c = P_TIMEOUT; break; + + default: + c = P_OK; break; + } + + static char buffer[9]; + strcpy_P(buffer, c); + + return buffer; +} + +#endif + +void DHTesp::readSensor() +{ + // Make sure we don't poll the sensor too often + // - Max sample rate DHT11 is 1 Hz (duty cicle 1000 ms) + // - Max sample rate DHT22 is 0.5 Hz (duty cicle 2000 ms) + unsigned long startTime = millis(); + if ( (unsigned long)(startTime - lastReadTime) < (model == DHT11 ? 999L : 1999L) ) { + return; + } + lastReadTime = startTime; + + temperature = NAN; + humidity = NAN; + + uint16_t rawHumidity = 0; + uint16_t rawTemperature = 0; + uint16_t data = 0; + + // Request sample + digitalWrite(pin, LOW); // Send start signal + pinMode(pin, OUTPUT); + if ( model == DHT11 ) { + delay(18); + } + else { + // This will fail for a DHT11 - that's how we can detect such a device + delayMicroseconds(800); + } + + pinMode(pin, INPUT); + digitalWrite(pin, HIGH); // Switch bus to receive data + + // We're going to read 83 edges: + // - First a FALLING, RISING, and FALLING edge for the start bit + // - Then 40 bits: RISING and then a FALLING edge per bit + // To keep our code simple, we accept any HIGH or LOW reading if it's max 85 usecs long + +#ifdef ESP32 + // ESP32 is a multi core / multi processing chip + // It is necessary to disable task switches during the readings + portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; + portENTER_CRITICAL(&mux); +#else + cli(); +#endif + for ( int8_t i = -3 ; i < 2 * 40; i++ ) { + byte age; + startTime = micros(); + + do { + age = (unsigned long)(micros() - startTime); + if ( age > 90 ) { + error = ERROR_TIMEOUT; +#ifdef ESP32 + portEXIT_CRITICAL(&mux); +#else + sei(); +#endif + return; + } + } + while ( digitalRead(pin) == (i & 1) ? HIGH : LOW ); + + if ( i >= 0 && (i & 1) ) { + // Now we are being fed our 40 bits + data <<= 1; + + // A zero max 30 usecs, a one at least 68 usecs. + if ( age > 30 ) { + data |= 1; // we got a one + } + } + + switch ( i ) { + case 31: + rawHumidity = data; + break; + case 63: + rawTemperature = data; + data = 0; + break; + } + } + +#ifdef ESP32 + portEXIT_CRITICAL(&mux); +#else + sei(); +#endif + + // Verify checksum + + if ( (byte)(((byte)rawHumidity) + (rawHumidity >> 8) + ((byte)rawTemperature) + (rawTemperature >> 8)) != data ) { + error = ERROR_CHECKSUM; + return; + } + + // Store readings + + if ( model == DHT11 ) { + humidity = rawHumidity >> 8; + temperature = rawTemperature >> 8; + } + else { + humidity = rawHumidity * 0.1; + + if ( rawTemperature & 0x8000 ) { + rawTemperature = -(int16_t)(rawTemperature & 0x7FFF); + } + temperature = ((int16_t)rawTemperature) * 0.1; + } + + error = ERROR_NONE; +} + +//boolean isFahrenheit: True == Fahrenheit; False == Celcius +float DHTesp::computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit) { + // Using both Rothfusz and Steadman's equations + // http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml + float hi; + + if (!isFahrenheit) { + temperature = toFahrenheit(temperature); + } + + hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) + (percentHumidity * 0.094)); + + if (hi > 79) { + hi = -42.379 + + 2.04901523 * temperature + + 10.14333127 * percentHumidity + + -0.22475541 * temperature*percentHumidity + + -0.00683783 * pow(temperature, 2) + + -0.05481717 * pow(percentHumidity, 2) + + 0.00122874 * pow(temperature, 2) * percentHumidity + + 0.00085282 * temperature*pow(percentHumidity, 2) + + -0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2); + + if((percentHumidity < 13) && (temperature >= 80.0) && (temperature <= 112.0)) + hi -= ((13.0 - percentHumidity) * 0.25) * sqrt((17.0 - abs(temperature - 95.0)) * 0.05882); + + else if((percentHumidity > 85.0) && (temperature >= 80.0) && (temperature <= 87.0)) + hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2); + } + + return isFahrenheit ? hi : toCelsius(hi); +} + +//boolean isFahrenheit: True == Fahrenheit; False == Celcius +float DHTesp::computeDewPoint(float temperature, float percentHumidity, bool isFahrenheit) { + // reference: http://wahiduddin.net/calc/density_algorithms.htm + if (isFahrenheit) { + temperature = toCelsius(temperature); + } + double A0 = 373.15 / (273.15 + (double) temperature); + double SUM = -7.90298 * (A0 - 1); + SUM += 5.02808 * log10(A0); + SUM += -1.3816e-7 * (pow(10, (11.344 * (1 - 1 / A0))) - 1) ; + SUM += 8.1328e-3 * (pow(10, (-3.49149 * (A0 - 1))) - 1) ; + SUM += log10(1013.246); + double VP = pow(10, SUM - 3) * (double) percentHumidity; + double Td = log(VP / 0.61078); // temp var + Td = (241.88 * Td) / (17.558 - Td); + return isFahrenheit ? toFahrenheit(Td) : Td; +} + +//boolean isFahrenheit: True == Fahrenheit; False == Celcius +byte DHTesp::computePerception(float temperature, float percentHumidity, bool isFahrenheit) { + // Computing human perception from dew point + // reference: https://en.wikipedia.org/wiki/Dew_point ==> Relationship to human comfort + // reference: Horstmeyer, Steve (2006-08-15). "Relative Humidity....Relative to What? The Dew Point Temperature...a better approach". Steve Horstmeyer, Meteorologist, WKRC TV, Cincinnati, Ohio, USA. Retrieved 2009-08-20. + // Using table + // Return value Dew point Human perception[6] + // 7 Over 26 °C Severely high, even deadly for asthma related illnesses + // 6 24–26 °C Extremely uncomfortable, oppressive + // 5 21–24 °C Very humid, quite uncomfortable + // 4 18–21 °C Somewhat uncomfortable for most people at upper edge + // 3 16–18 °C OK for most, but all perceive the humidity at upper edge + // 2 13–16 °C Comfortable + // 1 10–12 °C Very comfortable + // 0 Under 10 °C A bit dry for some + + if (isFahrenheit) { + temperature = toCelsius(temperature); + } + float dewPoint = computeDewPoint(temperature, percentHumidity); + + if (dewPoint < 10.0f) { + return Perception_Dry; + } else if (dewPoint < 13.0f) { + return Perception_VeryComfy; + } else if (dewPoint < 16.0f) { + return Perception_Comfy; + } else if (dewPoint < 18.0f) { + return Perception_Ok; + } else if (dewPoint < 21.0f) { + return Perception_UnComfy; + } else if (dewPoint < 24.0f) { + return Perception_QuiteUnComfy; + } else if (dewPoint < 26.0f) { + return Perception_VeryUnComfy; + } + // else dew >= 26.0 + return Perception_SevereUncomfy; +} + +//boolean isFahrenheit: True == Fahrenheit; False == Celcius +float DHTesp::getComfortRatio(ComfortState& destComfortStatus, float temperature, float percentHumidity, bool isFahrenheit) { + float ratio = 100; //100% + float distance = 0; + float kTempFactor = 3; //take into account the slope of the lines + float kHumidFactor = 0.1; //take into account the slope of the lines + uint8_t tempComfort = 0; + + if (isFahrenheit) { + temperature = toCelsius(temperature); + } + + destComfortStatus = Comfort_OK; + + distance = m_comfort.distanceTooHot(temperature, percentHumidity); + if(distance > 0) + { + //update the comfort descriptor + tempComfort += (uint8_t)Comfort_TooHot; + //decrease the comfot ratio taking the distance into account + ratio -= distance * kTempFactor; + } + + distance = m_comfort.distanceTooHumid(temperature, percentHumidity); + if(distance > 0) + { + //update the comfort descriptor + tempComfort += (uint8_t)Comfort_TooHumid; + //decrease the comfot ratio taking the distance into account + ratio -= distance * kHumidFactor; + } + + distance = m_comfort.distanceTooCold(temperature, percentHumidity); + if(distance > 0) + { + //update the comfort descriptor + tempComfort += (uint8_t)Comfort_TooCold; + //decrease the comfot ratio taking the distance into account + ratio -= distance * kTempFactor; + } + + distance = m_comfort.distanceTooDry(temperature, percentHumidity); + if(distance > 0) + { + //update the comfort descriptor + tempComfort += (uint8_t)Comfort_TooDry; + //decrease the comfot ratio taking the distance into account + ratio -= distance * kHumidFactor; + } + + destComfortStatus = (ComfortState)tempComfort; + + if(ratio < 0) + ratio = 0; + + return ratio; +} diff --git a/libraries/DHT_sensor_library_for_ESPx/DHTesp.h b/libraries/DHT_sensor_library_for_ESPx/DHTesp.h new file mode 100644 index 00000000..a062449a --- /dev/null +++ b/libraries/DHT_sensor_library_for_ESPx/DHTesp.h @@ -0,0 +1,170 @@ +/****************************************************************** + DHT Temperature & Humidity Sensor library for Arduino & ESP32. + + Features: + - Support for DHT11 and DHT22/AM2302/RHT03 + - Auto detect sensor model + - Very low memory footprint + - Very small code + + https://github.com/beegee-tokyo/arduino-DHTesp + + Written by Mark Ruys, mark@paracas.nl. + Updated to work with ESP32 by Bernd Giesecke, bernd@giesecke.tk + + GNU General Public License, check LICENSE for more information. + All text above must be included in any redistribution. + + Datasheets: + - http://www.micro4you.com/files/sensor/DHT11.pdf + - http://www.adafruit.com/datasheets/DHT22.pdf + - http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Weather/RHT03.pdf + - http://meteobox.tk/files/AM2302.pdf + + Changelog: + 2013-06-10: Initial version + 2013-06-12: Refactored code + 2013-07-01: Add a resetTimer method + 2017-12-12: Added task switch disable + Added computeHeatIndex function from Adafruit DNT library + 2017-12-14: Added computeDewPoint function from idDHTLib Library + Added getComfortRatio function from libDHT Library + 2017-12-15: Added computePerception function + 2018-01-02: Added example for multiple sensors usage. + 2018-01-03: Added function getTempAndHumidity which returns temperature and humidity in one call. + 2018-01-03: Added retry in case the reading from the sensor fails with a timeout. + 2018-01-08: Added ESP8266 (and probably AVR) compatibility. + ******************************************************************/ + +#ifndef dhtesp_h +#define dhtesp_h + +#if ARDUINO < 100 + #include +#else + #include +#endif + +// Reference: http://epb.apogee.net/res/refcomf.asp (References invalid) +enum ComfortState { + Comfort_OK = 0, + Comfort_TooHot = 1, + Comfort_TooCold = 2, + Comfort_TooDry = 4, + Comfort_TooHumid = 8, + Comfort_HotAndHumid = 9, + Comfort_HotAndDry = 5, + Comfort_ColdAndHumid = 10, + Comfort_ColdAndDry = 6 +}; + +// References https://en.wikipedia.org/wiki/Dew_point ==> Relationship to human comfort +enum PerceptionState { + Perception_Dry = 0, + Perception_VeryComfy = 1, + Perception_Comfy = 2, + Perception_Ok = 3, + Perception_UnComfy = 4, + Perception_QuiteUnComfy = 5, + Perception_VeryUnComfy = 6, + Perception_SevereUncomfy = 7 +}; + +struct TempAndHumidity { + float temperature; + float humidity; +}; + +struct ComfortProfile +{ + //Represent the 4 line equations: + //dry, humid, hot, cold, using the y = mx + b formula + float m_tooHot_m, m_tooHot_b; + float m_tooCold_m, m_tooHCold_b; + float m_tooDry_m, m_tooDry_b; + float m_tooHumid_m, m_tooHumid_b; + + inline bool isTooHot(float temp, float humidity) {return (temp > (humidity * m_tooHot_m + m_tooHot_b));} + inline bool isTooHumid(float temp, float humidity) {return (temp > (humidity * m_tooHumid_m + m_tooHumid_b));} + inline bool isTooCold(float temp, float humidity) {return (temp < (humidity * m_tooCold_m + m_tooHCold_b));} + inline bool isTooDry(float temp, float humidity) {return (temp < (humidity * m_tooDry_m + m_tooDry_b));} + + inline float distanceTooHot(float temp, float humidity) {return temp - (humidity * m_tooHot_m + m_tooHot_b);} + inline float distanceTooHumid(float temp, float humidity) {return temp - (humidity * m_tooHumid_m + m_tooHumid_b);} + inline float distanceTooCold(float temp, float humidity) {return (humidity * m_tooCold_m + m_tooHCold_b) - temp;} + inline float distanceTooDry(float temp, float humidity) {return (humidity * m_tooDry_m + m_tooDry_b) - temp;} +}; + +class DHTesp +{ +public: + + typedef enum { + AUTO_DETECT, + DHT11, + DHT22, + AM2302, // Packaged DHT22 + RHT03 // Equivalent to DHT22 + } + DHT_MODEL_t; + + typedef enum { + ERROR_NONE = 0, + ERROR_TIMEOUT, + ERROR_CHECKSUM + } + DHT_ERROR_t; + + TempAndHumidity values; + + void setup(uint8_t pin, DHT_MODEL_t model=AUTO_DETECT); + void resetTimer(); + + float getTemperature(); + float getHumidity(); + TempAndHumidity getTempAndHumidity(); + + DHT_ERROR_t getStatus() { return error; }; + const char* getStatusString(); + + DHT_MODEL_t getModel() { return model; } + + int getMinimumSamplingPeriod() { return model == DHT11 ? 1000 : 2000; } + + int8_t getNumberOfDecimalsTemperature() { return model == DHT11 ? 0 : 1; }; + int8_t getLowerBoundTemperature() { return model == DHT11 ? 0 : -40; }; + int8_t getUpperBoundTemperature() { return model == DHT11 ? 50 : 125; }; + + int8_t getNumberOfDecimalsHumidity() { return 0; }; + int8_t getLowerBoundHumidity() { return model == DHT11 ? 20 : 0; }; + int8_t getUpperBoundHumidity() { return model == DHT11 ? 90 : 100; }; + + static float toFahrenheit(float fromCelcius) { return 1.8 * fromCelcius + 32.0; }; + static float toCelsius(float fromFahrenheit) { return (fromFahrenheit - 32.0) / 1.8; }; + + float computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit=false); + float computeDewPoint(float temperature, float percentHumidity, bool isFahrenheit=false); + float getComfortRatio(ComfortState& destComfStatus, float temperature, float percentHumidity, bool isFahrenheit=false); + ComfortProfile getComfortProfile() {return m_comfort;} + void setComfortProfile(ComfortProfile& c) {m_comfort = c;} + inline bool isTooHot(float temp, float humidity) {return m_comfort.isTooHot(temp, humidity);} + inline bool isTooHumid(float temp, float humidity) {return m_comfort.isTooHumid(temp, humidity);} + inline bool isTooCold(float temp, float humidity) {return m_comfort.isTooCold(temp, humidity);} + inline bool isTooDry(float temp, float humidity) {return m_comfort.isTooDry(temp, humidity);} + byte computePerception(float temperature, float percentHumidity, bool isFahrenheit=false); +protected: + void readSensor(); + + float temperature; + float humidity; + + uint8_t pin; + +private: + DHT_MODEL_t model; + DHT_ERROR_t error; + unsigned long lastReadTime; + ComfortProfile m_comfort; +}; + +#endif /*dhtesp_h*/ diff --git a/gpl.txt b/libraries/DHT_sensor_library_for_ESPx/LICENSE similarity index 99% rename from gpl.txt rename to libraries/DHT_sensor_library_for_ESPx/LICENSE index 94a9ed02..30ace6a8 100644 --- a/gpl.txt +++ b/libraries/DHT_sensor_library_for_ESPx/LICENSE @@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - - Copyright (C) + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - Copyright (C) + {project} Copyright (C) {year} {fullname} This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. @@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. \ No newline at end of file diff --git a/libraries/DHT_sensor_library_for_ESPx/README.md b/libraries/DHT_sensor_library_for_ESPx/README.md new file mode 100644 index 00000000..e51edcf9 --- /dev/null +++ b/libraries/DHT_sensor_library_for_ESPx/README.md @@ -0,0 +1,149 @@ +DHTesp +=== + +An Arduino library for reading the DHT family of temperature and humidity sensors. +Forked from [arduino-DHT](https://github.com/markruys/arduino-DHT) +Original written by Mark Ruys, . + +Why did I clone this library instead of forking the original repo and push the changes? +When I searched through Github for DHT libraries, I found a lot of them, some of them offers additional functions, some of them only basic temperature and humidity values. I wanted to combine all interesting functions into one library. In addition, none of the DHT libraries I found were written to work without errors on the ESP32. For ESP32 (a multi core/ multi processing SOC) task switching must be disabled while reading data from the sensor. +Another problem I found is that many of the available libraries use the same naming (dht.h, dht.cpp), which easily leads to conflicts if different libraries are used for different platforms. + +The library is tested as well on ESP8266 and should work on AVR boards as well. + +Changes to the original library: +-------- +- 2017-12-12: Renamed DHT class to DHTesp and filenames from dht.* to DHTesp.* to avoid conflicts with other libraries - beegee-tokyo, . +- 2017-12-12: Updated to work with ESP32 - beegee-tokyo, . +- 2017-12-12: Added function computeHeatIndex. Reference: [Adafruit DHT library](https://github.com/adafruit/DHT-sensor-library). +- 2017-12-14: Added function computeDewPoint. Reference: [idDHTLib](https://github.com/niesteszeck/idDHTLib). +- 2017-12-14: Added function getComfortRatio. Reference: [libDHT](https://github.com/ADiea/libDHT). (References about Human Comfort invalid) +- 2017-12-15: Added function computePerception. Reference: [WikiPedia Dew point==> Relationship to human comfort](https://en.wikipedia.org/wiki/Dew_point) - beegee-tokyo, . +- 2018-01-02: Added example for multiple sensors usage. +- 2018-01-03: Added function getTempAndHumidity which returns temperature and humidity in one call. +- 2018-01-03: Added retry in case the reading from the sensor fails with a timeout. +- 2018-01-08: Added ESP8266 (and probably AVR) compatibility. +- 2018-03-11: Updated DHT example +Features +-------- + - Support for DHT11 and DHT22, AM2302, RHT03 + - Auto detect sensor model + - Determine heat index + - Determine dewpoint + - Determine thermal comfort: + * Empiric comfort function based on comfort profiles(parametric lines) + * Multiple comfort profiles possible. Default based on http://epb.apogee.net/res/refcomf.asp (References invalid) + * Determine if it's too cold, hot, humid, dry, based on current comfort profile + * More info at [Determining Thermal Comfort Using a Humidity and Temperature Sensor](https://www.azosensors.com/article.aspx?ArticleID=487) + - Determine human perception based on humidity, temperature and dew point according to Horstmeyer, Steve (2006-08-15). [Relative Humidity....Relative to What? The Dew Point Temperature...a better approach](http://www.shorstmeyer.com/wxfaqs/humidity/humidity.html) + +Functions +----- +_**`void setup(uint8_t pin, DHT_MODEL_t model=AUTO_DETECT);`**_ +- Call to initialize the interface, define the GPIO pin to which the sensor is connected and define the sensor type. Valid sensor types are: + - AUTO_DETECT Try to detect which sensor is connected + - DHT11 + - DHT22 + - AM2302 Packaged DHT22 + - RHT03 Equivalent to DHT22 + +_**`void resetTimer();`**_ +- Reset last time the sensor was read + +_**`float getTemperature();`**_ +- Get the temperature in degree Centigrade from the sensor +Either one of _`getTemperature()`_ or _`getHumidity()`_ or _`getTempAndHumidity()`_ initiates reading a value from the sensor if the last reading was older than the minimal refresh time of the sensor. +See example _`DHT_ESP32.ino`_ or _`DHT_Test.ino`_ + +_**`float getHumidity();`**_ +- Get the humidity from the sensor +Either one of _`getTemperature()`_ or _`getHumidity()`_ or _`getTempAndHumidity()`_ initiates reading a value from the sensor if the last reading was older than the minimal refresh time of the sensor. +See example _`DHT_ESP32.ino`_ or _`DHT_Test.ino`_ + +_**`TempAndHumidity getTempAndHumidity();`**_ +- Get the temperature and humidity from the sensor +Either one of _`getTemperature()`_ or _`getHumidity()`_ or _`getTempAndHumidity()`_ initiates reading a value from the sensor if the last reading was older than the minimal refresh time of the sensor. +Return value is a struct of type _`TempAndHumidity`_ with temperature and humidity as float values. +See example _`DHT_Multi.ino`_ + +_**`DHT_ERROR_t getStatus();`**_ +- Get last error if reading from the sensor failed. Possible values are: + - ERROR_NONE no error occured + - ERROR_TIMEOUT timeout reading from the sensor + - ERROR_CHECKSUM checksum of received values doesn't match + +_**`const char* getStatusString();`**_ +- Get last error as a char * + +_**`DHT_MODEL_t getModel()`**_ +- Get detected (or defined) sensor type + +_**`int getMinimumSamplingPeriod();`**_ +- Get minimmum possible sampling period. For DHT11 this is 1000ms, for other sensors it is 2000ms + +_**`int8_t getNumberOfDecimalsTemperature();`**_ +- Get number of decimals in the temperature value. For DHT11 this is 0, for other sensors it is 1 + +_**`int8_t getLowerBoundTemperature();`**_ +- Get lower temperature range of the sensor. For DHT11 this is 0 degree Centigrade, for other sensors this is -40 degree Centrigrade + +_**`int8_t getUpperBoundTemperature();`**_ +- Get upper temperature range of the sensor. For DHT11 this is 50 degree Centigrade, for other sensors this is 125 degree Centrigrade + +_**`int8_t getNumberOfDecimalsHumidity();`**_ +- Get number of decimals in the humidity value. This is always 0. + +_**`int8_t getLowerBoundHumidity();`**_ +- Get lower humidity range of the sensor. For DHT11 this is 20 percent, for other sensors this is 0 percent + +_**`int8_t getUpperBoundHumidity();`**_ +- Get upper temperature range of the sensor. For DHT11 this is 90 percent, for other sensors this is 100 percent + +_**`static float toFahrenheit(float fromCelcius);`**_ +- Convert Centrigrade value to Fahrenheit value + +_**`static float toCelsius(float fromFahrenheit) { return (fromFahrenheit - 32.0) / 1.8; };`**_ +- Convert Fahrenheit value to Centigrade value + +_**`float computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit=false);`**_ +- Compute the heat index. Default temperature is in Centrigrade. + +_**`float computeDewPoint(float temperature, float percentHumidity, bool isFahrenheit=false);`**_ +- Compute the dew point. Default temperature is in Centrigrade. + +_**`float getComfortRatio(ComfortState& destComfStatus, float temperature, float percentHumidity, bool isFahrenheit=false);`**_ +- Compute the comfort ratio. Default temperature is in Centrigrade. Return values: +0 -> OK +1 -> Too Hot +2 -> Too cold +4 -> Too dry +8 -> Too humid +9 -> Hot and humid +5 -> Hot and dry +10 -> Cold and humid +6 -> Cold and dry + +_**`byte computePerception(float temperature, float percentHumidity, bool isFahrenheit=false);`**_ +- Compute the human perception. Default temperature is in Centrigrade. Return values: +0 -> Dry +1 -> Very comfortable +2 -> Comfortable +3 -> Ok +4 -> Uncomfortable +5 -> Quite uncomfortable +6 -> Very uncomfortable +7 -> Severe uncomfortable + +Usage +----- +See [examples](https://github.com/beegee-tokyo/DHTesp/blob/master/examples). For all the options, see [dhtesp.h](https://github.com/beegee-tokyo/DHTesp/blob/master/DHTesp.h). + +Installation +------------ + +In Arduino IDE open Sketch->Include Library->Manage Libraries then search for _**DHT ESP**_ +In PlatformIO open PlatformIO Home, switch to libraries and search for _**DHT ESP32**_. Or install the library in the terminal with _**`platformio lib install 2029`**_ + +For manual installation [download](https://github.com/beegee-tokyo/DHTesp/archive/master.zip) the archive, unzip it and place the DHTesp folder into the library directory. +In Arduino IDE this is usually _**`/libraries/`**_ +In PlatformIO this is usually _**``**_ diff --git a/libraries/DHT_sensor_library_for_ESPx/examples/DHT_ESP32/DHT_ESP32.ino b/libraries/DHT_sensor_library_for_ESPx/examples/DHT_ESP32/DHT_ESP32.ino new file mode 100644 index 00000000..282bbaa1 --- /dev/null +++ b/libraries/DHT_sensor_library_for_ESPx/examples/DHT_ESP32/DHT_ESP32.ino @@ -0,0 +1,172 @@ +#include "DHTesp.h" +#include "ESP32Ticker.h" + +/**************************************************************/ +/* Example how to read DHT sensors from an ESP32 using multi- */ +/* tasking. */ +/* This example depends on the ESP32Ticker library to wake up */ +/* the task every 20 seconds */ +/* Please install Ticker-esp32 library first */ +/* bertmelis/Ticker-esp32 */ +/* https://github.com/bertmelis/Ticker-esp32 */ +/**************************************************************/ + +DHTesp dht; + +void tempTask(void *pvParameters); +bool getTemperature(); +void triggerGetTemp(); + +/** Task handle for the light value read task */ +TaskHandle_t tempTaskHandle = NULL; +/** Ticker for temperature reading */ +Ticker tempTicker; +/** Comfort profile */ +ComfortState cf; +/** Flag if task should run */ +bool tasksEnabled = false; +/** Pin number for DHT11 data pin */ +int dhtPin = 17; + +/** + * initTemp + * Setup DHT library + * Setup task and timer for repeated measurement + * @return bool + * true if task and timer are started + * false if task or timer couldn't be started + */ +bool initTemp() { + byte resultValue = 0; + // Initialize temperature sensor + dht.setup(dhtPin, DHTesp::DHT11); + Serial.println("DHT initiated"); + + // Start task to get temperature + xTaskCreatePinnedToCore( + tempTask, /* Function to implement the task */ + "tempTask ", /* Name of the task */ + 4000, /* Stack size in words */ + NULL, /* Task input parameter */ + 5, /* Priority of the task */ + &tempTaskHandle, /* Task handle. */ + 1); /* Core where the task should run */ + + if (tempTaskHandle == NULL) { + Serial.println("Failed to start task for temperature update"); + return false; + } else { + // Start update of environment data every 20 seconds + tempTicker.attach(20, triggerGetTemp); + } + return true; +} + +/** + * triggerGetTemp + * Sets flag dhtUpdated to true for handling in loop() + * called by Ticker getTempTimer + */ +void triggerGetTemp() { + if (tempTaskHandle != NULL) { + xTaskResumeFromISR(tempTaskHandle); + } +} + +/** + * Task to reads temperature from DHT11 sensor + * @param pvParameters + * pointer to task parameters + */ +void tempTask(void *pvParameters) { + Serial.println("tempTask loop started"); + while (1) // tempTask loop + { + if (tasksEnabled) { + // Get temperature values + getTemperature(); + } + // Got sleep again + vTaskSuspend(NULL); + } +} + +/** + * getTemperature + * Reads temperature from DHT11 sensor + * @return bool + * true if temperature could be aquired + * false if aquisition failed +*/ +bool getTemperature() { + // Reading temperature for humidity takes about 250 milliseconds! + // Sensor readings may also be up to 2 seconds 'old' (it's a very slow sensor) + TempAndHumidity newValues = dht.getTempAndHumidity(); + // Check if any reads failed and exit early (to try again). + if (dht.getStatus() != 0) { + Serial.println("DHT11 error status: " + String(dht.getStatusString())); + return false; + } + + float heatIndex = dht.computeHeatIndex(newValues.temperature, newValues.humidity); + float dewPoint = dht.computeDewPoint(newValues.temperature, newValues.humidity); + float cr = dht.getComfortRatio(cf, newValues.temperature, newValues.humidity); + + String comfortStatus; + switch(cf) { + case Comfort_OK: + comfortStatus = "Comfort_OK"; + break; + case Comfort_TooHot: + comfortStatus = "Comfort_TooHot"; + break; + case Comfort_TooCold: + comfortStatus = "Comfort_TooCold"; + break; + case Comfort_TooDry: + comfortStatus = "Comfort_TooDry"; + break; + case Comfort_TooHumid: + comfortStatus = "Comfort_TooHumid"; + break; + case Comfort_HotAndHumid: + comfortStatus = "Comfort_HotAndHumid"; + break; + case Comfort_HotAndDry: + comfortStatus = "Comfort_HotAndDry"; + break; + case Comfort_ColdAndHumid: + comfortStatus = "Comfort_ColdAndHumid"; + break; + case Comfort_ColdAndDry: + comfortStatus = "Comfort_ColdAndDry"; + break; + default: + comfortStatus = "Unknown:"; + break; + }; + + Serial.println(" T:" + String(newValues.temperature) + " H:" + String(newValues.humidity) + " I:" + String(heatIndex) + " D:" + String(dewPoint) + " " + comfortStatus); + return true; +} + +void setup() +{ + Serial.begin(115200); + Serial.println(); + Serial.println("DHT ESP32 example with tasks"); + initTemp(); +} + +void loop() { + if (!tasksEnabled) { + // Wait 2 seconds to let system settle down + delay(2000); + // Enable task that will read values from the DHT sensor + tasksEnabled = true; + if (tempTaskHandle != NULL) { + vTaskResume(tempTaskHandle); + } + } + yield(); +} diff --git a/libraries/DHT_sensor_library_for_ESPx/examples/DHT_Multi/DHT_Multi.ino b/libraries/DHT_sensor_library_for_ESPx/examples/DHT_Multi/DHT_Multi.ino new file mode 100644 index 00000000..75997f19 --- /dev/null +++ b/libraries/DHT_sensor_library_for_ESPx/examples/DHT_Multi/DHT_Multi.ino @@ -0,0 +1,119 @@ +#include + +#include "ESP32Ticker.h" +#include "DHTesp.h" + +/** Initialize DHT sensor 1 */ +DHTesp dhtSensor1; +/** Initialize DHT sensor 2 */ +DHTesp dhtSensor2; +/** Initialize DHT sensor 3 */ +DHTesp dhtSensor3; +/** Task handle for the light value read task */ +TaskHandle_t tempTaskHandle = NULL; +/** Pin number for DHT11 1 data pin */ +int dhtPin1 = 17; +/** Pin number for DHT11 2 data pin */ +int dhtPin2 = 16; +/** Pin number for DHT11 3 data pin */ +int dhtPin3 = 27; +/** Ticker for temperature reading */ +Ticker tempTicker; +/** Flags for temperature readings finished */ +bool gotNewTemperature = false; +/** Data from sensor 1 */ +TempAndHumidity sensor1Data; +/** Data from sensor 2 */ +TempAndHumidity sensor2Data; +/** Data from sensor 3 */ +TempAndHumidity sensor3Data; + +/* Flag if main loop is running */ +bool tasksEnabled = false; + +/** + +/** + * Task to reads temperature from DHT11 sensor + * @param pvParameters + * pointer to task parameters + */ +void tempTask(void *pvParameters) { + Serial.println("tempTask loop started"); + while (1) // tempTask loop + { + if (tasksEnabled && !gotNewTemperature) { // Read temperature only if old data was processed already + // Reading temperature for humidity takes about 250 milliseconds! + // Sensor readings may also be up to 2 seconds 'old' (it's a very slow sensor) + sensor1Data = dhtSensor1.getTempAndHumidity(); // Read values from sensor 1 + sensor2Data = dhtSensor2.getTempAndHumidity(); // Read values from sensor 1 + sensor3Data = dhtSensor3.getTempAndHumidity(); // Read values from sensor 1 + gotNewTemperature = true; + } + vTaskSuspend(NULL); + } +} + +/** + * triggerGetTemp + * Sets flag dhtUpdated to true for handling in loop() + * called by Ticker tempTicker + */ +void triggerGetTemp() { + if (tempTaskHandle != NULL) { + xTaskResumeFromISR(tempTaskHandle); + } +} + +/** + * Arduino setup function (called once after boot/reboot) + */ +void setup() { + Serial.begin(115200); + Serial.println("Example for 3 DHT11/22 sensors"); + + // Initialize temperature sensor 1 + dhtSensor1.setup(dhtPin1, DHTesp::DHT11); + // Initialize temperature sensor 2 + dhtSensor2.setup(dhtPin2, DHTesp::DHT11); + // Initialize temperature sensor 3 + dhtSensor3.setup(dhtPin3, DHTesp::DHT11); + + // Start task to get temperature + xTaskCreatePinnedToCore( + tempTask, /* Function to implement the task */ + "tempTask ", /* Name of the task */ + 4000, /* Stack size in words */ + NULL, /* Task input parameter */ + 5, /* Priority of the task */ + &tempTaskHandle, /* Task handle. */ + 1); /* Core where the task should run */ + + if (tempTaskHandle == NULL) { + Serial.println("[ERROR] Failed to start task for temperature update"); + } else { + // Start update of environment data every 30 seconds + tempTicker.attach(30, triggerGetTemp); + } + + // Signal end of setup() to tasks + tasksEnabled = true; +} // End of setup. + + +/** + * loop + * Arduino loop function, called once 'setup' is complete (your own code + * should go here) + */ +void loop() { + if (gotNewTemperature) { + Serial.println("Sensor 1 data:"); + Serial.println("Temp: " + String(sensor1Data.temperature,2) + "'C Humidity: " + String(sensor1Data.humidity,1) + "%"); + Serial.println("Sensor 2 data:"); + Serial.println("Temp: " + String(sensor2Data.temperature,2) + "'C Humidity: " + String(sensor2Data.humidity,1) + "%"); + Serial.println("Sensor 3 data:"); + Serial.println("Temp: " + String(sensor3Data.temperature,2) + "'C Humidity: " + String(sensor3Data.humidity,1) + "%"); + gotNewTemperature = false; + } +} // End of loop diff --git a/libraries/DHT_sensor_library_for_ESPx/examples/DHT_Test/DHT_Test.ino b/libraries/DHT_sensor_library_for_ESPx/examples/DHT_Test/DHT_Test.ino new file mode 100644 index 00000000..a374e460 --- /dev/null +++ b/libraries/DHT_sensor_library_for_ESPx/examples/DHT_Test/DHT_Test.ino @@ -0,0 +1,38 @@ +#include "DHTesp.h" + +DHTesp dht; + +void setup() +{ + Serial.begin(115200); + Serial.println(); + Serial.println("Status\tHumidity (%)\tTemperature (C)\t(F)\tHeatIndex (C)\t(F)"); + + dht.setup(22); // Connect DHT sensor to GPIO 17 +} + +void loop() +{ + + //delay(dht.getMinimumSamplingPeriod()*1.2); + static uint32_t last_dht_update= 0; + uint32_t now_dht = millis(); + if (now_dht - last_dht_update > dht.getMinimumSamplingPeriod() *1.2) { + last_dht_update = now_dht; + float humidity = dht.getHumidity(); + float temperature = dht.getTemperature(); + + Serial.print(dht.getStatusString()); + Serial.print("\t H:"); + Serial.print(humidity, 1); + Serial.print("%\t\T :"); + Serial.print(temperature, 1); + Serial.println("C\t\t"); + //Serial.print(dht.toFahrenheit(temperature), 1); + //Serial.print("\t\t"); + //Serial.print(dht.computeHeatIndex(temperature, humidity, false), 1); + //Serial.print("\t\t"); + //Serial.println(dht.computeHeatIndex(dht.toFahrenheit(temperature), humidity, true), 1); + } +} + diff --git a/libraries/DHT_sensor_library_for_ESPx/keywords.txt b/libraries/DHT_sensor_library_for_ESPx/keywords.txt new file mode 100644 index 00000000..953f244b --- /dev/null +++ b/libraries/DHT_sensor_library_for_ESPx/keywords.txt @@ -0,0 +1,79 @@ +####################################### +# Syntax Coloring Map For DHTesp +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +DHTesp KEYWORD1 +TempAndHumidity KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +setup KEYWORD2 +getTemperature KEYWORD2 +getHumidity KEYWORD2 +getTempAndHumidity KEYWORD2 +getStatus KEYWORD2 +getStatusString KEYWORD2 +getModel KEYWORD2 +getMinimumSamplingPeriod KEYWORD2 +toFahrenheit KEYWORD2 +toCelsius KEYWORD2 +computeHeatIndex KEYWORD2 +computeDewPoint KEYWORD2 +getComfortRatio KEYWORD2 +getNumberOfDecimalsTemperature KEYWORD2 +getLowerBoundTemperature KEYWORD2 +getUpperBoundTemperature KEYWORD2 +getNumberOfDecimalsHumidity KEYWORD2 +getLowerBoundHumidity KEYWORD2 +getUpperBoundHumidity KEYWORD2 +getComfortProfile KEYWORD2 +setComfortProfile KEYWORD2 +isTooHot KEYWORD2 +isTooHumid KEYWORD2 +isTooCold KEYWORD2 +isTooDry KEYWORD2 +computePerception KEYWORD2 + +####################################### +# Instances (KEYWORD2) +####################################### + + +####################################### +# Constants (LITERAL1) +####################################### + +AUTO_DETECT LITERAL1 +DHT11 LITERAL1 +DHT22 LITERAL1 +AM2302 LITERAL1 +RHT03 LITERAL1 + +ERROR_NONE LITERAL1 +ERROR_TIMEOUT LITERAL1 +ERROR_CHECKSUM LITERAL1 + +Comfort_OK LITERAL1 +Comfort_TooHot LITERAL1 +Comfort_TooCold LITERAL1 +Comfort_TooDry LITERAL1 +Comfort_TooHumid LITERAL1 +Comfort_HotAndHumid LITERAL1 +Comfort_HotAndDry LITERAL1 +Comfort_ColdAndHumid LITERAL1 +Comfort_ColdAndDry LITERAL1 + +Perception_Dry LITERAL1 +Perception_VeryComfy LITERAL1 +Perception_Comfy LITERAL1 +Perception_Ok LITERAL1 +Perception_UnComfy LITERAL1 +Perception_QuiteUnComfy LITERAL1 +Perception_UnComfy LITERAL1 +Perception_VeryUncomfy LITERAL1 diff --git a/libraries/DHT_sensor_library_for_ESPx/library.json b/libraries/DHT_sensor_library_for_ESPx/library.json new file mode 100644 index 00000000..a9d7f27b --- /dev/null +++ b/libraries/DHT_sensor_library_for_ESPx/library.json @@ -0,0 +1,15 @@ +{ + "name": "DHT sensor library for ESPx", + "keywords": "onewire, 1-wire, bus, sensor, temperature", + "description": "Arduino ESP library for DHT11, DHT22, etc Temp & Humidity Sensors. Last changes: Updated example.", + "repository": + { + "type": "git", + "url": "https://github.com/beegee-tokyo/DHTesp.git" + }, + "version": "1.0.6", + "frameworks": "arduino", + "platforms": "*", + "license": "GPL-3.0-only", + "homepage": "http://desire.giesecke.tk/index.php/2018/01/30/esp32-dht11/" +} diff --git a/libraries/DHT_sensor_library_for_ESPx/library.properties b/libraries/DHT_sensor_library_for_ESPx/library.properties new file mode 100644 index 00000000..84d28855 --- /dev/null +++ b/libraries/DHT_sensor_library_for_ESPx/library.properties @@ -0,0 +1,10 @@ +name=DHT sensor library for ESPx +version=1.0.6 +author=beegee_tokyo +maintainer=beegee_tokyo +sentence=Arduino ESP library for DHT11, DHT22, etc Temp & Humidity Sensors +paragraph=Optimized libray to match ESP32 requirements. Last changes: Updated example. +category=Sensors +url=http://desire.giesecke.tk/index.php/2018/01/30/esp32-dht11/ +architectures=esp8266,esp32,arduino-esp32 +includes=DHTesp.h diff --git a/libraries/ESP32SSDP/ESP32SSDP.cpp b/libraries/ESP32SSDP/ESP32SSDP.cpp new file mode 100644 index 00000000..866cce57 --- /dev/null +++ b/libraries/ESP32SSDP/ESP32SSDP.cpp @@ -0,0 +1,442 @@ +/* +ESP32 Simple Service Discovery +Copyright (c) 2015 Hristo Gochkov + +Original (Arduino) version by Filippo Sallemi, July 23, 2014. +Can be found at: https://github.com/nomadnt/uSSDP + +License (MIT license): + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#include +#include "ESP32SSDP.h" +#include "WiFiUdp.h" +#include + +//#define DEBUG_SSDP Serial + +#define SSDP_INTERVAL 1200 +#define SSDP_PORT 1900 +#define SSDP_METHOD_SIZE 10 +#define SSDP_URI_SIZE 2 +#define SSDP_BUFFER_SIZE 64 +#define SSDP_MULTICAST_TTL 2 +static const IPAddress SSDP_MULTICAST_ADDR(239, 255, 255, 250); + + + +static const char _ssdp_response_template[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "EXT:\r\n"; + +static const char _ssdp_notify_template[] PROGMEM = + "NOTIFY * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "NTS: ssdp:alive\r\n"; + +static const char _ssdp_packet_template[] PROGMEM = + "%s" // _ssdp_response_template / _ssdp_notify_template + "CACHE-CONTROL: max-age=%u\r\n" // SSDP_INTERVAL + "SERVER: Arduino/1.0 UPNP/1.1 %s/%s\r\n" // _modelName, _modelNumber + "USN: uuid:%s\r\n" // _uuid + "%s: %s\r\n" // "NT" or "ST", _deviceType + "LOCATION: http://%u.%u.%u.%u:%u/%s\r\n" // WiFi.localIP(), _port, _schemaURL + "\r\n"; + +static const char _ssdp_schema_template[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/xml\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "\r\n" + "" + "" + "" + "1" + "0" + "" + "http://%u.%u.%u.%u:%u/" // WiFi.localIP(), _port + "" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "uuid:%s" + "" +// "" +// "" +// "image/png" +// "48" +// "48" +// "24" +// "icon48.png" +// "" +// "" +// "image/png" +// "120" +// "120" +// "24" +// "icon120.png" +// "" +// "" + "\r\n" + "\r\n"; + +struct SSDPTimer { + ETSTimer timer; +}; + +SSDPClass::SSDPClass() : +_server(0), +_timer(new SSDPTimer), +_port(80), +_ttl(SSDP_MULTICAST_TTL), +_respondToPort(0), +_pending(false), +_delay(0), +_process_time(0), +_notify_time(0) +{ + _uuid[0] = '\0'; + _modelNumber[0] = '\0'; + sprintf(_deviceType, "urn:schemas-upnp-org:device:Basic:1"); + _friendlyName[0] = '\0'; + _presentationURL[0] = '\0'; + _serialNumber[0] = '\0'; + _modelName[0] = '\0'; + _modelURL[0] = '\0'; + _manufacturer[0] = '\0'; + _manufacturerURL[0] = '\0'; + sprintf(_schemaURL, "ssdp/schema.xml"); +} + +SSDPClass::~SSDPClass(){ + delete _timer; +} + + +bool SSDPClass::begin(){ + _pending = false; + + uint32_t chipId = ((uint16_t) (ESP.getEfuseMac() >> 32)); + sprintf(_uuid, "38323636-4558-4dda-9188-cda0e6%02x%02x%02x", + (uint16_t) ((chipId >> 16) & 0xff), + (uint16_t) ((chipId >> 8) & 0xff), + (uint16_t) chipId & 0xff ); + +#ifdef DEBUG_SSDP + DEBUG_SSDP.printf("SSDP UUID: %s\n", (char *)_uuid); +#endif + + if (_server) { + delete (_server); + _server = 0; + } + + _server = new WiFiUDP; + + if (!(_server->beginMulticast(IPAddress(SSDP_MULTICAST_ADDR), SSDP_PORT))) { +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Error begin"); +#endif + return false; + } + + _startTimer(); + + return true; +} + +void SSDPClass::_send(ssdp_method_t method){ + char buffer[1460]; + IPAddress ip = WiFi.localIP(); + + char valueBuffer[strlen_P(_ssdp_notify_template)+1]; + strcpy_P(valueBuffer, (method == NONE)?_ssdp_response_template:_ssdp_notify_template); + + int len = snprintf_P(buffer, sizeof(buffer), + _ssdp_packet_template, + valueBuffer, + SSDP_INTERVAL, + _modelName, _modelNumber, + _uuid, + (method == NONE)?"ST":"NT", + _deviceType, + ip[0], ip[1], ip[2], ip[3], _port, _schemaURL + ); + + IPAddress remoteAddr; + uint16_t remotePort; + if(method == NONE) { + remoteAddr = _respondToAddr; + remotePort = _respondToPort; +#ifdef DEBUG_SSDP + DEBUG_SSDP.print("Sending Response to "); +#endif + } else { + remoteAddr = IPAddress(SSDP_MULTICAST_ADDR); + remotePort = SSDP_PORT; +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Sending Notify to "); +#endif + } +#ifdef DEBUG_SSDP + DEBUG_SSDP.print(remoteAddr); + DEBUG_SSDP.print(":"); + DEBUG_SSDP.println(remotePort); +#endif + _server->beginPacket(remoteAddr, remotePort); + _server->println(buffer); + _server->endPacket(); +} + +void SSDPClass::schema(WiFiClient client){ + IPAddress ip = WiFi.localIP(); + char buffer[strlen_P(_ssdp_schema_template)+1]; + strcpy_P(buffer, _ssdp_schema_template); + client.printf(buffer, + ip[0], ip[1], ip[2], ip[3], _port, + _deviceType, + _friendlyName, + _presentationURL, + _serialNumber, + _modelName, + _modelNumber, + _modelURL, + _manufacturer, + _manufacturerURL, + _uuid + ); +} + +void SSDPClass::_update(){ + int nbBytes =0; + char * packetBuffer = NULL; + + if(!_pending && _server) { + ssdp_method_t method = NONE; + nbBytes= _server->parsePacket(); + typedef enum {METHOD, URI, PROTO, KEY, VALUE, ABORT} states; + states state = METHOD; + typedef enum {START, MAN, ST, MX} headers; + headers header = START; + + uint8_t cursor = 0; + uint8_t cr = 0; + + char buffer[SSDP_BUFFER_SIZE] = {0}; + packetBuffer = new char[nbBytes +1]; + int message_size=_server->read(packetBuffer,nbBytes); + int process_pos = 0; + packetBuffer[message_size]='\0'; + _respondToAddr = _server->remoteIP(); + _respondToPort = _server->remotePort(); +#ifdef DEBUG_SSDP + if (message_size) { + DEBUG_SSDP.println("****************************************************"); + DEBUG_SSDP.println(_server->remoteIP()); + DEBUG_SSDP.println(packetBuffer); + DEBUG_SSDP.println("****************************************************"); + } +#endif + while(process_pos < message_size){ + + char c = packetBuffer[process_pos]; + process_pos++; + (c == '\r' || c == '\n') ? cr++ : cr = 0; +#ifdef DEBUG_SSDP + if ((c == '\r' || c == '\n') && (cr < 2)) DEBUG_SSDP.println(buffer); +#endif + switch(state){ + case METHOD: + if(c == ' '){ + if(strcmp(buffer, "M-SEARCH") == 0) method = SEARCH; + + if(method == NONE) state = ABORT; + else state = URI; + cursor = 0; + + } else if(cursor < SSDP_METHOD_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; } + break; + case URI: + if(c == ' '){ + if(strcmp(buffer, "*")) state = ABORT; + else state = PROTO; + cursor = 0; + } else if(cursor < SSDP_URI_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; } + break; + case PROTO: + if(cr == 2){ state = KEY; cursor = 0; } + break; + case KEY: + if(cr == 4){ _pending = true; _process_time = millis(); } + else if(c == ' '){ cursor = 0; state = VALUE; } + else if(c != '\r' && c != '\n' && c != ':' && cursor < SSDP_BUFFER_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; } + break; + case VALUE: + if(cr == 2){ + switch(header){ + case START: + break; + case MAN: +#ifdef DEBUG_SSDP + DEBUG_SSDP.printf("MAN: %s\n", (char *)buffer); +#endif + break; + case ST: + if(strcmp(buffer, "ssdp:all")){ + state = ABORT; +#ifdef DEBUG_SSDP + DEBUG_SSDP.printf("REJECT: %s\n", (char *)buffer); +#endif + } + // if the search type matches our type, we should respond instead of ABORT + if(strcasecmp(buffer, _deviceType) == 0){ + _pending = true; + _process_time = 0; +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("the search type matches our type"); +#endif + state = KEY; + } + break; + case MX: + _delay = random(0, atoi(buffer)) * 1000L; + break; + } + + if(state != ABORT){ state = KEY; header = START; cursor = 0; } + } else if(c != '\r' && c != '\n'){ + if(header == START){ + if(strncmp(buffer, "MA", 2) == 0) header = MAN; + else if(strcmp(buffer, "ST") == 0) header = ST; + else if(strcmp(buffer, "MX") == 0) header = MX; + } + + if(cursor < SSDP_BUFFER_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; } + } + break; + case ABORT: + _pending = false; _delay = 0; + break; + } + } + } + if(packetBuffer) delete packetBuffer; + if(_pending && (millis() - _process_time) > _delay){ + _pending = false; _delay = 0; +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Send None"); +#endif + _send(NONE); + } else if(_notify_time == 0 || (millis() - _notify_time) > (SSDP_INTERVAL * 1000L)){ + _notify_time = millis(); + #ifdef DEBUG_SSDP + DEBUG_SSDP.println("Send Notify"); +#endif + _send(NOTIFY); + } else { +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Do not sent"); +#endif + } + + if (_pending) { + _server->flush(); + } + +} + +void SSDPClass::setSchemaURL(const char *url){ + strlcpy(_schemaURL, url, sizeof(_schemaURL)); +} + +void SSDPClass::setHTTPPort(uint16_t port){ + _port = port; +} + +void SSDPClass::setDeviceType(const char *deviceType){ + strlcpy(_deviceType, deviceType, sizeof(_deviceType)); +} + +void SSDPClass::setName(const char *name){ + strlcpy(_friendlyName, name, sizeof(_friendlyName)); +} + +void SSDPClass::setURL(const char *url){ + strlcpy(_presentationURL, url, sizeof(_presentationURL)); +} + +void SSDPClass::setSerialNumber(const char *serialNumber){ + strlcpy(_serialNumber, serialNumber, sizeof(_serialNumber)); +} + +void SSDPClass::setSerialNumber(const uint32_t serialNumber){ + snprintf(_serialNumber, sizeof(uint32_t)*2+1, "%08X", serialNumber); +} + +void SSDPClass::setModelName(const char *name){ + strlcpy(_modelName, name, sizeof(_modelName)); +} + +void SSDPClass::setModelNumber(const char *num){ + strlcpy(_modelNumber, num, sizeof(_modelNumber)); +} + +void SSDPClass::setModelURL(const char *url){ + strlcpy(_modelURL, url, sizeof(_modelURL)); +} + +void SSDPClass::setManufacturer(const char *name){ + strlcpy(_manufacturer, name, sizeof(_manufacturer)); +} + +void SSDPClass::setManufacturerURL(const char *url){ + strlcpy(_manufacturerURL, url, sizeof(_manufacturerURL)); +} + +void SSDPClass::setTTL(const uint8_t ttl){ + _ttl = ttl; +} + +void SSDPClass::_onTimerStatic(SSDPClass* self) { +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Update"); +#endif + self->_update(); +} + +void SSDPClass::_startTimer() { + ETSTimer* tm = &(_timer->timer); + const int interval = 1000; + ets_timer_disarm(tm); + ets_timer_setfn(tm, reinterpret_cast(&SSDPClass::_onTimerStatic), reinterpret_cast(this)); + ets_timer_arm(tm, interval, 1 /* repeat */); +} + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP) +SSDPClass SSDP; +#endif diff --git a/libraries/ESP32SSDP/ESP32SSDP.h b/libraries/ESP32SSDP/ESP32SSDP.h new file mode 100644 index 00000000..e9017baa --- /dev/null +++ b/libraries/ESP32SSDP/ESP32SSDP.h @@ -0,0 +1,126 @@ +/* +ESP32 Simple Service Discovery +Copyright (c) 2015 Hristo Gochkov + +Original (Arduino) version by Filippo Sallemi, July 23, 2014. +Can be found at: https://github.com/nomadnt/uSSDP + +License (MIT license): + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#ifndef ESP32SSDP_H +#define ESP32SSDP_H + +#include +#include +#include + +#define SSDP_UUID_SIZE 37 +#define SSDP_SCHEMA_URL_SIZE 64 +#define SSDP_DEVICE_TYPE_SIZE 64 +#define SSDP_FRIENDLY_NAME_SIZE 64 +#define SSDP_SERIAL_NUMBER_SIZE 32 +#define SSDP_PRESENTATION_URL_SIZE 128 +#define SSDP_MODEL_NAME_SIZE 64 +#define SSDP_MODEL_URL_SIZE 128 +#define SSDP_MODEL_VERSION_SIZE 32 +#define SSDP_MANUFACTURER_SIZE 64 +#define SSDP_MANUFACTURER_URL_SIZE 128 + +typedef enum { + NONE, + SEARCH, + NOTIFY +} ssdp_method_t; + + +struct SSDPTimer; + +class SSDPClass{ + public: + SSDPClass(); + ~SSDPClass(); + + bool begin(); + + void schema(WiFiClient client); + + void setDeviceType(const String& deviceType) { setDeviceType(deviceType.c_str()); } + void setDeviceType(const char *deviceType); + void setName(const String& name) { setName(name.c_str()); } + void setName(const char *name); + void setURL(const String& url) { setURL(url.c_str()); } + void setURL(const char *url); + void setSchemaURL(const String& url) { setSchemaURL(url.c_str()); } + void setSchemaURL(const char *url); + void setSerialNumber(const String& serialNumber) { setSerialNumber(serialNumber.c_str()); } + void setSerialNumber(const char *serialNumber); + void setSerialNumber(const uint32_t serialNumber); + void setModelName(const String& name) { setModelName(name.c_str()); } + void setModelName(const char *name); + void setModelNumber(const String& num) { setModelNumber(num.c_str()); } + void setModelNumber(const char *num); + void setModelURL(const String& url) { setModelURL(url.c_str()); } + void setModelURL(const char *url); + void setManufacturer(const String& name) { setManufacturer(name.c_str()); } + void setManufacturer(const char *name); + void setManufacturerURL(const String& url) { setManufacturerURL(url.c_str()); } + void setManufacturerURL(const char *url); + void setHTTPPort(uint16_t port); + void setTTL(uint8_t ttl); + + protected: + void _send(ssdp_method_t method); + void _update(); + void _startTimer(); + static void _onTimerStatic(SSDPClass* self); + + WiFiUDP *_server; + SSDPTimer* _timer; + uint16_t _port; + uint8_t _ttl; + + IPAddress _respondToAddr; + uint16_t _respondToPort; + + bool _pending; + unsigned short _delay; + unsigned long _process_time; + unsigned long _notify_time; + + char _schemaURL[SSDP_SCHEMA_URL_SIZE]; + char _uuid[SSDP_UUID_SIZE]; + char _deviceType[SSDP_DEVICE_TYPE_SIZE]; + char _friendlyName[SSDP_FRIENDLY_NAME_SIZE]; + char _serialNumber[SSDP_SERIAL_NUMBER_SIZE]; + char _presentationURL[SSDP_PRESENTATION_URL_SIZE]; + char _manufacturer[SSDP_MANUFACTURER_SIZE]; + char _manufacturerURL[SSDP_MANUFACTURER_URL_SIZE]; + char _modelName[SSDP_MODEL_NAME_SIZE]; + char _modelURL[SSDP_MODEL_URL_SIZE]; + char _modelNumber[SSDP_MODEL_VERSION_SIZE]; +}; + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP) +extern SSDPClass SSDP; +#endif + +#endif diff --git a/libraries/ESP32SSDP/README.rst b/libraries/ESP32SSDP/README.rst new file mode 100644 index 00000000..b2c92c75 --- /dev/null +++ b/libraries/ESP32SSDP/README.rst @@ -0,0 +1,22 @@ +ESP32 Simple Service Discovery Copyright (c) 2015 Hristo Gochkov +Original (Arduino) version by Filippo Sallemi, July 23, 2014. Can be +found at: https://github.com/nomadnt/uSSDP + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/libraries/ESP32SSDP/examples/SSDP/SSDP.ino b/libraries/ESP32SSDP/examples/SSDP/SSDP.ino new file mode 100644 index 00000000..8014a3a5 --- /dev/null +++ b/libraries/ESP32SSDP/examples/SSDP/SSDP.ino @@ -0,0 +1,51 @@ +#include +#include +#include + +const char* ssid = "********"; +const char* password = "********"; + +WebServer HTTP(80); + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println("Starting WiFi..."); + + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if(WiFi.waitForConnectResult() == WL_CONNECTED){ + + Serial.printf("Starting HTTP...\n"); + HTTP.on("/index.html", HTTP_GET, [](){ + HTTP.send(200, "text/plain", "Hello World!"); + }); + HTTP.on("/description.xml", HTTP_GET, [](){ + SSDP.schema(HTTP.client()); + }); + HTTP.begin(); + + Serial.printf("Starting SSDP...\n"); + SSDP.setSchemaURL("description.xml"); + SSDP.setHTTPPort(80); + SSDP.setName("Philips hue clone"); + SSDP.setSerialNumber("001788102201"); + SSDP.setURL("index.html"); + SSDP.setModelName("Philips hue bridge 2012"); + SSDP.setModelNumber("929000226503"); + SSDP.setModelURL("http://www.meethue.com"); + SSDP.setManufacturer("Royal Philips Electronics"); + SSDP.setManufacturerURL("http://www.philips.com"); + SSDP.begin(); + + Serial.printf("Ready!\n"); + } else { + Serial.printf("WiFi Failed\n"); + while(1) delay(100); + } +} + +void loop() { + HTTP.handleClient(); + delay(1); +} diff --git a/libraries/ESP32SSDP/keywords.txt b/libraries/ESP32SSDP/keywords.txt new file mode 100644 index 00000000..241d3414 --- /dev/null +++ b/libraries/ESP32SSDP/keywords.txt @@ -0,0 +1,53 @@ +####################################### +# Syntax Coloring Map For Ultrasound +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +ESP8266SSDP KEYWORD1 +SSDP KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +schema KEYWORD2 +setName KEYWORD2 +setURL KEYWORD2 +setHTTPPort KEYWORD2 +setSchemaURL KEYWORD2 +setSerialNumber KEYWORD2 +setModelName KEYWORD2 +setModelNumber KEYWORD2 +setModelURL KEYWORD2 +setManufacturer KEYWORD2 +setManufacturerURL KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### +SSDP_INTERVAL LITERAL1 +SSDP_PORT LITERAL1 +SSDP_METHOD_SIZE LITERAL1 +SSDP_URI_SIZE LITERAL1 +SSDP_BUFFER_SIZE LITERAL1 +SSDP_BASE_SIZE LITERAL1 +SSDP_FRIENDLY_NAME_SIZE LITERAL1 +SSDP_SERIAL_NUMBER_SIZE LITERAL1 +SSDP_PRESENTATION_URL_SIZE LITERAL1 +SSDP_MODEL_NAME_SIZE LITERAL1 +SSDP_MODEL_URL_SIZE LITERAL1 +SSDP_MODEL_VERSION_SIZE LITERAL1 +SSDP_MANUFACTURER_SIZE LITERAL1 +SSDP_MANUFACTURER_URL_SIZE LITERAL1 +SEARCH LITERAL1 +NOTIFY LITERAL1 +BASIC LITERAL1 +MANAGEABLE LITERAL1 +SOLARPROTECTIONBLIND LITERAL1 +DIGITALSECURITYCAMERA LITERAL1 +HVAC LITERAL1 +LIGHTINGCONTROL LITERAL1 diff --git a/libraries/ESP32SSDP/library.properties b/libraries/ESP32SSDP/library.properties new file mode 100644 index 00000000..e37cd254 --- /dev/null +++ b/libraries/ESP32SSDP/library.properties @@ -0,0 +1,9 @@ +name=ESP32SSPD +version=1.0 +author=Me-No-Dev +maintainer=Me-No-Dev +sentence=Simple SSDP library for ESP32 +paragraph=Only for ESP32 +category=Communication +url= +architectures=esp32 diff --git a/libraries/ESPAsyncTCP/.gitignore b/libraries/ESPAsyncTCP/.gitignore new file mode 100644 index 00000000..9bea4330 --- /dev/null +++ b/libraries/ESPAsyncTCP/.gitignore @@ -0,0 +1,2 @@ + +.DS_Store diff --git a/libraries/ESPAsyncTCP/.travis.yml b/libraries/ESPAsyncTCP/.travis.yml new file mode 100644 index 00000000..89808806 --- /dev/null +++ b/libraries/ESPAsyncTCP/.travis.yml @@ -0,0 +1,37 @@ +sudo: false +language: bash +os: + - linux + +script: + - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16 + - sleep 3 + - export DISPLAY=:1.0 + - wget http://downloads.arduino.cc/arduino-1.6.5-linux64.tar.xz + - tar xf arduino-1.6.5-linux64.tar.xz + - mv arduino-1.6.5 $HOME/arduino_ide + - export PATH="$HOME/arduino_ide:$PATH" + - which arduino + - mkdir -p $HOME/Arduino/libraries + - cp -r $TRAVIS_BUILD_DIR $HOME/Arduino/libraries/ESPAsyncTCP + - cd $HOME/arduino_ide/hardware + - mkdir esp8266com + - cd esp8266com + - git clone https://github.com/esp8266/Arduino.git esp8266 + - cd esp8266/tools + - python get.py + - source $TRAVIS_BUILD_DIR/travis/common.sh + - arduino --board esp8266com:esp8266:generic --save-prefs + - arduino --get-pref sketchbook.path + - build_sketches arduino $HOME/Arduino/libraries/ESPAsyncTCP esp8266 + +notifications: + email: + on_success: change + on_failure: change + webhooks: + urls: + - https://webhooks.gitter.im/e/60e65d0c78ea0a920347 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: false # default: false diff --git a/libraries/ESPAsyncTCP/LICENSE.txt b/libraries/ESPAsyncTCP/LICENSE.txt new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/libraries/ESPAsyncTCP/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + 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 that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU 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 as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/libraries/ESPAsyncTCP/README.md b/libraries/ESPAsyncTCP/README.md new file mode 100644 index 00000000..2c48453c --- /dev/null +++ b/libraries/ESPAsyncTCP/README.md @@ -0,0 +1,30 @@ +# ESPAsyncTCP [![Build Status](https://travis-ci.org/me-no-dev/ESPAsyncTCP.svg?branch=master)](https://travis-ci.org/me-no-dev/ESPAsyncTCP) +Async TCP Library for ESP8266 Arduino + +For ESP32 look [HERE](https://github.com/me-no-dev/AsyncTCP) + +[![Join the chat at https://gitter.im/me-no-dev/ESPAsyncWebServer](https://badges.gitter.im/me-no-dev/ESPAsyncWebServer.svg)](https://gitter.im/me-no-dev/ESPAsyncWebServer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +This is a fully asynchronous TCP library, aimed at enabling trouble-free, multi-connection network environment for Espressif's ESP8266 MCUs. + +This library is the base for [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) + +## AsyncClient and AsyncServer +The base classes on which everything else is built. They expose all possible scenarios, but are really raw and require more skills to use. + +## AsyncPrinter +This class can be used to send data like any other ```Print``` interface (```Serial``` for example). +The object then can be used outside of the Async callbacks (the loop) and receive asynchronously data using ```onData```. The object can be checked if the underlying ```AsyncClient```is connected, or hook to the ```onDisconnect``` callback. + +## AsyncTCPbuffer +This class is really similar to the ```AsyncPrinter```, but it differs in the fact that it can buffer some of the incoming data. + +## SyncClient +It is exactly what it sounds like. This is a standard, blocking TCP Client, similar to the one included in ```ESP8266WiFi``` + +## Libraries and projects that use AsyncTCP +- [ESP Async Web Server](https://github.com/me-no-dev/ESPAsyncWebServer) +- [Async MQTT client](https://github.com/marvinroger/async-mqtt-client) +- [arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets) +- [ESP8266 Smart Home](https://github.com/baruch/esp8266_smart_home) +- [KBox Firmware](https://github.com/sarfata/kbox-firmware) diff --git a/libraries/ESPAsyncTCP/examples/SyncClient/.esp31b.skip b/libraries/ESPAsyncTCP/examples/SyncClient/.esp31b.skip new file mode 100644 index 00000000..e69de29b diff --git a/libraries/ESPAsyncTCP/examples/SyncClient/SyncClient.ino b/libraries/ESPAsyncTCP/examples/SyncClient/SyncClient.ino new file mode 100644 index 00000000..6ecc5255 --- /dev/null +++ b/libraries/ESPAsyncTCP/examples/SyncClient/SyncClient.ino @@ -0,0 +1,54 @@ +#ifdef ESP8266 +#include +#include +#include +#else +#include +#endif +#include "ESPAsyncTCP.h" +#include "SyncClient.h" + +const char* ssid = "**********"; +const char* password = "************"; + +void setup(){ + Serial.begin(115200); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.printf("WiFi Failed!\n"); + return; + } + Serial.printf("WiFi Connected!\n"); + Serial.println(WiFi.localIP()); +#ifdef ESP8266 + ArduinoOTA.begin(); +#endif + + SyncClient client; + if(!client.connect("www.google.com", 80)){ + Serial.println("Connect Failed"); + return; + } + client.setTimeout(2); + if(client.printf("GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n") > 0){ + while(client.connected() && client.available() == 0){ + delay(1); + } + while(client.available()){ + Serial.write(client.read()); + } + if(client.connected()){ + client.stop(); + } + } else { + client.stop(); + Serial.println("Send Failed"); + while(client.connected()) delay(0); + } +} + +void loop(){ +#ifdef ESP8266 + ArduinoOTA.handle(); +#endif +} diff --git a/libraries/ESPAsyncTCP/library.json b/libraries/ESPAsyncTCP/library.json new file mode 100644 index 00000000..71d9da77 --- /dev/null +++ b/libraries/ESPAsyncTCP/library.json @@ -0,0 +1,19 @@ +{ + "name":"ESPAsyncTCP", + "description":"Asynchronous TCP Library for ESP8266", + "keywords":"async,tcp", + "authors": + { + "name": "Hristo Gochkov", + "maintainer": true + }, + "repository": + { + "type": "git", + "url": "https://github.com/me-no-dev/ESPAsyncTCP.git" + }, + "version": "1.1.1", + "license": "LGPL-3.0", + "frameworks": "arduino", + "platforms":"espressif8266" +} diff --git a/libraries/ESPAsyncTCP/library.properties b/libraries/ESPAsyncTCP/library.properties new file mode 100644 index 00000000..9330e4d6 --- /dev/null +++ b/libraries/ESPAsyncTCP/library.properties @@ -0,0 +1,9 @@ +name=ESP AsyncTCP +version=1.1.0 +author=Me-No-Dev +maintainer=Me-No-Dev +sentence=Async TCP Library for ESP8266 and ESP31B +paragraph=Async TCP Library for ESP8266 and ESP31B +category=Other +url=https://github.com/me-no-dev/ESPAsyncTCP +architectures=* diff --git a/libraries/ESPAsyncTCP/src/AsyncPrinter.cpp b/libraries/ESPAsyncTCP/src/AsyncPrinter.cpp new file mode 100644 index 00000000..dd61e764 --- /dev/null +++ b/libraries/ESPAsyncTCP/src/AsyncPrinter.cpp @@ -0,0 +1,187 @@ +/* + Asynchronous TCP library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ + +#include "AsyncPrinter.h" + +AsyncPrinter::AsyncPrinter() + : _client(NULL) + , _data_cb(NULL) + , _data_arg(NULL) + , _close_cb(NULL) + , _close_arg(NULL) + , _tx_buffer(NULL) + , _tx_buffer_size(1460) + , next(NULL) +{} + +AsyncPrinter::AsyncPrinter(AsyncClient *client, size_t txBufLen) + : _client(client) + , _data_cb(NULL) + , _data_arg(NULL) + , _close_cb(NULL) + , _close_arg(NULL) + , _tx_buffer(NULL) + , _tx_buffer_size(txBufLen) + , next(NULL) +{ + _attachCallbacks(); + _tx_buffer = new cbuf(_tx_buffer_size); +} + +AsyncPrinter::~AsyncPrinter(){ + _on_close(); +} + +void AsyncPrinter::onData(ApDataHandler cb, void *arg){ + _data_cb = cb; + _data_arg = arg; +} + +void AsyncPrinter::onClose(ApCloseHandler cb, void *arg){ + _close_cb = cb; + _close_arg = arg; +} + +int AsyncPrinter::connect(IPAddress ip, uint16_t port){ + if(_client != NULL && connected()) + return 0; + _client = new AsyncClient(); + _client->onConnect([](void *obj, AsyncClient *c){ ((AsyncPrinter*)(obj))->_onConnect(c); }, this); + if(_client->connect(ip, port)){ + while(_client->state() < 4) + delay(1); + return connected(); + } + return 0; +} + +int AsyncPrinter::connect(const char *host, uint16_t port){ + if(_client != NULL && connected()) + return 0; + _client = new AsyncClient(); + _client->onConnect([](void *obj, AsyncClient *c){ ((AsyncPrinter*)(obj))->_onConnect(c); }, this); + if(_client->connect(host, port)){ + while(_client->state() < 4) + delay(1); + return connected(); + } + return 0; +} + +void AsyncPrinter::_onConnect(AsyncClient *c){ + if(_tx_buffer != NULL){ + cbuf *b = _tx_buffer; + _tx_buffer = NULL; + delete b; + } + _tx_buffer = new cbuf(_tx_buffer_size); + _attachCallbacks(); +} + +AsyncPrinter::operator bool(){ return connected(); } + +AsyncPrinter & AsyncPrinter::operator=(const AsyncPrinter &other){ + if(_client != NULL){ + _client->close(true); + _client = NULL; + } + _tx_buffer_size = other._tx_buffer_size; + if(_tx_buffer != NULL){ + cbuf *b = _tx_buffer; + _tx_buffer = NULL; + delete b; + } + _tx_buffer = new cbuf(other._tx_buffer_size); + _client = other._client; + _attachCallbacks(); + return *this; +} + +size_t AsyncPrinter::write(uint8_t data){ + return write(&data, 1); +} + +size_t AsyncPrinter::write(const uint8_t *data, size_t len){ + if(_tx_buffer == NULL || !connected()) + return 0; + size_t toWrite = 0; + size_t toSend = len; + while(_tx_buffer->room() < toSend){ + toWrite = _tx_buffer->room(); + _tx_buffer->write((const char*)data, toWrite); + while(!_client->canSend()) + delay(0); + _sendBuffer(); + toSend -= toWrite; + } + _tx_buffer->write((const char*)(data+(len - toSend)), toSend); + while(!_client->canSend()) delay(0); + _sendBuffer(); + return len; +} + +bool AsyncPrinter::connected(){ + return (_client != NULL && _client->connected()); +} + +void AsyncPrinter::close(){ + if(_client != NULL) + _client->close(true); +} + +size_t AsyncPrinter::_sendBuffer(){ + size_t available = _tx_buffer->available(); + if(!connected() || !_client->canSend() || available == 0) + return 0; + size_t sendable = _client->space(); + if(sendable < available) + available= sendable; + char *out = new char[available]; + _tx_buffer->read(out, available); + size_t sent = _client->write(out, available); + delete out; + return sent; +} + +void AsyncPrinter::_onData(void *data, size_t len){ + if(_data_cb) + _data_cb(_data_arg, this, (uint8_t*)data, len); +} + +void AsyncPrinter::_on_close(){ + if(_client != NULL){ + _client = NULL; + } + if(_tx_buffer != NULL){ + cbuf *b = _tx_buffer; + _tx_buffer = NULL; + delete b; + } + if(_close_cb) + _close_cb(_close_arg, this); +} + +void AsyncPrinter::_attachCallbacks(){ + _client->onPoll([](void *obj, AsyncClient* c){ ((AsyncPrinter*)(obj))->_sendBuffer(); }, this); + _client->onAck([](void *obj, AsyncClient* c, size_t len, uint32_t time){ ((AsyncPrinter*)(obj))->_sendBuffer(); }, this); + _client->onDisconnect([](void *obj, AsyncClient* c){ ((AsyncPrinter*)(obj))->_on_close(); delete c; }, this); + _client->onData([](void *obj, AsyncClient* c, void *data, size_t len){ ((AsyncPrinter*)(obj))->_onData(data, len); }, this); +} diff --git a/libraries/ESPAsyncTCP/src/AsyncPrinter.h b/libraries/ESPAsyncTCP/src/AsyncPrinter.h new file mode 100644 index 00000000..1fa0f8bd --- /dev/null +++ b/libraries/ESPAsyncTCP/src/AsyncPrinter.h @@ -0,0 +1,73 @@ +/* + Asynchronous TCP library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ + +#ifndef ASYNCPRINTER_H_ +#define ASYNCPRINTER_H_ + +#include "Arduino.h" +#include "ESPAsyncTCP.h" +#include "cbuf.h" + +class AsyncPrinter; + +typedef std::function ApDataHandler; +typedef std::function ApCloseHandler; + +class AsyncPrinter: public Print { + private: + AsyncClient *_client; + ApDataHandler _data_cb; + void *_data_arg; + ApCloseHandler _close_cb; + void *_close_arg; + cbuf *_tx_buffer; + size_t _tx_buffer_size; + + void _onConnect(AsyncClient *c); + public: + AsyncPrinter *next; + + AsyncPrinter(); + AsyncPrinter(AsyncClient *client, size_t txBufLen = 1460); + virtual ~AsyncPrinter(); + + int connect(IPAddress ip, uint16_t port); + int connect(const char *host, uint16_t port); + + void onData(ApDataHandler cb, void *arg); + void onClose(ApCloseHandler cb, void *arg); + + operator bool(); + AsyncPrinter & operator=(const AsyncPrinter &other); + + size_t write(uint8_t data); + size_t write(const uint8_t *data, size_t len); + + bool connected(); + void close(); + + size_t _sendBuffer(); + void _onData(void *data, size_t len); + void _on_close(); + void _attachCallbacks(); +}; + +#endif /* ASYNCPRINTER_H_ */ diff --git a/libraries/ESPAsyncTCP/src/ESPAsyncTCP.cpp b/libraries/ESPAsyncTCP/src/ESPAsyncTCP.cpp new file mode 100644 index 00000000..593d6d6b --- /dev/null +++ b/libraries/ESPAsyncTCP/src/ESPAsyncTCP.cpp @@ -0,0 +1,1066 @@ +/* + Asynchronous TCP library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ + +#include "Arduino.h" + +#include "ESPAsyncTCP.h" +extern "C"{ + #include "lwip/opt.h" + #include "lwip/tcp.h" + #include "lwip/inet.h" + #include "lwip/dns.h" + #include "lwip/init.h" +} +#include + +/* + Async TCP Client +*/ + +#if ASYNC_TCP_SSL_ENABLED +AsyncClient::AsyncClient(tcp_pcb* pcb, SSL_CTX * ssl_ctx): +#else +AsyncClient::AsyncClient(tcp_pcb* pcb): +#endif + _connect_cb(0) + , _connect_cb_arg(0) + , _discard_cb(0) + , _discard_cb_arg(0) + , _sent_cb(0) + , _sent_cb_arg(0) + , _error_cb(0) + , _error_cb_arg(0) + , _recv_cb(0) + , _recv_cb_arg(0) + , _timeout_cb(0) + , _timeout_cb_arg(0) + , _pcb_busy(false) +#if ASYNC_TCP_SSL_ENABLED + , _pcb_secure(false) + , _handshake_done(true) +#endif + , _pcb_sent_at(0) + , _close_pcb(false) + , _ack_pcb(true) + , _tx_unacked_len(0) + , _tx_acked_len(0) + , _tx_unsent_len(0) + , _rx_last_packet(0) + , _rx_since_timeout(0) + , _ack_timeout(ASYNC_MAX_ACK_TIME) + , _connect_port(0) + , prev(NULL) + , next(NULL) +{ + _pcb = pcb; + if(_pcb){ + _rx_last_packet = millis(); + tcp_setprio(_pcb, TCP_PRIO_MIN); + tcp_arg(_pcb, this); + tcp_recv(_pcb, &_s_recv); + tcp_sent(_pcb, &_s_sent); + tcp_err(_pcb, &_s_error); + tcp_poll(_pcb, &_s_poll, 1); +#if ASYNC_TCP_SSL_ENABLED + if(ssl_ctx){ + if(tcp_ssl_new_server(_pcb, ssl_ctx) < 0){ + _close(); + return; + } + tcp_ssl_arg(_pcb, this); + tcp_ssl_data(_pcb, &_s_data); + tcp_ssl_handshake(_pcb, &_s_handshake); + tcp_ssl_err(_pcb, &_s_ssl_error); + + _pcb_secure = true; + _handshake_done = false; + } +#endif + } +} + +AsyncClient::~AsyncClient(){ + if(_pcb) + _close(); +} + +#if ASYNC_TCP_SSL_ENABLED +bool AsyncClient::connect(IPAddress ip, uint16_t port, bool secure){ +#else +bool AsyncClient::connect(IPAddress ip, uint16_t port){ +#endif + if (_pcb) //already connected + return false; + ip_addr_t addr; + addr.addr = ip; +#if LWIP_VERSION_MAJOR == 1 + netif* interface = ip_route(&addr); + if (!interface){ //no route to host + return false; + } +#endif + tcp_pcb* pcb = tcp_new(); + if (!pcb){ //could not allocate pcb + return false; + } + +#if ASYNC_TCP_SSL_ENABLED + _pcb_secure = secure; + _handshake_done = !secure; +#endif + tcp_arg(pcb, this); + tcp_err(pcb, &_s_error); + tcp_connect(pcb, &addr, port,(tcp_connected_fn)&_s_connected); + return true; +} + +#if ASYNC_TCP_SSL_ENABLED +bool AsyncClient::connect(const char* host, uint16_t port, bool secure){ +#else +bool AsyncClient::connect(const char* host, uint16_t port){ +#endif + ip_addr_t addr; + err_t err = dns_gethostbyname(host, &addr, (dns_found_callback)&_s_dns_found, this); + if(err == ERR_OK) { +#if ASYNC_TCP_SSL_ENABLED + return connect(IPAddress(addr.addr), port, secure); +#else + return connect(IPAddress(addr.addr), port); +#endif + } else if(err == ERR_INPROGRESS) { +#if ASYNC_TCP_SSL_ENABLED + _pcb_secure = secure; + _handshake_done = !secure; +#endif + _connect_port = port; + return true; + } + return false; +} + +AsyncClient& AsyncClient::operator=(const AsyncClient& other){ + if (_pcb) + _close(); + + _pcb = other._pcb; + if (_pcb) { + _rx_last_packet = millis(); + tcp_setprio(_pcb, TCP_PRIO_MIN); + tcp_arg(_pcb, this); + tcp_recv(_pcb, &_s_recv); + tcp_sent(_pcb, &_s_sent); + tcp_err(_pcb, &_s_error); + tcp_poll(_pcb, &_s_poll, 1); +#if ASYNC_TCP_SSL_ENABLED + if(tcp_ssl_has(_pcb)){ + _pcb_secure = true; + _handshake_done = false; + tcp_ssl_arg(_pcb, this); + tcp_ssl_data(_pcb, &_s_data); + tcp_ssl_handshake(_pcb, &_s_handshake); + tcp_ssl_err(_pcb, &_s_ssl_error); + } else { + _pcb_secure = false; + _handshake_done = true; + } +#endif + } + return *this; +} + +bool AsyncClient::operator==(const AsyncClient &other) { + return (_pcb != NULL && other._pcb != NULL && (_pcb->remote_ip.addr == other._pcb->remote_ip.addr) && (_pcb->remote_port == other._pcb->remote_port)); +} + +int8_t AsyncClient::abort(){ + if(_pcb) { + tcp_abort(_pcb); + _pcb = NULL; + } + return ERR_ABRT; +} + +void AsyncClient::close(bool now){ + tcp_recved(_pcb, _rx_ack_len); + if(now) + _close(); + else + _close_pcb = true; +} + +void AsyncClient::stop() { + close(false); +} + +bool AsyncClient::free(){ + if(!_pcb) + return true; + if(_pcb->state == 0 || _pcb->state > 4) + return true; + return false; +} + +size_t AsyncClient::write(const char* data) { + if(data == NULL) + return 0; + return write(data, strlen(data)); +} + +size_t AsyncClient::write(const char* data, size_t size, uint8_t apiflags) { + size_t will_send = add(data, size, apiflags); + if(!will_send || !send()) + return 0; + return will_send; +} + +size_t AsyncClient::add(const char* data, size_t size, uint8_t apiflags) { + if(!_pcb || size == 0 || data == NULL) + return 0; + size_t room = space(); + if(!room) + return 0; +#if ASYNC_TCP_SSL_ENABLED + if(_pcb_secure){ + int sent = tcp_ssl_write(_pcb, (uint8_t*)data, size); + if(sent >= 0){ + _tx_unacked_len += sent; + return sent; + } + _close(); + return 0; + } +#endif + size_t will_send = (room < size) ? room : size; + int8_t err = tcp_write(_pcb, data, will_send, apiflags); + if(err != ERR_OK) + return 0; + _tx_unsent_len += will_send; + return will_send; +} + +bool AsyncClient::send(){ +#if ASYNC_TCP_SSL_ENABLED + if(_pcb_secure) + return true; +#endif + if(tcp_output(_pcb) == ERR_OK){ + _pcb_busy = true; + _pcb_sent_at = millis(); + _tx_unacked_len += _tx_unsent_len; + _tx_unsent_len = 0; + return true; + } + _tx_unsent_len = 0; + return false; +} + +size_t AsyncClient::ack(size_t len){ + if(len > _rx_ack_len) + len = _rx_ack_len; + if(len) + tcp_recved(_pcb, len); + _rx_ack_len -= len; + return len; +} + +// Private Callbacks + +long AsyncClient::_connected(void* pcb, long err){ + _pcb = reinterpret_cast(pcb); + if(_pcb){ + _pcb_busy = false; + _rx_last_packet = millis(); + tcp_setprio(_pcb, TCP_PRIO_MIN); + tcp_recv(_pcb, &_s_recv); + tcp_sent(_pcb, &_s_sent); + tcp_poll(_pcb, &_s_poll, 1); +#if ASYNC_TCP_SSL_ENABLED + if(_pcb_secure){ + if(tcp_ssl_new_client(_pcb) < 0){ + return _close(); + } + tcp_ssl_arg(_pcb, this); + tcp_ssl_data(_pcb, &_s_data); + tcp_ssl_handshake(_pcb, &_s_handshake); + tcp_ssl_err(_pcb, &_s_ssl_error); + } + } + if(!_pcb_secure && _connect_cb) +#else + } + if(_connect_cb) +#endif + _connect_cb(_connect_cb_arg, this); + return ERR_OK; +} + +int8_t AsyncClient::_close(){ + int8_t err = ERR_OK; + if(_pcb) { +#if ASYNC_TCP_SSL_ENABLED + if(_pcb_secure){ + tcp_ssl_free(_pcb); + } +#endif + tcp_arg(_pcb, NULL); + tcp_sent(_pcb, NULL); + tcp_recv(_pcb, NULL); + tcp_err(_pcb, NULL); + tcp_poll(_pcb, NULL, 0); + err = tcp_close(_pcb); + if(err != ERR_OK) { + err = abort(); + } + _pcb = NULL; + if(_discard_cb) + _discard_cb(_discard_cb_arg, this); + } + return err; +} + +void AsyncClient::_error(long err) { + if(_pcb){ +#if ASYNC_TCP_SSL_ENABLED + if(_pcb_secure){ + tcp_ssl_free(_pcb); + } +#endif + tcp_arg(_pcb, NULL); + tcp_sent(_pcb, NULL); + tcp_recv(_pcb, NULL); + tcp_err(_pcb, NULL); + tcp_poll(_pcb, NULL, 0); + _pcb = NULL; + } + if(_error_cb) + _error_cb(_error_cb_arg, this, err); + if(_discard_cb) + _discard_cb(_discard_cb_arg, this); +} + +#if ASYNC_TCP_SSL_ENABLED +void AsyncClient::_ssl_error(int8_t err){ + if(_error_cb) + _error_cb(_error_cb_arg, this, err+64); +} +#endif + +long AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) { + _rx_last_packet = millis(); + ASYNC_TCP_DEBUG("_sent: %u\n", len); + _tx_unacked_len -= len; + _tx_acked_len += len; + if(_tx_unacked_len == 0){ + _pcb_busy = false; + if(_sent_cb) + _sent_cb(_sent_cb_arg, this, _tx_acked_len, (millis() - _pcb_sent_at)); + _tx_acked_len = 0; + } + return ERR_OK; +} + +long AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, long err) { + if(pb == NULL){ + ASYNC_TCP_DEBUG("_recv: pb == NULL! Closing... %d\n", err); + return _close(); + } + + _rx_last_packet = millis(); +#if ASYNC_TCP_SSL_ENABLED + if(_pcb_secure){ + ASYNC_TCP_DEBUG("_recv: %d\n", pb->tot_len); + int read_bytes = tcp_ssl_read(pcb, pb); + if(read_bytes < 0){ + if (read_bytes != SSL_CLOSE_NOTIFY) { + ASYNC_TCP_DEBUG("_recv err: %d\n", read_bytes); + _close(); + } + return read_bytes; + } + return ERR_OK; + } +#endif + while(pb != NULL){ + //we should not ack before we assimilate the data + _ack_pcb = true; + pbuf *b = pb; + ASYNC_TCP_DEBUG("_recv: %d\n", b->len); + if(_recv_cb) + _recv_cb(_recv_cb_arg, this, b->payload, b->len); + if(!_ack_pcb) + _rx_ack_len += b->len; + else + tcp_recved(pcb, b->len); + pb = b->next; + b->next = NULL; + pbuf_free(b); + } + return ERR_OK; +} + +long AsyncClient::_poll(tcp_pcb* pcb){ + // Close requested + if(_close_pcb){ + _close_pcb = false; + _close(); + return ERR_OK; + } + uint32_t now = millis(); + + // ACK Timeout + if(_pcb_busy && _ack_timeout && (now - _pcb_sent_at) >= _ack_timeout){ + _pcb_busy = false; + if(_timeout_cb) + _timeout_cb(_timeout_cb_arg, this, (now - _pcb_sent_at)); + return ERR_OK; + } + // RX Timeout + if(_rx_since_timeout && (now - _rx_last_packet) >= (_rx_since_timeout * 1000)){ + _close(); + return ERR_OK; + } +#if ASYNC_TCP_SSL_ENABLED + // SSL Handshake Timeout + if(_pcb_secure && !_handshake_done && (now - _rx_last_packet) >= 2000){ + _close(); + return ERR_OK; + } +#endif + // Everything is fine + if(_poll_cb) + _poll_cb(_poll_cb_arg, this); + return ERR_OK; +} + +#if LWIP_VERSION_MAJOR == 1 +void AsyncClient::_dns_found(struct ip_addr *ipaddr){ +#else +void AsyncClient::_dns_found(const ip_addr *ipaddr){ +#endif + if(ipaddr){ +#if ASYNC_TCP_SSL_ENABLED + connect(IPAddress(ipaddr->addr), _connect_port, _pcb_secure); +#else + connect(IPAddress(ipaddr->addr), _connect_port); +#endif + } else { + if(_error_cb) + _error_cb(_error_cb_arg, this, -55); + if(_discard_cb) + _discard_cb(_discard_cb_arg, this); + } +} + +// lWIP Callbacks +#if LWIP_VERSION_MAJOR == 1 +void AsyncClient::_s_dns_found(const char *name, ip_addr_t *ipaddr, void *arg){ +#else +void AsyncClient::_s_dns_found(const char *name, const ip_addr *ipaddr, void *arg){ +#endif + reinterpret_cast(arg)->_dns_found(ipaddr); +} + +long AsyncClient::_s_poll(void *arg, struct tcp_pcb *tpcb) { + return reinterpret_cast(arg)->_poll(tpcb); +} + +long AsyncClient::_s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, long err) { + return reinterpret_cast(arg)->_recv(tpcb, pb, err); +} + +void AsyncClient::_s_error(void *arg, long err) { + reinterpret_cast(arg)->_error(err); +} + +long AsyncClient::_s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len) { + return reinterpret_cast(arg)->_sent(tpcb, len); +} + +long AsyncClient::_s_connected(void* arg, void* tpcb, long err){ + return reinterpret_cast(arg)->_connected(tpcb, err); +} + +#if ASYNC_TCP_SSL_ENABLED +void AsyncClient::_s_data(void *arg, struct tcp_pcb *tcp, uint8_t * data, size_t len){ + AsyncClient *c = reinterpret_cast(arg); + if(c->_recv_cb) + c->_recv_cb(c->_recv_cb_arg, c, data, len); +} + +void AsyncClient::_s_handshake(void *arg, struct tcp_pcb *tcp, SSL *ssl){ + AsyncClient *c = reinterpret_cast(arg); + c->_handshake_done = true; + if(c->_connect_cb) + c->_connect_cb(c->_connect_cb_arg, c); +} + +void AsyncClient::_s_ssl_error(void *arg, struct tcp_pcb *tcp, int8_t err){ + reinterpret_cast(arg)->_ssl_error(err); +} +#endif + +// Operators + +AsyncClient & AsyncClient::operator+=(const AsyncClient &other) { + if(next == NULL){ + next = (AsyncClient*)(&other); + next->prev = this; + } else { + AsyncClient *c = next; + while(c->next != NULL) c = c->next; + c->next =(AsyncClient*)(&other); + c->next->prev = c; + } + return *this; +} + +void AsyncClient::setRxTimeout(uint32_t timeout){ + _rx_since_timeout = timeout; +} + +uint32_t AsyncClient::getRxTimeout(){ + return _rx_since_timeout; +} + +uint32_t AsyncClient::getAckTimeout(){ + return _ack_timeout; +} + +void AsyncClient::setAckTimeout(uint32_t timeout){ + _ack_timeout = timeout; +} + +void AsyncClient::setNoDelay(bool nodelay){ + if(!_pcb) + return; + if(nodelay) + tcp_nagle_disable(_pcb); + else + tcp_nagle_enable(_pcb); +} + +bool AsyncClient::getNoDelay(){ + if(!_pcb) + return false; + return tcp_nagle_disabled(_pcb); +} + +uint16_t AsyncClient::getMss(){ + if(_pcb) + return tcp_mss(_pcb); + return 0; +} + +uint32_t AsyncClient::getRemoteAddress() { + if(!_pcb) + return 0; + return _pcb->remote_ip.addr; +} + +uint16_t AsyncClient::getRemotePort() { + if(!_pcb) + return 0; + return _pcb->remote_port; +} + +uint32_t AsyncClient::getLocalAddress() { + if(!_pcb) + return 0; + return _pcb->local_ip.addr; +} + +uint16_t AsyncClient::getLocalPort() { + if(!_pcb) + return 0; + return _pcb->local_port; +} + +IPAddress AsyncClient::remoteIP() { + return IPAddress(getRemoteAddress()); +} + +uint16_t AsyncClient::remotePort() { + return getRemotePort(); +} + +IPAddress AsyncClient::localIP() { + return IPAddress(getLocalAddress()); +} + +uint16_t AsyncClient::localPort() { + return getLocalPort(); +} + +#if ASYNC_TCP_SSL_ENABLED +SSL * AsyncClient::getSSL(){ + if(_pcb && _pcb_secure){ + return tcp_ssl_get_ssl(_pcb); + } + return NULL; +} +#endif + +uint8_t AsyncClient::state() { + if(!_pcb) + return 0; + return _pcb->state; +} + +bool AsyncClient::connected(){ + if (!_pcb) + return false; +#if ASYNC_TCP_SSL_ENABLED + return _pcb->state == 4 && _handshake_done; +#else + return _pcb->state == 4; +#endif +} + +bool AsyncClient::connecting(){ + if (!_pcb) + return false; + return _pcb->state > 0 && _pcb->state < 4; +} + +bool AsyncClient::disconnecting(){ + if (!_pcb) + return false; + return _pcb->state > 4 && _pcb->state < 10; +} + +bool AsyncClient::disconnected(){ + if (!_pcb) + return true; + return _pcb->state == 0 || _pcb->state == 10; +} + +bool AsyncClient::freeable(){ + if (!_pcb) + return true; + return _pcb->state == 0 || _pcb->state > 4; +} + +bool AsyncClient::canSend(){ + return !_pcb_busy && (space() > 0); +} + + +// Callback Setters + +void AsyncClient::onConnect(AcConnectHandler cb, void* arg){ + _connect_cb = cb; + _connect_cb_arg = arg; +} + +void AsyncClient::onDisconnect(AcConnectHandler cb, void* arg){ + _discard_cb = cb; + _discard_cb_arg = arg; +} + +void AsyncClient::onAck(AcAckHandler cb, void* arg){ + _sent_cb = cb; + _sent_cb_arg = arg; +} + +void AsyncClient::onError(AcErrorHandler cb, void* arg){ + _error_cb = cb; + _error_cb_arg = arg; +} + +void AsyncClient::onData(AcDataHandler cb, void* arg){ + _recv_cb = cb; + _recv_cb_arg = arg; +} + +void AsyncClient::onTimeout(AcTimeoutHandler cb, void* arg){ + _timeout_cb = cb; + _timeout_cb_arg = arg; +} + +void AsyncClient::onPoll(AcConnectHandler cb, void* arg){ + _poll_cb = cb; + _poll_cb_arg = arg; +} + + +size_t AsyncClient::space(){ +#if ASYNC_TCP_SSL_ENABLED + if((_pcb != NULL) && (_pcb->state == 4) && _handshake_done){ + uint16_t s = tcp_sndbuf(_pcb); + if(_pcb_secure){ +#ifdef AXTLS_2_0_0_SNDBUF + return tcp_ssl_sndbuf(_pcb); +#else + if(s >= 128) //safe approach + return s - 128; + return 0; +#endif + } + return s; + } +#else + if((_pcb != NULL) && (_pcb->state == 4)){ + return tcp_sndbuf(_pcb); + } +#endif + return 0; +} + +const char * AsyncClient::errorToString(int8_t error){ + switch(error){ + case 0: return "OK"; + case -1: return "Out of memory error"; + case -2: return "Buffer error"; + case -3: return "Timeout"; + case -4: return "Routing problem"; + case -5: return "Operation in progress"; + case -6: return "Illegal value"; + case -7: return "Operation would block"; + case -8: return "Connection aborted"; + case -9: return "Connection reset"; + case -10: return "Connection closed"; + case -11: return "Not connected"; + case -12: return "Illegal argument"; + case -13: return "Address in use"; + case -14: return "Low-level netif error"; + case -15: return "Already connected"; + case -55: return "DNS failed"; + default: return "UNKNOWN"; + } +} + +const char * AsyncClient::stateToString(){ + switch(state()){ + case 0: return "Closed"; + case 1: return "Listen"; + case 2: return "SYN Sent"; + case 3: return "SYN Received"; + case 4: return "Established"; + case 5: return "FIN Wait 1"; + case 6: return "FIN Wait 2"; + case 7: return "Close Wait"; + case 8: return "Closing"; + case 9: return "Last ACK"; + case 10: return "Time Wait"; + default: return "UNKNOWN"; + } +} + +/* + Async TCP Server +*/ +struct pending_pcb { + tcp_pcb* pcb; + pbuf *pb; + struct pending_pcb * next; +}; + +AsyncServer::AsyncServer(IPAddress addr, uint16_t port) + : _port(port) + , _addr(addr) + , _noDelay(false) + , _pcb(0) + , _connect_cb(0) + , _connect_cb_arg(0) +#if ASYNC_TCP_SSL_ENABLED + , _pending(NULL) + , _ssl_ctx(NULL) + , _file_cb(0) + , _file_cb_arg(0) +#endif +{} + +AsyncServer::AsyncServer(uint16_t port) + : _port(port) + , _addr((uint32_t) IPADDR_ANY) + , _noDelay(false) + , _pcb(0) + , _connect_cb(0) + , _connect_cb_arg(0) +#if ASYNC_TCP_SSL_ENABLED + , _pending(NULL) + , _ssl_ctx(NULL) + , _file_cb(0) + , _file_cb_arg(0) +#endif +{} + +AsyncServer::~AsyncServer(){ + end(); +} + +void AsyncServer::onClient(AcConnectHandler cb, void* arg){ + _connect_cb = cb; + _connect_cb_arg = arg; +} + +#if ASYNC_TCP_SSL_ENABLED +void AsyncServer::onSslFileRequest(AcSSlFileHandler cb, void* arg){ + _file_cb = cb; + _file_cb_arg = arg; +} +#endif + +void AsyncServer::begin(){ + if(_pcb) + return; + + int8_t err; + tcp_pcb* pcb = tcp_new(); + if (!pcb){ + return; + } + + ip_addr_t local_addr; + local_addr.addr = (uint32_t) _addr; + err = tcp_bind(pcb, &local_addr, _port); + + if (err != ERR_OK) { + tcp_close(pcb); + return; + } + + tcp_pcb* listen_pcb = tcp_listen(pcb); + if (!listen_pcb) { + tcp_close(pcb); + return; + } + _pcb = listen_pcb; + tcp_arg(_pcb, (void*) this); + tcp_accept(_pcb, &_s_accept); +} + +#if ASYNC_TCP_SSL_ENABLED +void AsyncServer::beginSecure(const char *cert, const char *key, const char *password){ + if(_ssl_ctx){ + return; + } + tcp_ssl_file(_s_cert, this); + _ssl_ctx = tcp_ssl_new_server_ctx(cert, key, password); + if(_ssl_ctx){ + begin(); + } +} +#endif + +void AsyncServer::end(){ + if(_pcb){ + //cleanup all connections? + tcp_abort(_pcb); + tcp_arg(_pcb, NULL); + tcp_accept(_pcb, NULL); + if(tcp_close(_pcb) != ERR_OK){ + tcp_abort(_pcb); + } + _pcb = NULL; + } +#if ASYNC_TCP_SSL_ENABLED + if(_ssl_ctx){ + ssl_ctx_free(_ssl_ctx); + _ssl_ctx = NULL; + if(_pending){ + struct pending_pcb * p; + while(_pending){ + p = _pending; + _pending = _pending->next; + if(p->pb){ + pbuf_free(p->pb); + } + free(p); + } + } + } +#endif +} + +void AsyncServer::setNoDelay(bool nodelay){ + _noDelay = nodelay; +} + +bool AsyncServer::getNoDelay(){ + return _noDelay; +} + +uint8_t AsyncServer::status(){ + if (!_pcb) + return 0; + return _pcb->state; +} + +long AsyncServer::_accept(tcp_pcb* pcb, long err){ + if(_connect_cb){ +#if ASYNC_TCP_SSL_ENABLED + if (_noDelay || _ssl_ctx) +#else + if (_noDelay) +#endif + tcp_nagle_disable(pcb); + else + tcp_nagle_enable(pcb); + + +#if ASYNC_TCP_SSL_ENABLED + if(_ssl_ctx){ + if(tcp_ssl_has_client() || _pending){ + struct pending_pcb * new_item = (struct pending_pcb*)malloc(sizeof(struct pending_pcb)); + if(!new_item){ + ASYNC_TCP_DEBUG("### malloc new pending failed!\n"); + if(tcp_close(pcb) != ERR_OK){ + tcp_abort(pcb); + } + return ERR_OK; + } + ASYNC_TCP_DEBUG("### put to wait: %d\n", _clients_waiting); + new_item->pcb = pcb; + new_item->pb = NULL; + new_item->next = NULL; + tcp_setprio(_pcb, TCP_PRIO_MIN); + tcp_arg(pcb, this); + tcp_poll(pcb, &_s_poll, 1); + tcp_recv(pcb, &_s_recv); + + if(_pending == NULL){ + _pending = new_item; + } else { + struct pending_pcb * p = _pending; + while(p->next != NULL) + p = p->next; + p->next = new_item; + } + } else { + AsyncClient *c = new AsyncClient(pcb, _ssl_ctx); + if(c){ + c->onConnect([this](void * arg, AsyncClient *c){ + _connect_cb(_connect_cb_arg, c); + }, this); + } + } + return ERR_OK; + } else { + AsyncClient *c = new AsyncClient(pcb, NULL); +#else + AsyncClient *c = new AsyncClient(pcb); +#endif + if(c){ + _connect_cb(_connect_cb_arg, c); + return ERR_OK; + } +#if ASYNC_TCP_SSL_ENABLED + } +#endif + } + if(tcp_close(pcb) != ERR_OK){ + tcp_abort(pcb); + } + return ERR_OK; +} + + long AsyncServer::_s_accept(void *arg, tcp_pcb* pcb, long err){ + return reinterpret_cast(arg)->_accept(pcb, err); + } + +#if ASYNC_TCP_SSL_ENABLED +long AsyncServer::_poll(tcp_pcb* pcb){ + if(!tcp_ssl_has_client() && _pending){ + struct pending_pcb * p = _pending; + if(p->pcb == pcb){ + _pending = _pending->next; + } else { + while(p->next && p->next->pcb != pcb) p = p->next; + if(!p->next) return 0; + struct pending_pcb * b = p->next; + p->next = b->next; + p = b; + } + ASYNC_TCP_DEBUG("### remove from wait: %d\n", _clients_waiting); + AsyncClient *c = new AsyncClient(pcb, _ssl_ctx); + if(c){ + c->onConnect([this](void * arg, AsyncClient *c){ + _connect_cb(_connect_cb_arg, c); + }, this); + if(p->pb) + c->_recv(pcb, p->pb, 0); + } + free(p); + } + return ERR_OK; +} + +long AsyncServer::_recv(struct tcp_pcb *pcb, struct pbuf *pb, long err){ + if(!_pending) + return ERR_OK; + + struct pending_pcb * p; + + if(!pb){ + ASYNC_TCP_DEBUG("### close from wait: %d\n", _clients_waiting); + p = _pending; + if(p->pcb == pcb){ + _pending = _pending->next; + } else { + while(p->next && p->next->pcb != pcb) p = p->next; + if(!p->next) return 0; + struct pending_pcb * b = p->next; + p->next = b->next; + p = b; + } + if(p->pb){ + pbuf_free(p->pb); + } + free(p); + tcp_close(pcb); + tcp_abort(pcb); + } else { + ASYNC_TCP_DEBUG("### wait _recv: %u %d\n", pb->tot_len, _clients_waiting); + p = _pending; + while(p && p->pcb != pcb) + p = p->next; + if(p){ + if(p->pb){ + pbuf_chain(p->pb, pb); + } else { + p->pb = pb; + } + } + } + return ERR_OK; +} + +int AsyncServer::_cert(const char *filename, uint8_t **buf){ + if(_file_cb){ + return _file_cb(_file_cb_arg, filename, buf); + } + *buf = 0; + return 0; +} + +int AsyncServer::_s_cert(void *arg, const char *filename, uint8_t **buf){ + return reinterpret_cast(arg)->_cert(filename, buf); +} + +long AsyncServer::_s_poll(void *arg, struct tcp_pcb *pcb){ + return reinterpret_cast(arg)->_poll(pcb); +} + +long AsyncServer::_s_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, long err){ + return reinterpret_cast(arg)->_recv(pcb, pb, err); +} +#endif diff --git a/libraries/ESPAsyncTCP/src/ESPAsyncTCP.h b/libraries/ESPAsyncTCP/src/ESPAsyncTCP.h new file mode 100644 index 00000000..1e72dfd5 --- /dev/null +++ b/libraries/ESPAsyncTCP/src/ESPAsyncTCP.h @@ -0,0 +1,249 @@ +/* + Asynchronous TCP library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ + +#ifndef ASYNCTCP_H_ +#define ASYNCTCP_H_ + +#include +#include "IPAddress.h" +#include +#include "lwip/init.h" + +class AsyncClient; + +#define ASYNC_MAX_ACK_TIME 5000 +#define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given) +#define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react. + +typedef std::function AcConnectHandler; +typedef std::function AcAckHandler; +typedef std::function AcErrorHandler; +typedef std::function AcDataHandler; +typedef std::function AcTimeoutHandler; + +struct tcp_pcb; +struct pbuf; +struct ip_addr; +#if ASYNC_TCP_SSL_ENABLED +struct SSL_; +typedef struct SSL_ SSL; +struct SSL_CTX_; +typedef struct SSL_CTX_ SSL_CTX; +#endif + +class AsyncClient { + protected: + friend class AsyncTCPbuffer; + tcp_pcb* _pcb; + AcConnectHandler _connect_cb; + void* _connect_cb_arg; + AcConnectHandler _discard_cb; + void* _discard_cb_arg; + AcAckHandler _sent_cb; + void* _sent_cb_arg; + AcErrorHandler _error_cb; + void* _error_cb_arg; + AcDataHandler _recv_cb; + void* _recv_cb_arg; + AcTimeoutHandler _timeout_cb; + void* _timeout_cb_arg; + AcConnectHandler _poll_cb; + void* _poll_cb_arg; + bool _pcb_busy; +#if ASYNC_TCP_SSL_ENABLED + bool _pcb_secure; + bool _handshake_done; +#endif + uint32_t _pcb_sent_at; + bool _close_pcb; + bool _ack_pcb; + uint32_t _tx_unacked_len; + uint32_t _tx_acked_len; + uint32_t _tx_unsent_len; + uint32_t _rx_ack_len; + uint32_t _rx_last_packet; + uint32_t _rx_since_timeout; + uint32_t _ack_timeout; + uint16_t _connect_port; + + int8_t _close(); + long _connected(void* pcb, long err); + void _error(long err); +#if ASYNC_TCP_SSL_ENABLED + void _ssl_error(int8_t err); +#endif + long _poll(tcp_pcb* pcb); + long _sent(tcp_pcb* pcb, uint16_t len); +#if LWIP_VERSION_MAJOR == 1 + void _dns_found(struct ip_addr *ipaddr); +#else + void _dns_found(const ip_addr *ipaddr); +#endif + static long _s_poll(void *arg, struct tcp_pcb *tpcb); + static long _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, long err); + static void _s_error(void *arg, long err); + static long _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len); + static long _s_connected(void* arg, void* tpcb, long err); +#if LWIP_VERSION_MAJOR == 1 + static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg); +#else + static void _s_dns_found(const char *name, const ip_addr *ipaddr, void *arg); +#endif +#if ASYNC_TCP_SSL_ENABLED + static void _s_data(void *arg, struct tcp_pcb *tcp, uint8_t * data, size_t len); + static void _s_handshake(void *arg, struct tcp_pcb *tcp, SSL *ssl); + static void _s_ssl_error(void *arg, struct tcp_pcb *tcp, int8_t err); +#endif + + public: + AsyncClient* prev; + AsyncClient* next; + +#if ASYNC_TCP_SSL_ENABLED + AsyncClient(tcp_pcb* pcb = 0, SSL_CTX * ssl_ctx = NULL); +#else + AsyncClient(tcp_pcb* pcb = 0); +#endif + ~AsyncClient(); + + AsyncClient & operator=(const AsyncClient &other); + AsyncClient & operator+=(const AsyncClient &other); + + bool operator==(const AsyncClient &other); + + bool operator!=(const AsyncClient &other) { + return !(*this == other); + } +#if ASYNC_TCP_SSL_ENABLED + bool connect(IPAddress ip, uint16_t port, bool secure=false); + bool connect(const char* host, uint16_t port, bool secure=false); +#else + bool connect(IPAddress ip, uint16_t port); + bool connect(const char* host, uint16_t port); +#endif + void close(bool now = false); + void stop(); + int8_t abort(); + bool free(); + + bool canSend();//ack is not pending + size_t space(); + size_t add(const char* data, size_t size, uint8_t apiflags=0);//add for sending + bool send();//send all data added with the method above + size_t ack(size_t len); //ack data that you have not acked using the method below + void ackLater(){ _ack_pcb = false; } //will not ack the current packet. Call from onData + +#if ASYNC_TCP_SSL_ENABLED + SSL *getSSL(); +#endif + + size_t write(const char* data); + size_t write(const char* data, size_t size, uint8_t apiflags=0); //only when canSend() == true + + uint8_t state(); + bool connecting(); + bool connected(); + bool disconnecting(); + bool disconnected(); + bool freeable();//disconnected or disconnecting + + uint16_t getMss(); + uint32_t getRxTimeout(); + void setRxTimeout(uint32_t timeout);//no RX data timeout for the connection in seconds + uint32_t getAckTimeout(); + void setAckTimeout(uint32_t timeout);//no ACK timeout for the last sent packet in milliseconds + void setNoDelay(bool nodelay); + bool getNoDelay(); + uint32_t getRemoteAddress(); + uint16_t getRemotePort(); + uint32_t getLocalAddress(); + uint16_t getLocalPort(); + + IPAddress remoteIP(); + uint16_t remotePort(); + IPAddress localIP(); + uint16_t localPort(); + + void onConnect(AcConnectHandler cb, void* arg = 0); //on successful connect + void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected + void onAck(AcAckHandler cb, void* arg = 0); //ack received + void onError(AcErrorHandler cb, void* arg = 0); //unsuccessful connect or error + void onData(AcDataHandler cb, void* arg = 0); //data received + void onTimeout(AcTimeoutHandler cb, void* arg = 0); //ack timeout + void onPoll(AcConnectHandler cb, void* arg = 0); //every 125ms when connected + + const char * errorToString(int8_t error); + const char * stateToString(); + + long _recv(tcp_pcb* pcb, pbuf* pb, long err); +}; + +#if ASYNC_TCP_SSL_ENABLED +typedef std::function AcSSlFileHandler; +struct pending_pcb; +#endif + +class AsyncServer { + protected: + uint16_t _port; + IPAddress _addr; + bool _noDelay; + tcp_pcb* _pcb; + AcConnectHandler _connect_cb; + void* _connect_cb_arg; +#if ASYNC_TCP_SSL_ENABLED + struct pending_pcb * _pending; + SSL_CTX * _ssl_ctx; + AcSSlFileHandler _file_cb; + void* _file_cb_arg; +#endif + + public: + + AsyncServer(IPAddress addr, uint16_t port); + AsyncServer(uint16_t port); + ~AsyncServer(); + void onClient(AcConnectHandler cb, void* arg); +#if ASYNC_TCP_SSL_ENABLED + void onSslFileRequest(AcSSlFileHandler cb, void* arg); + void beginSecure(const char *cert, const char *private_key_file, const char *password); +#endif + void begin(); + void end(); + void setNoDelay(bool nodelay); + bool getNoDelay(); + uint8_t status(); + + protected: + long _accept(tcp_pcb* newpcb, long err); + static long _s_accept(void *arg, tcp_pcb* newpcb, long err); +#if ASYNC_TCP_SSL_ENABLED + int _cert(const char *filename, uint8_t **buf); + long _poll(tcp_pcb* pcb); + long _recv(tcp_pcb *pcb, struct pbuf *pb, long err); + static int _s_cert(void *arg, const char *filename, uint8_t **buf); + static long _s_poll(void *arg, struct tcp_pcb *tpcb); + static long _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, long err); +#endif +}; + + +#endif /* ASYNCTCP_H_ */ diff --git a/libraries/ESPAsyncTCP/src/ESPAsyncTCPbuffer.cpp b/libraries/ESPAsyncTCP/src/ESPAsyncTCPbuffer.cpp new file mode 100644 index 00000000..7bca134f --- /dev/null +++ b/libraries/ESPAsyncTCP/src/ESPAsyncTCPbuffer.cpp @@ -0,0 +1,541 @@ +/** + * @file ESPAsyncTCPbuffer.cpp + * @date 22.01.2016 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the Asynv TCP for ESP. + * + * 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 + * + */ + + +#include +#include + +#include "ESPAsyncTCPbuffer.h" + + +AsyncTCPbuffer::AsyncTCPbuffer(AsyncClient* client) { + if(client == NULL) { + DEBUG_ASYNC_TCP("[A-TCP] client is null!!!\n"); + panic(); + } + + _client = client; + _TXbufferWrite = new cbuf(1460); + _TXbufferRead = _TXbufferWrite; + _RXbuffer = new cbuf(100); + _RXmode = ATB_RX_MODE_FREE; + _rxSize = 0; + _rxTerminator = 0x00; + _rxReadBytesPtr = NULL; + _rxReadStringPtr = NULL; + _cbDisconnect = NULL; + + _cbRX = NULL; + _cbDone = NULL; + _attachCallbacks(); +} + +AsyncTCPbuffer::~AsyncTCPbuffer() { + if(_client) { + _client->close(); + } + + if(_RXbuffer) { + delete _RXbuffer; + _RXbuffer = NULL; + } + + if(_TXbufferWrite) { + // will be deleted in _TXbufferRead chain + _TXbufferWrite = NULL; + } + + if(_TXbufferRead) { + cbuf * next = _TXbufferRead->next; + delete _TXbufferRead; + while(next != NULL) { + _TXbufferRead = next; + next = _TXbufferRead->next; + delete _TXbufferRead; + } + _TXbufferRead = NULL; + } +} + +size_t AsyncTCPbuffer::write(String & data) { + return write(data.c_str(), data.length()); +} + +size_t AsyncTCPbuffer::write(uint8_t data) { + return write(&data, 1); +} + +size_t AsyncTCPbuffer::write(const char* data) { + return write((const uint8_t *) data, strlen(data)); +} + +size_t AsyncTCPbuffer::write(const char *data, size_t len) { + return write((const uint8_t *) data, len); +} + +/** + * write data in to buffer and try to send the data + * @param data + * @param len + * @return + */ +size_t AsyncTCPbuffer::write(const uint8_t *data, size_t len) { + if(_TXbufferWrite == NULL || _client == NULL || !_client->connected() || data == NULL || len == 0) { + return 0; + } + + size_t bytesLeft = len; + while(bytesLeft) { + size_t w = _TXbufferWrite->write((const char*) data, bytesLeft); + bytesLeft -= w; + data += w; + _sendBuffer(); + + // add new buffer since we have more data + if(_TXbufferWrite->full() && bytesLeft > 0) { + + // to less ram!!! + if(ESP.getFreeHeap() < 4096) { + DEBUG_ASYNC_TCP("[A-TCP] run out of Heap can not send all Data!\n"); + return (len - bytesLeft); + } + + cbuf * next = new cbuf(1460); + + if(next == NULL) { + DEBUG_ASYNC_TCP("[A-TCP] run out of Heap!\n"); + panic(); + } else { + DEBUG_ASYNC_TCP("[A-TCP] new cbuf\n"); + } + + // add new buffer to chain (current cbuf) + _TXbufferWrite->next = next; + + // move ptr for next data + _TXbufferWrite = next; + } + } + + return len; + +} + +/** + * wait until all data has send out + */ +void AsyncTCPbuffer::flush() { + while(!_TXbufferWrite->empty()) { + while(!_client->canSend()) { + delay(0); + } + _sendBuffer(); + } +} + +void AsyncTCPbuffer::noCallback() { + _RXmode = ATB_RX_MODE_NONE; +} + +void AsyncTCPbuffer::readStringUntil(char terminator, String * str, AsyncTCPbufferDoneCb done) { + if(_client == NULL) { + return; + } + DEBUG_ASYNC_TCP("[A-TCP] readStringUntil terminator: %02X\n", terminator); + _RXmode = ATB_RX_MODE_NONE; + _cbDone = done; + _rxReadStringPtr = str; + _rxTerminator = terminator; + _rxSize = 0; + _RXmode = ATB_RX_MODE_TERMINATOR_STRING; +} + +/* + void AsyncTCPbuffer::readBytesUntil(char terminator, char *buffer, size_t length, AsyncTCPbufferDoneCb done) { + _RXmode = ATB_RX_MODE_NONE; + _cbDone = done; + _rxReadBytesPtr = (uint8_t *) buffer; + _rxTerminator = terminator; + _rxSize = length; + _RXmode = ATB_RX_MODE_TERMINATOR; + _handleRxBuffer(NULL, 0); + } + + void AsyncTCPbuffer::readBytesUntil(char terminator, uint8_t *buffer, size_t length, AsyncTCPbufferDoneCb done) { + readBytesUntil(terminator, (char *) buffer, length, done); + } + */ + +void AsyncTCPbuffer::readBytes(char *buffer, size_t length, AsyncTCPbufferDoneCb done) { + if(_client == NULL) { + return; + } + DEBUG_ASYNC_TCP("[A-TCP] readBytes length: %d\n", length); + _RXmode = ATB_RX_MODE_NONE; + _cbDone = done; + _rxReadBytesPtr = (uint8_t *) buffer; + _rxSize = length; + _RXmode = ATB_RX_MODE_READ_BYTES; +} + +void AsyncTCPbuffer::readBytes(uint8_t *buffer, size_t length, AsyncTCPbufferDoneCb done) { + readBytes((char *) buffer, length, done); +} + +void AsyncTCPbuffer::onData(AsyncTCPbufferDataCb cb) { + if(_client == NULL) { + return; + } + DEBUG_ASYNC_TCP("[A-TCP] onData\n"); + _RXmode = ATB_RX_MODE_NONE; + _cbDone = NULL; + _cbRX = cb; + _RXmode = ATB_RX_MODE_FREE; +} + +void AsyncTCPbuffer::onDisconnect(AsyncTCPbufferDisconnectCb cb) { + _cbDisconnect = cb; +} + +IPAddress AsyncTCPbuffer::remoteIP() { + if(!_client) { + return IPAddress(0U); + } + return _client->remoteIP(); +} + +uint16_t AsyncTCPbuffer::remotePort() { + if(!_client) { + return 0; + } + return _client->remotePort(); +} + +bool AsyncTCPbuffer::connected() { + if(!_client) { + return false; + } + return _client->connected(); +} + +void AsyncTCPbuffer::stop() { + + if(!_client) { + return; + } + _client->stop(); + _client = NULL; + + if(_cbDone) { + switch(_RXmode) { + case ATB_RX_MODE_READ_BYTES: + case ATB_RX_MODE_TERMINATOR: + case ATB_RX_MODE_TERMINATOR_STRING: + _RXmode = ATB_RX_MODE_NONE; + _cbDone(false, NULL); + break; + default: + break; + } + } + _RXmode = ATB_RX_MODE_NONE; +} + +void AsyncTCPbuffer::close() { + stop(); +} + + +///-------------------------------- + +/** + * attachCallbacks to AsyncClient class + */ +void AsyncTCPbuffer::_attachCallbacks() { + if(!_client) { + return; + } + DEBUG_ASYNC_TCP("[A-TCP] attachCallbacks\n"); + + _client->onPoll([](void *obj, AsyncClient* c) { + AsyncTCPbuffer* b = ((AsyncTCPbuffer*)(obj)); + if((b->_TXbufferRead != NULL) && !b->_TXbufferRead->empty()) { + b->_sendBuffer(); + } + // if(!b->_RXbuffer->empty()) { + // b->_handleRxBuffer(NULL, 0); + // } + }, this); + + _client->onAck([](void *obj, AsyncClient* c, size_t len, uint32_t time) { + DEBUG_ASYNC_TCP("[A-TCP] onAck\n"); + ((AsyncTCPbuffer*)(obj))->_sendBuffer(); + }, this); + + _client->onDisconnect([](void *obj, AsyncClient* c) { + DEBUG_ASYNC_TCP("[A-TCP] onDisconnect\n"); + AsyncTCPbuffer* b = ((AsyncTCPbuffer*)(obj)); + b->_client = NULL; + bool del = true; + if(b->_cbDisconnect) { + del = b->_cbDisconnect(b); + } + delete c; + if(del) { + delete b; + } + }, this); + + _client->onData([](void *obj, AsyncClient* c, void *buf, size_t len) { + AsyncTCPbuffer* b = ((AsyncTCPbuffer*)(obj)); + b->_rxData((uint8_t *)buf, len); + }, this); + + _client->onTimeout([](void *obj, AsyncClient* c, uint32_t time){ + DEBUG_ASYNC_TCP("[A-TCP] onTimeout\n"); + c->close(); + }, this); + + DEBUG_ASYNC_TCP("[A-TCP] attachCallbacks Done.\n"); +} + +/** + * send TX buffer if possible + */ +void AsyncTCPbuffer::_sendBuffer() { + //DEBUG_ASYNC_TCP("[A-TCP] _sendBuffer...\n"); + size_t available = _TXbufferRead->available(); + if(available == 0 || _client == NULL || !_client->connected() || !_client->canSend()) { + return; + } + + while((_client->space() > 0) && (_TXbufferRead->available() > 0) && _client->canSend()) { + + available = _TXbufferRead->available(); + + if(available > _client->space()) { + available = _client->space(); + } + + char *out = new char[available]; + if(out == NULL) { + DEBUG_ASYNC_TCP("[A-TCP] to less heap, try later.\n"); + return; + } + + // read data from buffer + _TXbufferRead->peek(out, available); + + // send data + size_t send = _client->write((const char*) out, available); + if(send != available) { + DEBUG_ASYNC_TCP("[A-TCP] write failed send: %d available: %d \n", send, available); + } + + // remove really send data from buffer + _TXbufferRead->remove(send); + + // if buffer is empty and there is a other buffer in chain delete the empty one + if(_TXbufferRead->available() == 0 && _TXbufferRead->next != NULL) { + cbuf * old = _TXbufferRead; + _TXbufferRead = _TXbufferRead->next; + delete old; + DEBUG_ASYNC_TCP("[A-TCP] delete cbuf\n"); + } + + delete out; + } + +} + +/** + * called on incoming data + * @param buf + * @param len + */ +void AsyncTCPbuffer::_rxData(uint8_t *buf, size_t len) { + if(!_client || !_client->connected()) { + DEBUG_ASYNC_TCP("[A-TCP] not connected!\n"); + return; + } + if(!_RXbuffer) { + DEBUG_ASYNC_TCP("[A-TCP] _rxData no _RXbuffer!\n"); + return; + } + DEBUG_ASYNC_TCP("[A-TCP] _rxData len: %d RXmode: %d\n", len, _RXmode); + + size_t handled = 0; + + if(_RXmode != ATB_RX_MODE_NONE) { + handled = _handleRxBuffer((uint8_t *) buf, len); + buf += handled; + len -= handled; + + // handle as much as possible before using the buffer + if(_RXbuffer->empty()) { + while(_RXmode != ATB_RX_MODE_NONE && handled != 0 && len > 0) { + handled = _handleRxBuffer(buf, len); + buf += handled; + len -= handled; + } + } + } + + if(len > 0) { + + if(_RXbuffer->room() < len) { + // to less space + DEBUG_ASYNC_TCP("[A-TCP] _rxData buffer full try resize\n"); + _RXbuffer->resizeAdd((len + _RXbuffer->room())); + + if(_RXbuffer->room() < len) { + DEBUG_ASYNC_TCP("[A-TCP] _rxData buffer to full can only handle %d!!!\n", _RXbuffer->room()); + } + } + + _RXbuffer->write((const char *) (buf), len); + } + + if(!_RXbuffer->empty() && _RXmode != ATB_RX_MODE_NONE) { + // handle as much as possible data in buffer + handled = _handleRxBuffer(NULL, 0); + while(_RXmode != ATB_RX_MODE_NONE && handled != 0) { + handled = _handleRxBuffer(NULL, 0); + } + } + + // clean up ram + if(_RXbuffer->empty() && _RXbuffer->room() != 100) { + _RXbuffer->resize(100); + } + +} + +/** + * + */ +size_t AsyncTCPbuffer::_handleRxBuffer(uint8_t *buf, size_t len) { + if(!_client || !_client->connected() || _RXbuffer == NULL) { + return 0; + } + + DEBUG_ASYNC_TCP("[A-TCP] _handleRxBuffer len: %d RXmode: %d\n", len, _RXmode); + + size_t BufferAvailable = _RXbuffer->available(); + size_t r = 0; + + if(_RXmode == ATB_RX_MODE_NONE) { + return 0; + } else if(_RXmode == ATB_RX_MODE_FREE) { + if(_cbRX == NULL) { + return 0; + } + + if(BufferAvailable > 0) { + uint8_t * b = new uint8_t[BufferAvailable]; + _RXbuffer->peek((char *) b, BufferAvailable); + r = _cbRX(b, BufferAvailable); + _RXbuffer->remove(r); + } + + if(r == BufferAvailable && buf && (len > 0)) { + return _cbRX(buf, len); + } else { + return 0; + } + + } else if(_RXmode == ATB_RX_MODE_READ_BYTES) { + if(_rxReadBytesPtr == NULL || _cbDone == NULL) { + return 0; + } + + size_t newReadCount = 0; + + if(BufferAvailable) { + r = _RXbuffer->read((char *) _rxReadBytesPtr, _rxSize); + _rxSize -= r; + _rxReadBytesPtr += r; + } + + if(_RXbuffer->empty() && (len > 0) && buf) { + r = len; + if(r > _rxSize) { + r = _rxSize; + } + memcpy(_rxReadBytesPtr, buf, r); + _rxReadBytesPtr += r; + _rxSize -= r; + newReadCount += r; + } + + if(_rxSize == 0) { + _RXmode = ATB_RX_MODE_NONE; + _cbDone(true, NULL); + } + + // add left over bytes to Buffer + return newReadCount; + + } else if(_RXmode == ATB_RX_MODE_TERMINATOR) { + // TODO implement read terminator non string + + } else if(_RXmode == ATB_RX_MODE_TERMINATOR_STRING) { + if(_rxReadStringPtr == NULL || _cbDone == NULL) { + return 0; + } + + // handle Buffer + if(BufferAvailable > 0) { + while(!_RXbuffer->empty()) { + char c = _RXbuffer->read(); + if(c == _rxTerminator || c == 0x00) { + _RXmode = ATB_RX_MODE_NONE; + _cbDone(true, _rxReadStringPtr); + return 0; + } else { + (*_rxReadStringPtr) += c; + } + } + } + + if(_RXbuffer->empty() && (len > 0) && buf) { + size_t newReadCount = 0; + while(newReadCount < len) { + char c = (char) *buf; + buf++; + newReadCount++; + if(c == _rxTerminator || c == 0x00) { + _RXmode = ATB_RX_MODE_NONE; + _cbDone(true, _rxReadStringPtr); + return newReadCount; + } else { + (*_rxReadStringPtr) += c; + } + } + return newReadCount; + } + } + + return 0; +} diff --git a/libraries/ESPAsyncTCP/src/ESPAsyncTCPbuffer.h b/libraries/ESPAsyncTCP/src/ESPAsyncTCPbuffer.h new file mode 100644 index 00000000..374364e5 --- /dev/null +++ b/libraries/ESPAsyncTCP/src/ESPAsyncTCPbuffer.h @@ -0,0 +1,118 @@ +/** + * @file ESPAsyncTCPbuffer.h + * @date 22.01.2016 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the Asynv TCP for ESP. + * + * 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 + * + */ + +#ifndef ESPASYNCTCPBUFFER_H_ +#define ESPASYNCTCPBUFFER_H_ + +//#define DEBUG_ASYNC_TCP(...) while(((U0S >> USTXC) & 0x7F) != 0x00); os_printf( __VA_ARGS__ ); while(((U0S >> USTXC) & 0x7F) != 0x00) + +#ifndef DEBUG_ASYNC_TCP +#define DEBUG_ASYNC_TCP(...) +#endif + +#include +#include + +#include "ESPAsyncTCP.h" + + + +typedef enum { + ATB_RX_MODE_NONE, + ATB_RX_MODE_FREE, + ATB_RX_MODE_READ_BYTES, + ATB_RX_MODE_TERMINATOR, + ATB_RX_MODE_TERMINATOR_STRING +} atbRxMode_t; + +class AsyncTCPbuffer: public Print { + + public: + + typedef std::function AsyncTCPbufferDataCb; + typedef std::function AsyncTCPbufferDoneCb; + typedef std::function AsyncTCPbufferDisconnectCb; + + AsyncTCPbuffer(AsyncClient* c); + virtual ~AsyncTCPbuffer(); + + size_t write(String & data); + size_t write(uint8_t data); + size_t write(const char* data); + size_t write(const char *data, size_t len); + size_t write(const uint8_t *data, size_t len); + + void flush(); + + void noCallback(); + + void readStringUntil(char terminator, String * str, AsyncTCPbufferDoneCb done); + + // TODO implement read terminator non string + //void readBytesUntil(char terminator, char *buffer, size_t length, AsyncTCPbufferDoneCb done); + //void readBytesUntil(char terminator, uint8_t *buffer, size_t length, AsyncTCPbufferDoneCb done); + + void readBytes(char *buffer, size_t length, AsyncTCPbufferDoneCb done); + void readBytes(uint8_t *buffer, size_t length, AsyncTCPbufferDoneCb done); + + // TODO implement + // void setTimeout(size_t timeout); + + void onData(AsyncTCPbufferDataCb cb); + void onDisconnect(AsyncTCPbufferDisconnectCb cb); + + IPAddress remoteIP(); + uint16_t remotePort(); + IPAddress localIP(); + uint16_t localPort(); + + bool connected(); + + void stop(); + void close(); + + protected: + AsyncClient* _client; + cbuf * _TXbufferRead; + cbuf * _TXbufferWrite; + cbuf * _RXbuffer; + atbRxMode_t _RXmode; + size_t _rxSize; + char _rxTerminator; + uint8_t * _rxReadBytesPtr; + String * _rxReadStringPtr; + + AsyncTCPbufferDataCb _cbRX; + AsyncTCPbufferDoneCb _cbDone; + AsyncTCPbufferDisconnectCb _cbDisconnect; + + void _attachCallbacks(); + void _sendBuffer(); + void _on_close(); + void _rxData(uint8_t *buf, size_t len); + size_t _handleRxBuffer(uint8_t *buf, size_t len); + +}; + +#endif /* ESPASYNCTCPBUFFER_H_ */ diff --git a/libraries/ESPAsyncTCP/src/SyncClient.cpp b/libraries/ESPAsyncTCP/src/SyncClient.cpp new file mode 100644 index 00000000..df9489f7 --- /dev/null +++ b/libraries/ESPAsyncTCP/src/SyncClient.cpp @@ -0,0 +1,286 @@ +/* + Asynchronous TCP library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +#include "SyncClient.h" +#include "Arduino.h" +#include "ESPAsyncTCP.h" +#include "cbuf.h" + + +SyncClient::SyncClient(size_t txBufLen) + : _client(NULL) + , _tx_buffer(NULL) + , _tx_buffer_size(txBufLen) + , _rx_buffer(NULL) +{} + +SyncClient::SyncClient(AsyncClient *client, size_t txBufLen) + : _client(client) + , _tx_buffer(new cbuf(txBufLen)) + , _tx_buffer_size(txBufLen) + , _rx_buffer(NULL) +{ + _attachCallbacks(); +} + +SyncClient::~SyncClient(){ + if(_tx_buffer != NULL){ + cbuf *b = _tx_buffer; + _tx_buffer = NULL; + delete b; + } + while(_rx_buffer != NULL){ + cbuf *b = _rx_buffer; + _rx_buffer = _rx_buffer->next; + delete b; + } +} + +#if ASYNC_TCP_SSL_ENABLED +int SyncClient::connect(IPAddress ip, uint16_t port, bool secure){ +#else +int SyncClient::connect(IPAddress ip, uint16_t port){ +#endif + if(_client != NULL && connected()) + return 0; + _client = new AsyncClient(); + _client->onConnect([](void *obj, AsyncClient *c){ ((SyncClient*)(obj))->_onConnect(c); }, this); + _attachCallbacks_Disconnect(); +#if ASYNC_TCP_SSL_ENABLED + if(_client->connect(ip, port, secure)){ +#else + if(_client->connect(ip, port)){ +#endif + while(_client != NULL && !_client->connected() && !_client->disconnecting()) + delay(1); + return connected(); + } + return 0; +} + +#if ASYNC_TCP_SSL_ENABLED +int SyncClient::connect(const char *host, uint16_t port, bool secure){ +#else +int SyncClient::connect(const char *host, uint16_t port){ +#endif + if(_client != NULL && connected()){ + return 0; + } + _client = new AsyncClient(); + _client->onConnect([](void *obj, AsyncClient *c){ ((SyncClient*)(obj))->_onConnect(c); }, this); + _attachCallbacks_Disconnect(); +#if ASYNC_TCP_SSL_ENABLED + if(_client->connect(host, port, secure)){ +#else + if(_client->connect(host, port)){ +#endif + while(_client != NULL && !_client->connected() && !_client->disconnecting()) + delay(1); + return connected(); + } + return 0; +} + +SyncClient & SyncClient::operator=(const SyncClient &other){ + if(_client != NULL){ + _client->abort(); + _client->free(); + _client = NULL; + } + _tx_buffer_size = other._tx_buffer_size; + if(_tx_buffer != NULL){ + cbuf *b = _tx_buffer; + _tx_buffer = NULL; + delete b; + } + while(_rx_buffer != NULL){ + cbuf *b = _rx_buffer; + _rx_buffer = b->next; + delete b; + } + _tx_buffer = new cbuf(other._tx_buffer_size); + _client = other._client; + _attachCallbacks(); + return *this; +} + +void SyncClient::setTimeout(uint32_t seconds){ + if(_client != NULL) + _client->setRxTimeout(seconds); +} + +uint8_t SyncClient::status(){ + if(_client == NULL) + return 0; + return _client->state(); +} + +uint8_t SyncClient::connected(){ + return (_client != NULL && _client->connected()); +} + +void SyncClient::stop(){ + if(_client != NULL) + _client->close(true); +} + +size_t SyncClient::_sendBuffer(){ + size_t available = _tx_buffer->available(); + if(!connected() || !_client->canSend() || available == 0) + return 0; + size_t sendable = _client->space(); + if(sendable < available) + available= sendable; + char *out = new char[available]; + _tx_buffer->read(out, available); + size_t sent = _client->write(out, available); + delete[] out; + return sent; +} + +void SyncClient::_onData(void *data, size_t len){ + _client->ackLater(); + cbuf *b = new cbuf(len+1); + if(b != NULL){ + b->write((const char *)data, len); + if(_rx_buffer == NULL) + _rx_buffer = b; + else { + cbuf *p = _rx_buffer; + while(p->next != NULL) + p = p->next; + p->next = b; + } + } +} + +void SyncClient::_onDisconnect(){ + if(_client != NULL){ + _client = NULL; + } + if(_tx_buffer != NULL){ + cbuf *b = _tx_buffer; + _tx_buffer = NULL; + delete b; + } +} + +void SyncClient::_onConnect(AsyncClient *c){ + _client = c; + if(_tx_buffer != NULL){ + cbuf *b = _tx_buffer; + _tx_buffer = NULL; + delete b; + } + _tx_buffer = new cbuf(_tx_buffer_size); + _attachCallbacks_AfterConnected(); +} + +void SyncClient::_attachCallbacks(){ + _attachCallbacks_Disconnect(); + _attachCallbacks_AfterConnected(); +} + +void SyncClient::_attachCallbacks_AfterConnected(){ + _client->onAck([](void *obj, AsyncClient* c, size_t len, uint32_t time){ ((SyncClient*)(obj))->_sendBuffer(); }, this); + _client->onData([](void *obj, AsyncClient* c, void *data, size_t len){ ((SyncClient*)(obj))->_onData(data, len); }, this); + _client->onTimeout([](void *obj, AsyncClient* c, uint32_t time){ c->close(); }, this); +} + +void SyncClient::_attachCallbacks_Disconnect(){ + _client->onDisconnect([](void *obj, AsyncClient* c){ ((SyncClient*)(obj))->_onDisconnect(); delete c; }, this); +} + +size_t SyncClient::write(uint8_t data){ + return write(&data, 1); +} + +size_t SyncClient::write(const uint8_t *data, size_t len){ + if(_tx_buffer == NULL || !connected()){ + return 0; + } + size_t toWrite = 0; + size_t toSend = len; + while(_tx_buffer->room() < toSend){ + toWrite = _tx_buffer->room(); + _tx_buffer->write((const char*)data, toWrite); + while(!_client->canSend() && connected()) + delay(0); + _sendBuffer(); + toSend -= toWrite; + } + _tx_buffer->write((const char*)(data+(len - toSend)), toSend); + if(_client->canSend() && connected()) + _sendBuffer(); + return len; +} + +int SyncClient::available(){ + if(_rx_buffer == NULL) return 0; + size_t a = 0; + cbuf *b = _rx_buffer; + while(b != NULL){ + a += b->available(); + b = b->next; + } + return a; +} + +int SyncClient::peek(){ + if(_rx_buffer == NULL) return -1; + return _rx_buffer->peek(); +} + +int SyncClient::read(uint8_t *data, size_t len){ + if(_rx_buffer == NULL) return -1; + + size_t readSoFar = 0; + while(_rx_buffer != NULL && (len - readSoFar) >= _rx_buffer->available()){ + cbuf *b = _rx_buffer; + _rx_buffer = _rx_buffer->next; + size_t toRead = b->available(); + readSoFar += b->read((char*)(data+readSoFar), toRead); + if(connected()){ + _client->ack(b->size() - 1); + } + delete b; + } + if(_rx_buffer != NULL && readSoFar < len){ + readSoFar += _rx_buffer->read((char*)(data+readSoFar), (len - readSoFar)); + } + return readSoFar; +} + +int SyncClient::read(){ + uint8_t res = 0; + if(read(&res, 1) != 1) + return -1; + return res; +} + +void SyncClient::flush(){ + if(_tx_buffer == NULL || !connected()) + return; + if(_tx_buffer->available()){ + while(!_client->canSend() && connected()) + delay(0); + _sendBuffer(); + } +} diff --git a/libraries/ESPAsyncTCP/src/SyncClient.h b/libraries/ESPAsyncTCP/src/SyncClient.h new file mode 100644 index 00000000..e46f4e48 --- /dev/null +++ b/libraries/ESPAsyncTCP/src/SyncClient.h @@ -0,0 +1,82 @@ +/* + Asynchronous TCP library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ + +#ifndef SYNCCLIENT_H_ +#define SYNCCLIENT_H_ + +#include "Client.h" +#include +class cbuf; +class AsyncClient; + +class SyncClient: public Client { + private: + AsyncClient *_client; + cbuf *_tx_buffer; + size_t _tx_buffer_size; + cbuf *_rx_buffer; + + size_t _sendBuffer(); + void _onData(void *data, size_t len); + void _onConnect(AsyncClient *c); + void _onDisconnect(); + void _attachCallbacks(); + void _attachCallbacks_Disconnect(); + void _attachCallbacks_AfterConnected(); + + public: + SyncClient(size_t txBufLen = 1460); + SyncClient(AsyncClient *client, size_t txBufLen = 1460); + virtual ~SyncClient(); + + operator bool(){ return connected(); } + SyncClient & operator=(const SyncClient &other); + +#if ASYNC_TCP_SSL_ENABLED + int connect(IPAddress ip, uint16_t port, bool secure); + int connect(const char *host, uint16_t port, bool secure); + int connect(IPAddress ip, uint16_t port){ + return connect(ip, port, false); + } + int connect(const char *host, uint16_t port){ + return connect(host, port, false); + } +#else + int connect(IPAddress ip, uint16_t port); + int connect(const char *host, uint16_t port); +#endif + void setTimeout(uint32_t seconds); + + uint8_t status(); + uint8_t connected(); + void stop(); + + size_t write(uint8_t data); + size_t write(const uint8_t *data, size_t len); + + int available(); + int peek(); + int read(); + int read(uint8_t *data, size_t len); + void flush(); +}; + +#endif /* SYNCCLIENT_H_ */ diff --git a/libraries/ESPAsyncTCP/src/async_config.h b/libraries/ESPAsyncTCP/src/async_config.h new file mode 100644 index 00000000..61cfc25d --- /dev/null +++ b/libraries/ESPAsyncTCP/src/async_config.h @@ -0,0 +1,11 @@ +#ifndef LIBRARIES_ESPASYNCTCP_SRC_ASYNC_CONFIG_H_ +#define LIBRARIES_ESPASYNCTCP_SRC_ASYNC_CONFIG_H_ + +#ifndef ASYNC_TCP_SSL_ENABLED +#define ASYNC_TCP_SSL_ENABLED 0 +#endif + +#define ASYNC_TCP_DEBUG(...) //ets_printf(__VA_ARGS__) +#define TCP_SSL_DEBUG(...) //ets_printf(__VA_ARGS__) + +#endif /* LIBRARIES_ESPASYNCTCP_SRC_ASYNC_CONFIG_H_ */ diff --git a/libraries/ESPAsyncTCP/src/tcp_axtls.c b/libraries/ESPAsyncTCP/src/tcp_axtls.c new file mode 100644 index 00000000..2a7b9663 --- /dev/null +++ b/libraries/ESPAsyncTCP/src/tcp_axtls.c @@ -0,0 +1,586 @@ +/* + Asynchronous TCP library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +/* + * Compatibility for AxTLS with LWIP raw tcp mode (http://lwip.wikia.com/wiki/Raw/TCP) + * Original Code and Inspiration: Slavey Karadzhov + */ +#include +#if ASYNC_TCP_SSL_ENABLED + +#include "lwip/opt.h" +#include "lwip/tcp.h" +#include "lwip/inet.h" +#include +#include +#include +#include + +uint8_t * default_private_key = NULL; +uint16_t default_private_key_len = 0; + +uint8_t * default_certificate = NULL; +uint16_t default_certificate_len = 0; + +static uint8_t _tcp_ssl_has_client = 0; + +SSL_CTX * tcp_ssl_new_server_ctx(const char *cert, const char *private_key_file, const char *password){ + uint32_t options = SSL_CONNECT_IN_PARTS; + SSL_CTX *ssl_ctx; + + if(private_key_file){ + options |= SSL_NO_DEFAULT_KEY; + } + + if ((ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_SVR_SESS)) == NULL){ + TCP_SSL_DEBUG("tcp_ssl_new_server_ctx: failed to allocate context\n"); + return NULL; + } + + if (private_key_file){ + int obj_type = SSL_OBJ_RSA_KEY; + if (strstr(private_key_file, ".p8")) + obj_type = SSL_OBJ_PKCS8; + else if (strstr(private_key_file, ".p12")) + obj_type = SSL_OBJ_PKCS12; + + if (ssl_obj_load(ssl_ctx, obj_type, private_key_file, password)){ + TCP_SSL_DEBUG("tcp_ssl_new_server_ctx: load private key '%s' failed\n", private_key_file); + return NULL; + } + } + + if (cert){ + if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, cert, NULL)){ + TCP_SSL_DEBUG("tcp_ssl_new_server_ctx: load certificate '%s' failed\n", cert); + return NULL; + } + } + return ssl_ctx; +} + +struct tcp_ssl_pcb { + struct tcp_pcb *tcp; + int fd; + SSL_CTX* ssl_ctx; + SSL *ssl; + uint8_t type; + int handshake; + void * arg; + tcp_ssl_data_cb_t on_data; + tcp_ssl_handshake_cb_t on_handshake; + tcp_ssl_error_cb_t on_error; + int last_wr; + struct pbuf *tcp_pbuf; + int pbuf_offset; + struct tcp_ssl_pcb * next; +}; + +typedef struct tcp_ssl_pcb tcp_ssl_t; + +static tcp_ssl_t * tcp_ssl_array = NULL; +static int tcp_ssl_next_fd = 0; + +uint8_t tcp_ssl_has_client(){ + return _tcp_ssl_has_client; +} + +tcp_ssl_t * tcp_ssl_new(struct tcp_pcb *tcp) { + + if(tcp_ssl_next_fd < 0){ + tcp_ssl_next_fd = 0;//overflow + } + + tcp_ssl_t * new_item = (tcp_ssl_t*)malloc(sizeof(tcp_ssl_t)); + if(!new_item){ + TCP_SSL_DEBUG("tcp_ssl_new: failed to allocate tcp_ssl\n"); + return NULL; + } + + new_item->tcp = tcp; + new_item->handshake = SSL_NOT_OK; + new_item->arg = NULL; + new_item->on_data = NULL; + new_item->on_handshake = NULL; + new_item->on_error = NULL; + new_item->tcp_pbuf = NULL; + new_item->pbuf_offset = 0; + new_item->next = NULL; + new_item->ssl_ctx = NULL; + new_item->ssl = NULL; + new_item->type = TCP_SSL_TYPE_CLIENT; + new_item->fd = tcp_ssl_next_fd++; + + if(tcp_ssl_array == NULL){ + tcp_ssl_array = new_item; + } else { + tcp_ssl_t * item = tcp_ssl_array; + while(item->next != NULL) + item = item->next; + item->next = new_item; + } + + TCP_SSL_DEBUG("tcp_ssl_new: %d\n", new_item->fd); + return new_item; +} + +tcp_ssl_t* tcp_ssl_get(struct tcp_pcb *tcp) { + if(tcp == NULL) { + return NULL; + } + tcp_ssl_t * item = tcp_ssl_array; + while(item && item->tcp != tcp){ + item = item->next; + } + return item; +} + +int tcp_ssl_new_client(struct tcp_pcb *tcp){ + SSL_CTX* ssl_ctx; + tcp_ssl_t * tcp_ssl; + + if(tcp == NULL) { + return -1; + } + + if(tcp_ssl_get(tcp) != NULL){ + TCP_SSL_DEBUG("tcp_ssl_new_client: tcp_ssl already exists\n"); + return -1; + } + + ssl_ctx = ssl_ctx_new(SSL_CONNECT_IN_PARTS | SSL_SERVER_VERIFY_LATER, 1); + if(ssl_ctx == NULL){ + TCP_SSL_DEBUG("tcp_ssl_new_client: failed to allocate ssl context\n"); + return -1; + } + + tcp_ssl = tcp_ssl_new(tcp); + if(tcp_ssl == NULL){ + ssl_ctx_free(ssl_ctx); + return -1; + } + + tcp_ssl->ssl_ctx = ssl_ctx; + + tcp_ssl->ssl = ssl_client_new(ssl_ctx, tcp_ssl->fd, NULL, 0, NULL); + if(tcp_ssl->ssl == NULL){ + TCP_SSL_DEBUG("tcp_ssl_new_client: failed to allocate ssl\n"); + tcp_ssl_free(tcp); + return -1; + } + + return tcp_ssl->fd; +} + +int tcp_ssl_new_server(struct tcp_pcb *tcp, SSL_CTX* ssl_ctx){ + tcp_ssl_t * tcp_ssl; + + if(tcp == NULL) { + return -1; + } + + if(ssl_ctx == NULL){ + return -1; + } + + if(tcp_ssl_get(tcp) != NULL){ + TCP_SSL_DEBUG("tcp_ssl_new_server: tcp_ssl already exists\n"); + return -1; + } + + tcp_ssl = tcp_ssl_new(tcp); + if(tcp_ssl == NULL){ + return -1; + } + + tcp_ssl->type = TCP_SSL_TYPE_SERVER; + tcp_ssl->ssl_ctx = ssl_ctx; + + _tcp_ssl_has_client = 1; + tcp_ssl->ssl = ssl_server_new(ssl_ctx, tcp_ssl->fd); + if(tcp_ssl->ssl == NULL){ + TCP_SSL_DEBUG("tcp_ssl_new_server: failed to allocate ssl\n"); + tcp_ssl_free(tcp); + return -1; + } + + return tcp_ssl->fd; +} + +int tcp_ssl_free(struct tcp_pcb *tcp) { + + if(tcp == NULL) { + return -1; + } + + tcp_ssl_t * item = tcp_ssl_array; + + if(item->tcp == tcp){ + tcp_ssl_array = tcp_ssl_array->next; + if(item->tcp_pbuf != NULL){ + pbuf_free(item->tcp_pbuf); + } + TCP_SSL_DEBUG("tcp_ssl_free: %d\n", item->fd); + if(item->ssl) + ssl_free(item->ssl); + if(item->type == TCP_SSL_TYPE_CLIENT && item->ssl_ctx) + ssl_ctx_free(item->ssl_ctx); + if(item->type == TCP_SSL_TYPE_SERVER) + _tcp_ssl_has_client = 0; + free(item); + return 0; + } + + while(item->next && item->next->tcp != tcp) + item = item->next; + + if(item->next == NULL){ + return ERR_TCP_SSL_INVALID_CLIENTFD_DATA;//item not found + } + + tcp_ssl_t * i = item->next; + item->next = i->next; + if(i->tcp_pbuf != NULL){ + pbuf_free(i->tcp_pbuf); + } + TCP_SSL_DEBUG("tcp_ssl_free: %d\n", i->fd); + if(i->ssl) + ssl_free(i->ssl); + if(i->type == TCP_SSL_TYPE_CLIENT && i->ssl_ctx) + ssl_ctx_free(i->ssl_ctx); + if(i->type == TCP_SSL_TYPE_SERVER) + _tcp_ssl_has_client = 0; + free(i); + return 0; +} + +#ifdef AXTLS_2_0_0_SNDBUF +int tcp_ssl_sndbuf(struct tcp_pcb *tcp){ + int expected; + int available; + int result = -1; + + if(tcp == NULL) { + return result; + } + tcp_ssl_t * tcp_ssl = tcp_ssl_get(tcp); + if(!tcp_ssl){ + TCP_SSL_DEBUG("tcp_ssl_sndbuf: tcp_ssl is NULL\n"); + return result; + } + available = tcp_sndbuf(tcp); + if(!available){ + TCP_SSL_DEBUG("tcp_ssl_sndbuf: tcp_sndbuf is zero\n"); + return 0; + } + result = available; + while((expected = ssl_calculate_write_length(tcp_ssl->ssl, result)) > available){ + result -= (expected - available) + 4; + } + + if(expected > 0){ + //TCP_SSL_DEBUG("tcp_ssl_sndbuf: tcp_sndbuf is %d from %d\n", result, available); + return result; + } + + return 0; +} +#endif + +int tcp_ssl_write(struct tcp_pcb *tcp, uint8_t *data, size_t len) { + if(tcp == NULL) { + return -1; + } + tcp_ssl_t * tcp_ssl = tcp_ssl_get(tcp); + if(!tcp_ssl){ + TCP_SSL_DEBUG("tcp_ssl_write: tcp_ssl is NULL\n"); + return 0; + } + tcp_ssl->last_wr = 0; + +#ifdef AXTLS_2_0_0_SNDBUF + int expected_len = ssl_calculate_write_length(tcp_ssl->ssl, len); + int available_len = tcp_sndbuf(tcp); + if(expected_len < 0 || expected_len > available_len){ + TCP_SSL_DEBUG("tcp_ssl_write: data will not fit! %u < %d(%u)\r\n", available_len, expected_len, len); + return -1; + } +#endif + + int rc = ssl_write(tcp_ssl->ssl, data, len); + + //TCP_SSL_DEBUG("tcp_ssl_write: %u -> %d (%d)\r\n", len, tcp_ssl->last_wr, rc); + + if (rc < 0){ + if(rc != SSL_CLOSE_NOTIFY) { + TCP_SSL_DEBUG("tcp_ssl_write error: %d\r\n", rc); + } + return rc; + } + + return tcp_ssl->last_wr; +} + +/** + * Reads data from the SSL over TCP stream. Returns decrypted data. + * @param tcp_pcb *tcp - pointer to the raw tcp object + * @param pbuf *p - pointer to the buffer with the TCP packet data + * + * @return int + * 0 - when everything is fine but there are no symbols to process yet + * < 0 - when there is an error + * > 0 - the length of the clear text characters that were read + */ +int tcp_ssl_read(struct tcp_pcb *tcp, struct pbuf *p) { + if(tcp == NULL) { + return -1; + } + tcp_ssl_t* fd_data = NULL; + + int read_bytes = 0; + int total_bytes = 0; + uint8_t *read_buf; + + fd_data = tcp_ssl_get(tcp); + if(fd_data == NULL) { + TCP_SSL_DEBUG("tcp_ssl_read: tcp_ssl is NULL\n"); + return ERR_TCP_SSL_INVALID_CLIENTFD_DATA; + } + + if(p == NULL) { + TCP_SSL_DEBUG("tcp_ssl_read:p == NULL\n"); + return ERR_TCP_SSL_INVALID_DATA; + } + + //TCP_SSL_DEBUG("READY TO READ SOME DATA\n"); + + fd_data->tcp_pbuf = p; + fd_data->pbuf_offset = 0; + + do { + read_bytes = ssl_read(fd_data->ssl, &read_buf); + //TCP_SSL_DEBUG("tcp_ssl_ssl_read: %d\n", read_bytes); + if(read_bytes < SSL_OK) { + if(read_bytes != SSL_CLOSE_NOTIFY) { + TCP_SSL_DEBUG("tcp_ssl_read: read error: %d\n", read_bytes); + } + total_bytes = read_bytes; + break; + } else if(read_bytes > 0){ + if(fd_data->on_data){ + fd_data->on_data(fd_data->arg, tcp, read_buf, read_bytes); + } + total_bytes+= read_bytes; + } else { + if(fd_data->handshake != SSL_OK) { + fd_data->handshake = ssl_handshake_status(fd_data->ssl); + if(fd_data->handshake == SSL_OK){ + //TCP_SSL_DEBUG("tcp_ssl_read: handshake OK\n"); + if(fd_data->on_handshake) + fd_data->on_handshake(fd_data->arg, fd_data->tcp, fd_data->ssl); + } else if(fd_data->handshake != SSL_NOT_OK){ + TCP_SSL_DEBUG("tcp_ssl_read: handshake error: %d\n", fd_data->handshake); + if(fd_data->on_error) + fd_data->on_error(fd_data->arg, fd_data->tcp, fd_data->handshake); + return fd_data->handshake; + } + } + } + } while (p->tot_len - fd_data->pbuf_offset > 0); + + tcp_recved(tcp, p->tot_len); + pbuf_free(p); + + return total_bytes; +} + +SSL * tcp_ssl_get_ssl(struct tcp_pcb *tcp){ + tcp_ssl_t * tcp_ssl = tcp_ssl_get(tcp); + if(tcp_ssl){ + return tcp_ssl->ssl; + } + return NULL; +} + +bool tcp_ssl_has(struct tcp_pcb *tcp){ + return tcp_ssl_get(tcp) != NULL; +} + +int tcp_ssl_is_server(struct tcp_pcb *tcp){ + tcp_ssl_t * tcp_ssl = tcp_ssl_get(tcp); + if(tcp_ssl){ + return tcp_ssl->type; + } + return -1; +} + +void tcp_ssl_arg(struct tcp_pcb *tcp, void * arg){ + tcp_ssl_t * item = tcp_ssl_get(tcp); + if(item) { + item->arg = arg; + } +} + +void tcp_ssl_data(struct tcp_pcb *tcp, tcp_ssl_data_cb_t arg){ + tcp_ssl_t * item = tcp_ssl_get(tcp); + if(item) { + item->on_data = arg; + } +} + +void tcp_ssl_handshake(struct tcp_pcb *tcp, tcp_ssl_handshake_cb_t arg){ + tcp_ssl_t * item = tcp_ssl_get(tcp); + if(item) { + item->on_handshake = arg; + } +} + +void tcp_ssl_err(struct tcp_pcb *tcp, tcp_ssl_error_cb_t arg){ + tcp_ssl_t * item = tcp_ssl_get(tcp); + if(item) { + item->on_error = arg; + } +} + +static tcp_ssl_file_cb_t _tcp_ssl_file_cb = NULL; +static void * _tcp_ssl_file_arg = NULL; + +void tcp_ssl_file(tcp_ssl_file_cb_t cb, void * arg){ + _tcp_ssl_file_cb = cb; + _tcp_ssl_file_arg = arg; +} + +int ax_get_file(const char *filename, uint8_t **buf) { + //TCP_SSL_DEBUG("ax_get_file: %s\n", filename); + if(_tcp_ssl_file_cb){ + return _tcp_ssl_file_cb(_tcp_ssl_file_arg, filename, buf); + } + *buf = 0; + return 0; +} + +tcp_ssl_t* tcp_ssl_get_by_fd(int fd) { + tcp_ssl_t * item = tcp_ssl_array; + while(item && item->fd != fd){ + item = item->next; + } + return item; +} +/* + * The LWIP tcp raw version of the SOCKET_WRITE(A, B, C) + */ +int ax_port_write(int fd, uint8_t *data, uint16_t len) { + tcp_ssl_t *fd_data = NULL; + int tcp_len = 0; + err_t err = ERR_OK; + + //TCP_SSL_DEBUG("ax_port_write: %d, %d\n", fd, len); + + fd_data = tcp_ssl_get_by_fd(fd); + if(fd_data == NULL) { + //TCP_SSL_DEBUG("ax_port_write: tcp_ssl[%d] is NULL\n", fd); + return ERR_MEM; + } + + if (data == NULL || len == 0) { + return 0; + } + + if (tcp_sndbuf(fd_data->tcp) < len) { + tcp_len = tcp_sndbuf(fd_data->tcp); + if(tcp_len == 0) { + TCP_SSL_DEBUG("ax_port_write: tcp_sndbuf is zero: %d\n", len); + return ERR_MEM; + } + } else { + tcp_len = len; + } + + if (tcp_len > 2 * fd_data->tcp->mss) { + tcp_len = 2 * fd_data->tcp->mss; + } + + err = tcp_write(fd_data->tcp, data, tcp_len, TCP_WRITE_FLAG_COPY); + if(err < ERR_OK) { + if (err == ERR_MEM) { + TCP_SSL_DEBUG("ax_port_write: No memory %d (%d)\n", tcp_len, len); + return err; + } + TCP_SSL_DEBUG("ax_port_write: tcp_write error: %d\n", err); + return err; + } else if (err == ERR_OK) { + //TCP_SSL_DEBUG("ax_port_write: tcp_output: %d / %d\n", tcp_len, len); + err = tcp_output(fd_data->tcp); + if(err != ERR_OK) { + TCP_SSL_DEBUG("ax_port_write: tcp_output err: %d\n", err); + return err; + } + } + + fd_data->last_wr += tcp_len; + + return tcp_len; +} + +/* + * The LWIP tcp raw version of the SOCKET_READ(A, B, C) + */ +int ax_port_read(int fd, uint8_t *data, int len) { + tcp_ssl_t *fd_data = NULL; + uint8_t *read_buf = NULL; + uint8_t *pread_buf = NULL; + u16_t recv_len = 0; + + //TCP_SSL_DEBUG("ax_port_read: %d, %d\n", fd, len); + + fd_data = tcp_ssl_get_by_fd(fd); + if (fd_data == NULL) { + TCP_SSL_DEBUG("ax_port_read: tcp_ssl[%d] is NULL\n", fd); + return ERR_TCP_SSL_INVALID_CLIENTFD_DATA; + } + + if(fd_data->tcp_pbuf == NULL || fd_data->tcp_pbuf->tot_len == 0) { + return 0; + } + + read_buf =(uint8_t*)calloc(fd_data->tcp_pbuf->len + 1, sizeof(uint8_t)); + pread_buf = read_buf; + if (pread_buf != NULL){ + recv_len = pbuf_copy_partial(fd_data->tcp_pbuf, read_buf, len, fd_data->pbuf_offset); + fd_data->pbuf_offset += recv_len; + } + + if (recv_len != 0) { + memcpy(data, read_buf, recv_len); + } + + if(len < recv_len) { + TCP_SSL_DEBUG("ax_port_read: got %d bytes more than expected\n", recv_len - len); + } + + free(pread_buf); + pread_buf = NULL; + + return recv_len; +} + +void ax_wdt_feed() {} + +#endif diff --git a/libraries/ESPAsyncTCP/src/tcp_axtls.h b/libraries/ESPAsyncTCP/src/tcp_axtls.h new file mode 100644 index 00000000..57a0e52c --- /dev/null +++ b/libraries/ESPAsyncTCP/src/tcp_axtls.h @@ -0,0 +1,97 @@ +/* + Asynchronous TCP library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +/* + * Compatibility for AxTLS with LWIP raw tcp mode (http://lwip.wikia.com/wiki/Raw/TCP) + * Original Code and Inspiration: Slavey Karadzhov + */ + +#ifndef LWIPR_COMPAT_H +#define LWIPR_COMPAT_H + +#include + +#if ASYNC_TCP_SSL_ENABLED + +#include "lwipopts.h" +/* + * All those functions will run only if LWIP tcp raw mode is used + */ +#if LWIP_RAW==1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include "include/ssl.h" + +#define ERR_TCP_SSL_INVALID_SSL -101 +#define ERR_TCP_SSL_INVALID_TCP -102 +#define ERR_TCP_SSL_INVALID_CLIENTFD -103 +#define ERR_TCP_SSL_INVALID_CLIENTFD_DATA -104 +#define ERR_TCP_SSL_INVALID_DATA -105 + +#define TCP_SSL_TYPE_CLIENT 0 +#define TCP_SSL_TYPE_SERVER 1 + +#define tcp_ssl_ssl_write(A, B, C) tcp_ssl_write(A, B, C) +#define tcp_ssl_ssl_read(A, B) tcp_ssl_read(A, B) + +typedef void (* tcp_ssl_data_cb_t)(void *arg, struct tcp_pcb *tcp, uint8_t * data, size_t len); +typedef void (* tcp_ssl_handshake_cb_t)(void *arg, struct tcp_pcb *tcp, SSL *ssl); +typedef void (* tcp_ssl_error_cb_t)(void *arg, struct tcp_pcb *tcp, int8_t error); +typedef int (* tcp_ssl_file_cb_t)(void *arg, const char *filename, uint8_t **buf); + +uint8_t tcp_ssl_has_client(); + +int tcp_ssl_new_client(struct tcp_pcb *tcp); + +SSL_CTX * tcp_ssl_new_server_ctx(const char *cert, const char *private_key_file, const char *password); +int tcp_ssl_new_server(struct tcp_pcb *tcp, SSL_CTX* ssl_ctx); +int tcp_ssl_is_server(struct tcp_pcb *tcp); + +int tcp_ssl_free(struct tcp_pcb *tcp); +int tcp_ssl_read(struct tcp_pcb *tcp, struct pbuf *p); + +#ifdef AXTLS_2_0_0_SNDBUF +int tcp_ssl_sndbuf(struct tcp_pcb *tcp); +#endif + +int tcp_ssl_write(struct tcp_pcb *tcp, uint8_t *data, size_t len); + +void tcp_ssl_file(tcp_ssl_file_cb_t cb, void * arg); + +void tcp_ssl_arg(struct tcp_pcb *tcp, void * arg); +void tcp_ssl_data(struct tcp_pcb *tcp, tcp_ssl_data_cb_t arg); +void tcp_ssl_handshake(struct tcp_pcb *tcp, tcp_ssl_handshake_cb_t arg); +void tcp_ssl_err(struct tcp_pcb *tcp, tcp_ssl_error_cb_t arg); + +SSL * tcp_ssl_get_ssl(struct tcp_pcb *tcp); +bool tcp_ssl_has(struct tcp_pcb *tcp); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_RAW==1 */ + +#endif /* ASYNC_TCP_SSL_ENABLED */ + +#endif /* LWIPR_COMPAT_H */ diff --git a/libraries/ESPAsyncTCP/ssl/gen_server_cert.sh b/libraries/ESPAsyncTCP/ssl/gen_server_cert.sh new file mode 100644 index 00000000..fd749ed4 --- /dev/null +++ b/libraries/ESPAsyncTCP/ssl/gen_server_cert.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +cat > ca_cert.conf << EOF +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] + O = Espressif Systems +EOF + +openssl genrsa -out axTLS.ca_key.pem 2048 +openssl req -new -config ./ca_cert.conf -key axTLS.ca_key.pem -out axTLS.ca_x509.req +openssl x509 -req -sha1 -days 5000 -signkey axTLS.ca_key.pem -CAkey axTLS.ca_key.pem -in axTLS.ca_x509.req -out axTLS.ca_x509.pem + +cat > certs.conf << EOF +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] + O = axTLS on ESP8266 + CN = esp8266.local +EOF + +openssl genrsa -out axTLS.key_1024.pem 1024 +openssl req -new -config ./certs.conf -key axTLS.key_1024.pem -out axTLS.x509_1024.req +openssl x509 -req -sha1 -CAcreateserial -days 5000 -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem -in axTLS.x509_1024.req -out axTLS.x509_1024.pem + +openssl rsa -outform DER -in axTLS.key_1024.pem -out axTLS.key_1024 +openssl x509 -outform DER -in axTLS.x509_1024.pem -out axTLS.x509_1024.cer + +cat axTLS.key_1024 > server.key +cat axTLS.x509_1024.cer > server.cer + +rm axTLS.* ca_cert.conf certs.conf diff --git a/libraries/ESPAsyncTCP/ssl/server.cer b/libraries/ESPAsyncTCP/ssl/server.cer new file mode 100644 index 00000000..b5e5f248 Binary files /dev/null and b/libraries/ESPAsyncTCP/ssl/server.cer differ diff --git a/libraries/ESPAsyncTCP/ssl/server.key b/libraries/ESPAsyncTCP/ssl/server.key new file mode 100644 index 00000000..1b7095f9 Binary files /dev/null and b/libraries/ESPAsyncTCP/ssl/server.key differ diff --git a/libraries/ESPAsyncTCP/travis/common.sh b/libraries/ESPAsyncTCP/travis/common.sh new file mode 100644 index 00000000..57bede34 --- /dev/null +++ b/libraries/ESPAsyncTCP/travis/common.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +function build_sketches() +{ + local arduino=$1 + local srcpath=$2 + local platform=$3 + local sketches=$(find $srcpath -name *.ino) + for sketch in $sketches; do + local sketchdir=$(dirname $sketch) + if [[ -f "$sketchdir/.$platform.skip" ]]; then + echo -e "\n\n ------------ Skipping $sketch ------------ \n\n"; + continue + fi + echo -e "\n\n ------------ Building $sketch ------------ \n\n"; + $arduino --verify $sketch; + local result=$? + if [ $result -ne 0 ]; then + echo "Build failed ($1)" + return $result + fi + done +} diff --git a/libraries/ESPAsyncWebServer/.travis.yml b/libraries/ESPAsyncWebServer/.travis.yml new file mode 100644 index 00000000..ce0228f4 --- /dev/null +++ b/libraries/ESPAsyncWebServer/.travis.yml @@ -0,0 +1,39 @@ +sudo: false +language: bash +os: + - linux + +script: + - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16 + - sleep 3 + - export DISPLAY=:1.0 + - wget http://downloads.arduino.cc/arduino-1.6.5-linux64.tar.xz + - tar xf arduino-1.6.5-linux64.tar.xz + - mv arduino-1.6.5 $HOME/arduino_ide + - export PATH="$HOME/arduino_ide:$PATH" + - which arduino + - mkdir -p $HOME/Arduino/libraries + - cp -r $TRAVIS_BUILD_DIR $HOME/Arduino/libraries/ESPAsyncWebServer + - git clone https://github.com/bblanchon/ArduinoJson $HOME/Arduino/libraries/ArduinoJson + - git clone https://github.com/me-no-dev/ESPAsyncTCP $HOME/Arduino/libraries/ESPAsyncTCP + - cd $HOME/arduino_ide/hardware + - mkdir esp8266com + - cd esp8266com + - git clone https://github.com/esp8266/Arduino.git esp8266 + - cd esp8266/tools + - python get.py + - source $TRAVIS_BUILD_DIR/travis/common.sh + - arduino --board esp8266com:esp8266:generic --save-prefs + - arduino --get-pref sketchbook.path + - build_sketches arduino $HOME/Arduino/libraries/ESPAsyncWebServer esp8266 + +notifications: + email: + on_success: change + on_failure: change + webhooks: + urls: + - https://webhooks.gitter.im/e/60e65d0c78ea0a920347 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: false # default: false diff --git a/libraries/ESPAsyncWebServer/README.md b/libraries/ESPAsyncWebServer/README.md new file mode 100644 index 00000000..b58a9021 --- /dev/null +++ b/libraries/ESPAsyncWebServer/README.md @@ -0,0 +1,1368 @@ +# ESPAsyncWebServer [![Build Status](https://travis-ci.org/me-no-dev/ESPAsyncWebServer.svg?branch=master)](https://travis-ci.org/me-no-dev/ESPAsyncWebServer) + +For help and support [![Join the chat at https://gitter.im/me-no-dev/ESPAsyncWebServer](https://badges.gitter.im/me-no-dev/ESPAsyncWebServer.svg)](https://gitter.im/me-no-dev/ESPAsyncWebServer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +Async HTTP and WebSocket Server for ESP8266 Arduino + +For ESP8266 it requires [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) +To use this library you might need to have the latest git versions of [ESP8266](https://github.com/esp8266/Arduino) Arduino Core + +For ESP32 it requires [AsyncTCP](https://github.com/me-no-dev/AsyncTCP) to work +To use this library you might need to have the latest git versions of [ESP32](https://github.com/espressif/arduino-esp32) Arduino Core + +## Table of contents +- [ESPAsyncWebServer ](#espasyncwebserver-) + - [Why should you care](#why-should-you-care) + - [Important things to remember](#important-things-to-remember) + - [Principles of operation](#principles-of-operation) + - [The Async Web server](#the-async-web-server) + - [Request Life Cycle](#request-life-cycle) + - [Rewrites and how do they work](#rewrites-and-how-do-they-work) + - [Handlers and how do they work](#handlers-and-how-do-they-work) + - [Responses and how do they work](#responses-and-how-do-they-work) + - [Template processing](#template-processing) + - [Libraries and projects that use AsyncWebServer](#libraries-and-projects-that-use-asyncwebserver) + - [Request Variables](#request-variables) + - [Common Variables](#common-variables) + - [Headers](#headers) + - [GET, POST and FILE parameters](#get-post-and-file-parameters) + - [FILE Upload handling](#file-upload-handling) + - [Body data handling](#body-data-handling) + - [Responses](#responses) + - [Redirect to another URL](#redirect-to-another-url) + - [Basic response with HTTP Code](#basic-response-with-http-code) + - [Basic response with HTTP Code and extra headers](#basic-response-with-http-code-and-extra-headers) + - [Basic response with string content](#basic-response-with-string-content) + - [Basic response with string content and extra headers](#basic-response-with-string-content-and-extra-headers) + - [Send large webpage from PROGMEM](#send-large-webpage-from-progmem) + - [Send large webpage from PROGMEM and extra headers](#send-large-webpage-from-progmem-and-extra-headers) + - [Send large webpage from PROGMEM containing templates](#send-large-webpage-from-progmem-containing-templates) + - [Send large webpage from PROGMEM containing templates and extra headers](#send-large-webpage-from-progmem-containing-templates-and-extra-headers) + - [Send binary content from PROGMEM](#send-binary-content-from-progmem) + - [Respond with content coming from a Stream](#respond-with-content-coming-from-a-stream) + - [Respond with content coming from a Stream and extra headers](#respond-with-content-coming-from-a-stream-and-extra-headers) + - [Respond with content coming from a Stream containing templates](#respond-with-content-coming-from-a-stream-containing-templates) + - [Respond with content coming from a Stream containing templates and extra headers](#respond-with-content-coming-from-a-stream-containing-templates-and-extra-headers) + - [Respond with content coming from a File](#respond-with-content-coming-from-a-file) + - [Respond with content coming from a File and extra headers](#respond-with-content-coming-from-a-file-and-extra-headers) + - [Respond with content coming from a File containing templates](#respond-with-content-coming-from-a-file-containing-templates) + - [Respond with content using a callback](#respond-with-content-using-a-callback) + - [Respond with content using a callback and extra headers](#respond-with-content-using-a-callback-and-extra-headers) + - [Respond with content using a callback containing templates](#respond-with-content-using-a-callback-containing-templates) + - [Respond with content using a callback containing templates and extra headers](#respond-with-content-using-a-callback-containing-templates-and-extra-headers) + - [Chunked Response](#chunked-response) + - [Print to response](#print-to-response) + - [ArduinoJson Basic Response](#arduinojson-basic-response) + - [ArduinoJson Advanced Response](#arduinojson-advanced-response) + - [Serving static files](#serving-static-files) + - [Serving specific file by name](#serving-specific-file-by-name) + - [Serving files in directory](#serving-files-in-directory) + - [Specifying Cache-Control header](#specifying-cache-control-header) + - [Specifying Date-Modified header](#specifying-date-modified-header) + - [Specifying Template Processor callback](#specifying-template-processor-callback) + - [Using filters](#using-filters) + - [Serve different site files in AP mode](#serve-different-site-files-in-ap-mode) + - [Rewrite to different index on AP](#rewrite-to-different-index-on-ap) + - [Serving different hosts](#serving-different-hosts) + - [Bad Responses](#bad-responses) + - [Respond with content using a callback without content length to HTTP/1.0 clients](#respond-with-content-using-a-callback-without-content-length-to-http10-clients) + - [Async WebSocket Plugin](#async-websocket-plugin) + - [Async WebSocket Event](#async-websocket-event) + - [Methods for sending data to a socket client](#methods-for-sending-data-to-a-socket-client) + - [Async Event Source Plugin](#async-event-source-plugin) + - [Setup Event Source on the server](#setup-event-source-on-the-server) + - [Setup Event Source in the browser](#setup-event-source-in-the-browser) + - [Scanning for available WiFi Networks](#scanning-for-available-wifi-networks) + - [Remove handlers and rewrites](#remove-handlers-and-rewrites) + - [Setting up the server](#setting-up-the-server) + - [Setup global and class functions as request handlers](#setup-global-and-class-functions-as-request-handlers) + - [Methods for controlling websocket connections](#methods-for-controlling-websocket-connections) + - [Adding default headers to all responses](#adding-default-headers) +## Why should you care +- Using asynchronous network means that you can handle more than one connection at the same time +- You are called once the request is ready and parsed +- When you send the response, you are immediately ready to handle other connections + while the server is taking care of sending the response in the background +- Speed is OMG +- Easy to use API, HTTP Basic and Digest MD5 Authentication (default), ChunkedResponse +- Easily extendible to handle any type of content +- Supports Continue 100 +- Async WebSocket plugin offering different locations without extra servers or ports +- Async EventSource (Server-Sent Events) plugin to send events to the browser +- URL Rewrite plugin for conditional and permanent url rewrites +- ServeStatic plugin that supports cache, Last-Modified, default index and more +- Simple template processing engine to handle templates + +## Important things to remember +- This is fully asynchronous server and as such does not run on the loop thread. +- You can not use yield or delay or any function that uses them inside the callbacks +- The server is smart enough to know when to close the connection and free resources +- You can not send more than one response to a single request + +## Principles of operation + +### The Async Web server +- Listens for connections +- Wraps the new clients into ```Request``` +- Keeps track of clients and cleans memory +- Manages ```Rewrites``` and apply them on the request url +- Manages ```Handlers``` and attaches them to Requests + +### Request Life Cycle +- TCP connection is received by the server +- The connection is wrapped inside ```Request``` object +- When the request head is received (type, url, get params, http version and host), + the server goes through all ```Rewrites``` (in the order they were added) to rewrite the url and inject query parameters, + next, it goes through all attached ```Handlers```(in the order they were added) trying to find one + that ```canHandle``` the given request. If none are found, the default(catch-all) handler is attached. +- The rest of the request is received, calling the ```handleUpload``` or ```handleBody``` methods of the ```Handler``` if they are needed (POST+File/Body) +- When the whole request is parsed, the result is given to the ```handleRequest``` method of the ```Handler``` and is ready to be responded to +- In the ```handleRequest``` method, to the ```Request``` is attached a ```Response``` object (see below) that will serve the response data back to the client +- When the ```Response``` is sent, the client is closed and freed from the memory + +### Rewrites and how do they work +- The ```Rewrites``` are used to rewrite the request url and/or inject get parameters for a specific request url path. +- All ```Rewrites``` are evaluated on the request in the order they have been added to the server. +- The ```Rewrite``` will change the request url only if the request url (excluding get parameters) is fully match + the rewrite url, and when the optional ```Filter``` callback return true. +- Setting a ```Filter``` to the ```Rewrite``` enables to control when to apply the rewrite, decision can be based on + request url, http version, request host/port/target host, get parameters or the request client's localIP or remoteIP. +- Two filter callbacks are provided: ```ON_AP_FILTER``` to execute the rewrite when request is made to the AP interface, + ```ON_STA_FILTER``` to execute the rewrite when request is made to the STA interface. +- The ```Rewrite``` can specify a target url with optional get parameters, e.g. ```/to-url?with=params``` + +### Handlers and how do they work +- The ```Handlers``` are used for executing specific actions to particular requests +- One ```Handler``` instance can be attached to any request and lives together with the server +- Setting a ```Filter``` to the ```Handler``` enables to control when to apply the handler, decision can be based on + request url, http version, request host/port/target host, get parameters or the request client's localIP or remoteIP. +- Two filter callbacks are provided: ```ON_AP_FILTER``` to execute the rewrite when request is made to the AP interface, + ```ON_STA_FILTER``` to execute the rewrite when request is made to the STA interface. +- The ```canHandle``` method is used for handler specific control on whether the requests can be handled + and for declaring any interesting headers that the ```Request``` should parse. Decision can be based on request + method, request url, http version, request host/port/target host and get parameters +- Once a ```Handler``` is attached to given ```Request``` (```canHandle``` returned true) + that ```Handler``` takes care to receive any file/data upload and attach a ```Response``` + once the ```Request``` has been fully parsed +- ```Handlers``` are evaluated in the order they are attached to the server. The ```canHandle``` is called only + if the ```Filter``` that was set to the ```Handler``` return true. +- The first ```Handler``` that can handle the request is selected, not further ```Filter``` and ```canHandle``` are called. + +### Responses and how do they work +- The ```Response``` objects are used to send the response data back to the client +- The ```Response``` object lives with the ```Request``` and is freed on end or disconnect +- Different techniques are used depending on the response type to send the data in packets + returning back almost immediately and sending the next packet when this one is received. + Any time in between is spent to run the user loop and handle other network packets +- Responding asynchronously is probably the most difficult thing for most to understand +- Many different options exist for the user to make responding a background task + +### Template processing +- ESPAsyncWebserver contains simple template processing engine. +- Template processing can be added to most response types. +- Currently it supports only replacing template placeholders with actual values. No conditional processing, cycles, etc. +- Placeholders are delimited with ```%``` symbols. Like this: ```%TEMPLATE_PLACEHOLDER%```. +- It works by extracting placeholder name from response text and passing it to user provided function which should return actual value to be used instead of placeholder. +- Since it's user provided function, it is possible for library users to implement conditional processing and cycles themselves. +- Since it's impossible to know the actual response size after template processing step in advance (and, therefore, to include it in response headers), the response becomes [chunked](#chunked-response). + +## Libraries and projects that use AsyncWebServer +- [WebSocketToSerial](https://github.com/hallard/WebSocketToSerial) - Debug serial devices through the web browser +- [Sattrack](https://github.com/Hopperpop/Sattrack) - Track the ISS with ESP8266 +- [ESP Radio](https://github.com/Edzelf/Esp-radio) - Icecast radio based on ESP8266 and VS1053 +- [VZero](https://github.com/andig/vzero) - the Wireless zero-config controller for volkszaehler.org +- [ESPurna](https://bitbucket.org/xoseperez/espurna) - ESPurna ("spark" in Catalan) is a custom C firmware for ESP8266 based smart switches. It was originally developed with the ITead Sonoff in mind. +- [fauxmoESP](https://bitbucket.org/xoseperez/fauxmoesp) - Belkin WeMo emulator library for ESP8266. +- [ESP-RFID](https://github.com/omersiar/esp-rfid) - MFRC522 RFID Access Control Management project for ESP8266. + +## Request Variables + +### Common Variables +```cpp +request->version(); // uint8_t: 0 = HTTP/1.0, 1 = HTTP/1.1 +request->method(); // enum: HTTP_GET, HTTP_POST, HTTP_DELETE, HTTP_PUT, HTTP_PATCH, HTTP_HEAD, HTTP_OPTIONS +request->url(); // String: URL of the request (not including host, port or GET parameters) +request->host(); // String: The requested host (can be used for virtual hosting) +request->contentType(); // String: ContentType of the request (not avaiable in Handler::canHandle) +request->contentLength(); // size_t: ContentLength of the request (not avaiable in Handler::canHandle) +request->multipart(); // bool: True if the request has content type "multipart" +``` + +### Headers +```cpp +//List all collected headers +int headers = request->headers(); +int i; +for(i=0;igetHeader(i); + Serial.printf("HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); +} + +//get specific header by name +if(request->hasHeader("MyHeader")){ + AsyncWebHeader* h = request->getHeader("MyHeader"); + Serial.printf("MyHeader: %s\n", h->value().c_str()); +} + +//List all collected headers (Compatibility) +int headers = request->headers(); +int i; +for(i=0;iheaderName(i).c_str(), request->header(i).c_str()); +} + +//get specific header by name (Compatibility) +if(request->hasHeader("MyHeader")){ + Serial.printf("MyHeader: %s\n", request->header("MyHeader").c_str()); +} +``` + +### GET, POST and FILE parameters +```cpp +//List all parameters +int params = request->params(); +for(int i=0;igetParam(i); + if(p->isFile()){ //p->isPost() is also true + Serial.printf("FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); + } else if(p->isPost()){ + Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); + } else { + Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); + } +} + +//Check if GET parameter exists +if(request->hasParam("download")) + AsyncWebParameter* p = request->getParam("download"); + +//Check if POST (but not File) parameter exists +if(request->hasParam("download", true)) + AsyncWebParameter* p = request->getParam("download", true); + +//Check if FILE was uploaded +if(request->hasParam("download", true, true)) + AsyncWebParameter* p = request->getParam("download", true, true); + +//List all parameters (Compatibility) +int args = request->args(); +for(int i=0;iargName(i).c_str(), request->arg(i).c_str()); +} + +//Check if parameter exists (Compatibility) +if(request->hasArg("download")) + String arg = request->arg("download"); +``` + +### FILE Upload handling +```cpp +void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ + if(!index){ + Serial.printf("UploadStart: %s\n", filename.c_str()); + } + for(size_t i=0; iredirect("/login"); + +//to external url +request->redirect("http://esp8266.com"); +``` + +### Basic response with HTTP Code +```cpp +request->send(404); //Sends 404 File Not Found +``` + +### Basic response with HTTP Code and extra headers +```cpp +AsyncWebServerResponse *response = request->beginResponse(404); //Sends 404 File Not Found +response->addHeader("Server","ESP Async Web Server"); +request->send(response); +``` + +### Basic response with string content +```cpp +request->send(200, "text/plain", "Hello World!"); +``` + +### Basic response with string content and extra headers +```cpp +AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", "Hello World!"); +response->addHeader("Server","ESP Async Web Server"); +request->send(response); +``` + +### Send large webpage from PROGMEM +```cpp +const char index_html[] PROGMEM = "..."; // large char array, tested with 14k +request->send_P(200, "text/html", index_html); +``` + +### Send large webpage from PROGMEM and extra headers +```cpp +const char index_html[] PROGMEM = "..."; // large char array, tested with 14k +AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", index_html); +response->addHeader("Server","ESP Async Web Server"); +request->send(response); +``` + +### Send large webpage from PROGMEM containing templates +```cpp +String processor(const String& var) +{ + if(var == "HELLO_FROM_TEMPLATE") + return F("Hello world!"); + return String(); +} + +// ... + +const char index_html[] PROGMEM = "..."; // large char array, tested with 14k +request->send_P(200, "text/html", index_html, processor); +``` + +### Send large webpage from PROGMEM containing templates and extra headers +```cpp +String processor(const String& var) +{ + if(var == "HELLO_FROM_TEMPLATE") + return F("Hello world!"); + return String(); +} + +// ... + +const char index_html[] PROGMEM = "..."; // large char array, tested with 14k +AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", index_html, processor); +response->addHeader("Server","ESP Async Web Server"); +request->send(response); +``` + +### Send binary content from PROGMEM +```cpp + +//File: favicon.ico.gz, Size: 726 +#define favicon_ico_gz_len 726 +const uint8_t favicon_ico_gz[] PROGMEM = { + 0x1F, 0x8B, 0x08, 0x08, 0x0B, 0x87, 0x90, 0x57, 0x00, 0x03, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6F, + 0x6E, 0x2E, 0x69, 0x63, 0x6F, 0x00, 0xCD, 0x53, 0x5F, 0x48, 0x9A, 0x51, 0x14, 0xBF, 0x62, 0x6D, + 0x86, 0x96, 0xA9, 0x64, 0xD3, 0xFE, 0xA8, 0x99, 0x65, 0x1A, 0xB4, 0x8A, 0xA8, 0x51, 0x54, 0x23, + 0xA8, 0x11, 0x49, 0x51, 0x8A, 0x34, 0x62, 0x93, 0x85, 0x31, 0x58, 0x44, 0x12, 0x45, 0x2D, 0x58, + 0xF5, 0x52, 0x41, 0x10, 0x23, 0x82, 0xA0, 0x20, 0x98, 0x2F, 0xC1, 0x26, 0xED, 0xA1, 0x20, 0x89, + 0x04, 0xD7, 0x83, 0x58, 0x20, 0x28, 0x04, 0xAB, 0xD1, 0x9B, 0x8C, 0xE5, 0xC3, 0x60, 0x32, 0x64, + 0x0E, 0x56, 0xBF, 0x9D, 0xEF, 0xF6, 0x30, 0x82, 0xED, 0xAD, 0x87, 0xDD, 0x8F, 0xF3, 0xDD, 0x8F, + 0x73, 0xCF, 0xEF, 0x9C, 0xDF, 0x39, 0xBF, 0xFB, 0x31, 0x26, 0xA2, 0x27, 0x37, 0x97, 0xD1, 0x5B, + 0xCF, 0x9E, 0x67, 0x30, 0xA6, 0x66, 0x8C, 0x99, 0xC9, 0xC8, 0x45, 0x9E, 0x6B, 0x3F, 0x5F, 0x74, + 0xA6, 0x94, 0x5E, 0xDB, 0xFF, 0xB2, 0xE6, 0xE7, 0xE7, 0xF9, 0xDE, 0xD6, 0xD6, 0x96, 0xDB, 0xD8, + 0xD8, 0x78, 0xBF, 0xA1, 0xA1, 0xC1, 0xDA, 0xDC, 0xDC, 0x2C, 0xEB, 0xED, 0xED, 0x15, 0x9B, 0xCD, + 0xE6, 0x4A, 0x83, 0xC1, 0xE0, 0x2E, 0x29, 0x29, 0x99, 0xD6, 0x6A, 0xB5, 0x4F, 0x75, 0x3A, 0x9D, + 0x61, 0x75, 0x75, 0x95, 0xB5, 0xB7, 0xB7, 0xDF, 0xC8, 0xD1, 0xD4, 0xD4, 0xF4, 0xB0, 0xBA, 0xBA, + 0xFA, 0x83, 0xD5, 0x6A, 0xFD, 0x5A, 0x5E, 0x5E, 0x9E, 0x28, 0x2D, 0x2D, 0x0D, 0x10, 0xC6, 0x4B, + 0x98, 0x78, 0x5E, 0x5E, 0xDE, 0x95, 0x42, 0xA1, 0x40, 0x4E, 0x4E, 0xCE, 0x65, 0x76, 0x76, 0xF6, + 0x47, 0xB5, 0x5A, 0x6D, 0x4F, 0x26, 0x93, 0xA2, 0xD6, 0xD6, 0x56, 0x8E, 0x6D, 0x69, 0x69, 0xD1, + 0x11, 0x36, 0x62, 0xB1, 0x58, 0x60, 0x32, 0x99, 0xA0, 0xD7, 0xEB, 0x51, 0x58, 0x58, 0x88, 0xFC, + 0xFC, 0x7C, 0x10, 0x16, 0x02, 0x56, 0x2E, 0x97, 0x43, 0x2A, 0x95, 0x42, 0x2C, 0x16, 0x23, 0x33, + 0x33, 0x33, 0xAE, 0x52, 0xA9, 0x1E, 0x64, 0x65, 0x65, 0x71, 0x7C, 0x7D, 0x7D, 0xBD, 0x93, 0xEA, + 0xFE, 0x30, 0x1A, 0x8D, 0xE8, 0xEC, 0xEC, 0xC4, 0xE2, 0xE2, 0x22, 0x6A, 0x6A, 0x6A, 0x40, 0x39, + 0x41, 0xB5, 0x38, 0x4E, 0xC8, 0x33, 0x3C, 0x3C, 0x0C, 0x87, 0xC3, 0xC1, 0x6B, 0x54, 0x54, 0x54, + 0xBC, 0xE9, 0xEB, 0xEB, 0x93, 0x5F, 0x5C, 0x5C, 0x30, 0x8A, 0x9D, 0x2E, 0x2B, 0x2B, 0xBB, 0xA2, + 0x3E, 0x41, 0xBD, 0x21, 0x1E, 0x8F, 0x63, 0x6A, 0x6A, 0x0A, 0x81, 0x40, 0x00, 0x94, 0x1B, 0x3D, + 0x3D, 0x3D, 0x42, 0x3C, 0x96, 0x96, 0x96, 0x70, 0x7E, 0x7E, 0x8E, 0xE3, 0xE3, 0x63, 0xF8, 0xFD, + 0xFE, 0xB4, 0xD7, 0xEB, 0xF5, 0x8F, 0x8F, 0x8F, 0x5B, 0x68, 0x5E, 0x6F, 0x05, 0xCE, 0xB4, 0xE3, + 0xE8, 0xE8, 0x08, 0x27, 0x27, 0x27, 0xD8, 0xDF, 0xDF, 0xC7, 0xD9, 0xD9, 0x19, 0x6C, 0x36, 0x1B, + 0x36, 0x36, 0x36, 0x38, 0x9F, 0x85, 0x85, 0x05, 0xAC, 0xAF, 0xAF, 0x23, 0x1A, 0x8D, 0x22, 0x91, + 0x48, 0x20, 0x16, 0x8B, 0xFD, 0xDA, 0xDA, 0xDA, 0x7A, 0x41, 0x33, 0x7E, 0x57, 0x50, 0x50, 0x80, + 0x89, 0x89, 0x09, 0x84, 0xC3, 0x61, 0x6C, 0x6F, 0x6F, 0x23, 0x12, 0x89, 0xE0, 0xE0, 0xE0, 0x00, + 0x43, 0x43, 0x43, 0x58, 0x5E, 0x5E, 0xE6, 0x9C, 0x7D, 0x3E, 0x1F, 0x46, 0x47, 0x47, 0x79, 0xBE, + 0xBD, 0xBD, 0x3D, 0xE1, 0x3C, 0x1D, 0x0C, 0x06, 0x9F, 0x10, 0xB7, 0xC7, 0x84, 0x4F, 0xF6, 0xF7, + 0xF7, 0x63, 0x60, 0x60, 0x00, 0x83, 0x83, 0x83, 0x18, 0x19, 0x19, 0xC1, 0xDC, 0xDC, 0x1C, 0x8F, + 0x17, 0x7C, 0xA4, 0x27, 0xE7, 0x34, 0x39, 0x39, 0x89, 0x9D, 0x9D, 0x1D, 0x6E, 0x54, 0xE3, 0x13, + 0xE5, 0x34, 0x11, 0x37, 0x49, 0x51, 0x51, 0xD1, 0x4B, 0xA5, 0x52, 0xF9, 0x45, 0x26, 0x93, 0x5D, + 0x0A, 0xF3, 0x92, 0x48, 0x24, 0xA0, 0x6F, 0x14, 0x17, 0x17, 0xA3, 0xB6, 0xB6, 0x16, 0x5D, 0x5D, + 0x5D, 0x7C, 0x1E, 0xBB, 0xBB, 0xBB, 0x9C, 0xD7, 0xE1, 0xE1, 0x21, 0x42, 0xA1, 0xD0, 0x6B, 0xD2, + 0x45, 0x4C, 0x33, 0x12, 0x34, 0xCC, 0xA0, 0x19, 0x54, 0x92, 0x56, 0x0E, 0xD2, 0xD9, 0x43, 0xF8, + 0xCF, 0x82, 0x56, 0xC2, 0xDC, 0xEB, 0xEA, 0xEA, 0x38, 0x7E, 0x6C, 0x6C, 0x4C, 0xE0, 0xFE, 0x9D, + 0xB8, 0xBF, 0xA7, 0xFA, 0xAF, 0x56, 0x56, 0x56, 0xEE, 0x6D, 0x6E, 0x6E, 0xDE, 0xB8, 0x47, 0x55, + 0x55, 0x55, 0x6C, 0x66, 0x66, 0x46, 0x44, 0xDA, 0x3B, 0x34, 0x1A, 0x4D, 0x94, 0xB0, 0x3F, 0x09, + 0x7B, 0x45, 0xBD, 0xA5, 0x5D, 0x2E, 0x57, 0x8C, 0x7A, 0x73, 0xD9, 0xED, 0xF6, 0x3B, 0x84, 0xFF, + 0xE7, 0x7D, 0xA6, 0x3A, 0x2C, 0x95, 0x4A, 0xB1, 0x8E, 0x8E, 0x0E, 0x6D, 0x77, 0x77, 0xB7, 0xCD, + 0xE9, 0x74, 0x3E, 0x73, 0xBB, 0xDD, 0x8F, 0x3C, 0x1E, 0x8F, 0xE6, 0xF4, 0xF4, 0x94, 0xAD, 0xAD, + 0xAD, 0xDD, 0xDE, 0xCF, 0x73, 0x0B, 0x0B, 0xB8, 0xB6, 0xE0, 0x5D, 0xC6, 0x66, 0xC5, 0xE4, 0x10, + 0x4C, 0xF4, 0xF7, 0xD8, 0x59, 0xF2, 0x7F, 0xA3, 0xB8, 0xB4, 0xFC, 0x0F, 0xEE, 0x37, 0x70, 0xEC, + 0x16, 0x4A, 0x7E, 0x04, 0x00, 0x00 +}; + +AsyncWebServerResponse *response = request->beginResponse_P(200, "image/x-icon", favicon_ico_gz, favicon_ico_gz_len); +response->addHeader("Content-Encoding", "gzip"); +request->send(response); +``` + +### Respond with content coming from a Stream +```cpp +//read 12 bytes from Serial and send them as Content Type text/plain +request->send(Serial, "text/plain", 12); +``` + +### Respond with content coming from a Stream and extra headers +```cpp +//read 12 bytes from Serial and send them as Content Type text/plain +AsyncWebServerResponse *response = request->beginResponse(Serial, "text/plain", 12); +response->addHeader("Server","ESP Async Web Server"); +request->send(response); +``` + +### Respond with content coming from a Stream containing templates +```cpp +String processor(const String& var) +{ + if(var == "HELLO_FROM_TEMPLATE") + return F("Hello world!"); + return String(); +} + +// ... + +//read 12 bytes from Serial and send them as Content Type text/plain +request->send(Serial, "text/plain", 12, processor); +``` + +### Respond with content coming from a Stream containing templates and extra headers +```cpp +String processor(const String& var) +{ + if(var == "HELLO_FROM_TEMPLATE") + return F("Hello world!"); + return String(); +} + +// ... + +//read 12 bytes from Serial and send them as Content Type text/plain +AsyncWebServerResponse *response = request->beginResponse(Serial, "text/plain", 12, processor); +response->addHeader("Server","ESP Async Web Server"); +request->send(response); +``` + +### Respond with content coming from a File +```cpp +//Send index.htm with default content type +request->send(SPIFFS, "/index.htm"); + +//Send index.htm as text +request->send(SPIFFS, "/index.htm", "text/plain"); + +//Download index.htm +request->send(SPIFFS, "/index.htm", String(), true); +``` + +### Respond with content coming from a File and extra headers +```cpp +//Send index.htm with default content type +AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.htm"); + +//Send index.htm as text +AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.htm", "text/plain"); + +//Download index.htm +AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.htm", String(), true); + +response->addHeader("Server","ESP Async Web Server"); +request->send(response); +``` + +### Respond with content coming from a File containing templates +Internally uses [Chunked Response](#chunked-response). + +Index.htm contents: +``` +%HELLO_FROM_TEMPLATE% +``` + +Somewhere in source files: +```cpp +String processor(const String& var) +{ + if(var == "HELLO_FROM_TEMPLATE") + return F("Hello world!"); + return String(); +} + +// ... + +//Send index.htm with template processor function +request->send(SPIFFS, "/index.htm", String(), false, processor); +``` + +### Respond with content using a callback +```cpp +//send 128 bytes as plain text +request->send("text/plain", 128, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { + //Write up to "maxLen" bytes into "buffer" and return the amount written. + //index equals the amount of bytes that have been already sent + //You will not be asked for more bytes once the content length has been reached. + //Keep in mind that you can not delay or yield waiting for more data! + //Send what you currently have and you will be asked for more again + return mySource.read(buffer, maxLen); +}); +``` + +### Respond with content using a callback and extra headers +```cpp +//send 128 bytes as plain text +AsyncWebServerResponse *response = request->beginResponse("text/plain", 128, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { + //Write up to "maxLen" bytes into "buffer" and return the amount written. + //index equals the amount of bytes that have been already sent + //You will not be asked for more bytes once the content length has been reached. + //Keep in mind that you can not delay or yield waiting for more data! + //Send what you currently have and you will be asked for more again + return mySource.read(buffer, maxLen); +}); +response->addHeader("Server","ESP Async Web Server"); +request->send(response); +``` + +### Respond with content using a callback containing templates +```cpp +String processor(const String& var) +{ + if(var == "HELLO_FROM_TEMPLATE") + return F("Hello world!"); + return String(); +} + +// ... + +//send 128 bytes as plain text +request->send("text/plain", 128, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { + //Write up to "maxLen" bytes into "buffer" and return the amount written. + //index equals the amount of bytes that have been already sent + //You will not be asked for more bytes once the content length has been reached. + //Keep in mind that you can not delay or yield waiting for more data! + //Send what you currently have and you will be asked for more again + return mySource.read(buffer, maxLen); +}, processor); +``` + +### Respond with content using a callback containing templates and extra headers +```cpp +String processor(const String& var) +{ + if(var == "HELLO_FROM_TEMPLATE") + return F("Hello world!"); + return String(); +} + +// ... + +//send 128 bytes as plain text +AsyncWebServerResponse *response = request->beginResponse("text/plain", 128, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { + //Write up to "maxLen" bytes into "buffer" and return the amount written. + //index equals the amount of bytes that have been already sent + //You will not be asked for more bytes once the content length has been reached. + //Keep in mind that you can not delay or yield waiting for more data! + //Send what you currently have and you will be asked for more again + return mySource.read(buffer, maxLen); +}, processor); +response->addHeader("Server","ESP Async Web Server"); +request->send(response); +``` + +### Chunked Response +Used when content length is unknown. Works best if the client supports HTTP/1.1 +```cpp +AsyncWebServerResponse *response = request->beginChunkedResponse("text/plain", [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { + //Write up to "maxLen" bytes into "buffer" and return the amount written. + //index equals the amount of bytes that have been already sent + //You will be asked for more data until 0 is returned + //Keep in mind that you can not delay or yield waiting for more data! + return mySource.read(buffer, maxLen); +}); +response->addHeader("Server","ESP Async Web Server"); +request->send(response); +``` + +### Chunked Response containing templates +Used when content length is unknown. Works best if the client supports HTTP/1.1 +```cpp +String processor(const String& var) +{ + if(var == "HELLO_FROM_TEMPLATE") + return F("Hello world!"); + return String(); +} + +// ... + +AsyncWebServerResponse *response = request->beginChunkedResponse("text/plain", [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { + //Write up to "maxLen" bytes into "buffer" and return the amount written. + //index equals the amount of bytes that have been already sent + //You will be asked for more data until 0 is returned + //Keep in mind that you can not delay or yield waiting for more data! + return mySource.read(buffer, maxLen); +}, processor); +response->addHeader("Server","ESP Async Web Server"); +request->send(response); +``` + +### Print to response +```cpp +AsyncResponseStream *response = request->beginResponseStream("text/html"); +response->addHeader("Server","ESP Async Web Server"); +response->printf("Webpage at %s", request->url().c_str()); + +response->print("

Hello "); +response->print(request->client()->remoteIP()); +response->print("

"); + +response->print("

General

"); +response->print("
    "); +response->printf("
  • Version: HTTP/1.%u
  • ", request->version()); +response->printf("
  • Method: %s
  • ", request->methodToString()); +response->printf("
  • URL: %s
  • ", request->url().c_str()); +response->printf("
  • Host: %s
  • ", request->host().c_str()); +response->printf("
  • ContentType: %s
  • ", request->contentType().c_str()); +response->printf("
  • ContentLength: %u
  • ", request->contentLength()); +response->printf("
  • Multipart: %s
  • ", request->multipart()?"true":"false"); +response->print("
"); + +response->print("

Headers

"); +response->print("
    "); +int headers = request->headers(); +for(int i=0;igetHeader(i); + response->printf("
  • %s: %s
  • ", h->name().c_str(), h->value().c_str()); +} +response->print("
"); + +response->print("

Parameters

"); +response->print("
    "); +int params = request->params(); +for(int i=0;igetParam(i); + if(p->isFile()){ + response->printf("
  • FILE[%s]: %s, size: %u
  • ", p->name().c_str(), p->value().c_str(), p->size()); + } else if(p->isPost()){ + response->printf("
  • POST[%s]: %s
  • ", p->name().c_str(), p->value().c_str()); + } else { + response->printf("
  • GET[%s]: %s
  • ", p->name().c_str(), p->value().c_str()); + } +} +response->print("
"); + +response->print(""); +//send the response last +request->send(response); +``` + +### ArduinoJson Basic Response +This way of sending Json is great for when the result is below 4KB +```cpp +#include "AsyncJson.h" +#include "ArduinoJson.h" + + +AsyncResponseStream *response = request->beginResponseStream("text/json"); +DynamicJsonBuffer jsonBuffer; +JsonObject &root = jsonBuffer.createObject(); +root["heap"] = ESP.getFreeHeap(); +root["ssid"] = WiFi.SSID(); +root.printTo(*response); +request->send(response); +``` + +### ArduinoJson Advanced Response +This response can handle really large Json objects (tested to 40KB) +There isn't any noticeable speed decrease for small results with the method above +Since ArduinoJson does not allow reading parts of the string, the whole Json has to +be passed every time a chunks needs to be sent, which shows speed decrease proportional +to the resulting json packets +```cpp +#include "AsyncJson.h" +#include "ArduinoJson.h" + + +AsyncJsonResponse * response = new AsyncJsonResponse(); +response->addHeader("Server","ESP Async Web Server"); +JsonObject& root = response->getRoot(); +root["heap"] = ESP.getFreeHeap(); +root["ssid"] = WiFi.SSID(); +response->setLength(); +request->send(response); +``` + +## Serving static files +In addition to serving files from SPIFFS as described above, the server provide a dedicated handler that optimize the +performance of serving files from SPIFFS - ```AsyncStaticWebHandler```. Use ```server.serveStatic()``` function to +initialize and add a new instance of ```AsyncStaticWebHandler``` to the server. +The Handler will not handle the request if the file does not exists, e.g. the server will continue to look for another +handler that can handle the request. +Notice that you can chain setter functions to setup the handler, or keep a pointer to change it at a later time. + +### Serving specific file by name +```cpp +// Serve the file "/www/page.htm" when request url is "/page.htm" +server.serveStatic("/page.htm", SPIFFS, "/www/page.htm"); +``` + +### Serving files in directory +To serve files in a directory, the path to the files should specify a directory in SPIFFS and ends with "/". +```cpp +// Serve files in directory "/www/" when request url starts with "/" +// Request to the root or none existing files will try to server the defualt +// file name "index.htm" if exists +server.serveStatic("/", SPIFFS, "/www/"); + +// Server with different default file +server.serveStatic("/", SPIFFS, "/www/").setDefaultFile("default.html"); +``` + +### Serving static files with authentication + +```cpp +server + .serveStatic("/", SPIFFS, "/www/") + .setDefaultFile("default.html") + .setAuthentication("user", "pass"); +``` + +### Specifying Cache-Control header +It is possible to specify Cache-Control header value to reduce the number of calls to the server once the client loaded +the files. For more information on Cache-Control values see [Cache-Control](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9) +```cpp +// Cache responses for 10 minutes (600 seconds) +server.serveStatic("/", SPIFFS, "/www/").setCacheControl("max-age=600"); + +//*** Change Cache-Control after server setup *** + +// During setup - keep a pointer to the handler +AsyncStaticWebHandler* handler = &server.serveStatic("/", SPIFFS, "/www/").setCacheControl("max-age=600"); + +// At a later event - change Cache-Control +handler->setCacheControl("max-age=30"); +``` + +### Specifying Date-Modified header +It is possible to specify Date-Modified header to enable the server to return Not-Modified (304) response for requests +with "If-Modified-Since" header with the same value, instead of responding with the actual file content. +```cpp +// Update the date modified string every time files are updated +server.serveStatic("/", SPIFFS, "/www/").setLastModified("Mon, 20 Jun 2016 14:00:00 GMT"); + +//*** Chage last modified value at a later stage *** + +// During setup - read last modified value from config or EEPROM +String date_modified = loadDateModified(); +AsyncStaticWebHandler* handler = &server.serveStatic("/", SPIFFS, "/www/"); +handler->setLastModified(date_modified); + +// At a later event when files are updated +String date_modified = getNewDateModfied(); +saveDateModified(date_modified); // Save for next reset +handler->setLastModified(date_modified); +``` + +### Specifying Template Processor callback +It is possible to specify template processor for static files. For information on template processor see +[Respond with content coming from a File containing templates](#respond-with-content-coming-from-a-file-containing-templates). +```cpp +String processor(const String& var) +{ + if(var == "HELLO_FROM_TEMPLATE") + return F("Hello world!"); + return String(); +} + +// ... + +server.serveStatic("/", SPIFFS, "/www/").setTemplateProcessor(processor); +``` + + +## Using filters +Filters can be set to `Rewrite` or `Handler` in order to control when to apply the rewrite and consider the handler. +A filter is a callback function that evaluates the request and return a boolean `true` to include the item +or `false` to exclude it. +Two filter callback are provided for convince: +* `ON_STA_FILTER` - return true when requests are made to the STA (station mode) interface. +* `ON_AP_FILTER` - return true when requests are made to the AP (access point) interface. + +### Serve different site files in AP mode +```cpp +server.serveStatic("/", SPIFFS, "/www/").setFilter(ON_STA_FILTER); +server.serveStatic("/", SPIFFS, "/ap/").setFilter(ON_AP_FILTER); +``` + +### Rewrite to different index on AP +```cpp +// Serve the file "/www/index-ap.htm" in AP, and the file "/www/index.htm" on STA +server.rewrite("/", "index.htm"); +server.rewrite("/index.htm", "index-ap.htm").setFilter(ON_AP_FILTER); +server.serveStatic("/", SPIFFS, "/www/"); +``` + +### Serving different hosts +```cpp +// Filter callback using request host +bool filterOnHost1(AsyncWebServerRequest *request) { return request->host() == "host1"; } + +// Server setup: server files in "/host1/" to requests for "host1", and files in "/www/" otherwise. +server.serveStatic("/", SPIFFS, "/host1/").setFilter(filterOnHost1); +server.serveStatic("/", SPIFFS, "/www/"); +``` + +## Bad Responses +Some responses are implemented, but you should not use them, because they do not conform to HTTP. +The following example will lead to unclean close of the connection and more time wasted +than providing the length of the content + +### Respond with content using a callback without content length to HTTP/1.0 clients +```cpp +//This is used as fallback for chunked responses to HTTP/1.0 Clients +request->send("text/plain", 0, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { + //Write up to "maxLen" bytes into "buffer" and return the amount written. + //You will be asked for more data until 0 is returned + //Keep in mind that you can not delay or yield waiting for more data! + return mySource.read(buffer, maxLen); +}); +``` + +## Async WebSocket Plugin +The server includes a web socket plugin which lets you define different WebSocket locations to connect to +without starting another listening service or using different port + +### Async WebSocket Event +```cpp + +void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ + if(type == WS_EVT_CONNECT){ + //client connected + os_printf("ws[%s][%u] connect\n", server->url(), client->id()); + client->printf("Hello Client %u :)", client->id()); + client->ping(); + } else if(type == WS_EVT_DISCONNECT){ + //client disconnected + os_printf("ws[%s][%u] disconnect: %u\n", server->url(), client->id()); + } else if(type == WS_EVT_ERROR){ + //error was received from the other end + os_printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data); + } else if(type == WS_EVT_PONG){ + //pong message was received (in response to a ping request maybe) + os_printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len)?(char*)data:""); + } else if(type == WS_EVT_DATA){ + //data packet + AwsFrameInfo * info = (AwsFrameInfo*)arg; + if(info->final && info->index == 0 && info->len == len){ + //the whole message is in a single frame and we got all of it's data + os_printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len); + if(info->opcode == WS_TEXT){ + data[len] = 0; + os_printf("%s\n", (char*)data); + } else { + for(size_t i=0; i < info->len; i++){ + os_printf("%02x ", data[i]); + } + os_printf("\n"); + } + if(info->opcode == WS_TEXT) + client->text("I got your text message"); + else + client->binary("I got your binary message"); + } else { + //message is comprised of multiple frames or the frame is split into multiple packets + if(info->index == 0){ + if(info->num == 0) + os_printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); + os_printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); + } + + os_printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len); + if(info->message_opcode == WS_TEXT){ + data[len] = 0; + os_printf("%s\n", (char*)data); + } else { + for(size_t i=0; i < len; i++){ + os_printf("%02x ", data[i]); + } + os_printf("\n"); + } + + if((info->index + len) == info->len){ + os_printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); + if(info->final){ + os_printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); + if(info->message_opcode == WS_TEXT) + client->text("I got your text message"); + else + client->binary("I got your binary message"); + } + } + } + } +} +``` + +### Methods for sending data to a socket client +```cpp + + + +//Server methods +AsyncWebSocket ws("/ws"); +//printf to a client +ws.printf((uint32_t)client_id, arguments...); +//printf to all clients +ws.printfAll(arguments...); +//printf_P to a client +ws.printf_P((uint32_t)client_id, PSTR(format), arguments...); +//printfAll_P to all clients +ws.printf_P(PSTR(format), arguments...); +//send text to a client +ws.text((uint32_t)client_id, (char*)text); +ws.text((uint32_t)client_id, (uint8_t*)text, (size_t)len); +//send text from PROGMEM to a client +ws.text((uint32_t)client_id, PSTR("text")); +const char flash_text[] PROGMEM = "Text to send" +ws.text((uint32_t)client_id, FPSTR(flash_text)); +//send text to all clients +ws.textAll((char*)text); +ws.textAll((uint8_t*)text, (size_t)len); +//send binary to a client +ws.binary((uint32_t)client_id, (char*)binary); +ws.binary((uint32_t)client_id, (uint8_t*)binary, (size_t)len); +//send binary from PROGMEM to a client +const uint8_t flash_binary[] PROGMEM = { 0x01, 0x02, 0x03, 0x04 }; +ws.binary((uint32_t)client_id, flash_binary, 4); +//send binary to all clients +ws.binaryAll((char*)binary); +ws.binaryAll((uint8_t*)binary, (size_t)len); +//HTTP Authenticate before switch to Websocket protocol +ws.setAuthentication("user", "pass"); + +//client methods +AsyncWebSocketClient * client; +//printf +client->printf(arguments...); +//printf_P +client->printf_P(PSTR(format), arguments...); +//send text +client->text((char*)text); +client->text((uint8_t*)text, (size_t)len); +//send text from PROGMEM +client->text(PSTR("text")); +const char flash_text[] PROGMEM = "Text to send"; +client->text(FPSTR(flash_text)); +//send binary +client->binary((char*)binary); +client->binary((uint8_t*)binary, (size_t)len); +//send binary from PROGMEM +const uint8_t flash_binary[] PROGMEM = { 0x01, 0x02, 0x03, 0x04 }; +client->binary(flash_binary, 4); +``` + +### Direct access to web socket message buffer +When sending a web socket message using the above methods a buffer is created. Under certain circumstances you might want to manipulate or populate this buffer directly from your application, for example to prevent unnecessary duplications of the data. This example below shows how to create a buffer and print data to it from an ArduinoJson object then send it. + +```cpp +void sendDataWs(AsyncWebSocketClient * client) +{ + DynamicJsonBuffer jsonBuffer; + JsonObject& root = jsonBuffer.createObject(); + root["a"] = "abc"; + root["b"] = "abcd"; + root["c"] = "abcde"; + root["d"] = "abcdef"; + root["e"] = "abcdefg"; + size_t len = root.measureLength(); + AsyncWebSocketMessageBuffer * buffer = ws.makeBuffer(len); // creates a buffer (len + 1) for you. + if (buffer) { + root.printTo((char *)buffer->get(), len + 1); + if (client) { + client->text(buffer); + } else { + ws.textAll(buffer); + } + } +} +``` + +## Async Event Source Plugin +The server includes EventSource (Server-Sent Events) plugin which can be used to send short text events to the browser. +Difference between EventSource and WebSockets is that EventSource is single direction, text-only protocol. + +### Setup Event Source on the server +```cpp +AsyncWebServer server(80); +AsyncEventSource events("/events"); + +void setup(){ + // setup ...... + events.onConnect([](AsyncEventSourceClient *client){ + if(client->lastId()){ + Serial.printf("Client reconnected! Last message ID that it gat is: %u\n", client->lastId()); + } + //send event with message "hello!", id current millis + // and set reconnect delay to 1 second + client->send("hello!",NULL,millis(),1000); + }); + //HTTP Basic authentication + events.setAuthentication("user", "pass"); + server.addHandler(&events); + // setup ...... +} + +void loop(){ + if(eventTriggered){ // your logic here + //send event "myevent" + events.send("my event content","myevent",millis()); + } +} +``` + +### Setup Event Source in the browser +```javascript +if (!!window.EventSource) { + var source = new EventSource('/events'); + + source.addEventListener('open', function(e) { + console.log("Events Connected"); + }, false); + + source.addEventListener('error', function(e) { + if (e.target.readyState != EventSource.OPEN) { + console.log("Events Disconnected"); + } + }, false); + + source.addEventListener('message', function(e) { + console.log("message", e.data); + }, false); + + source.addEventListener('myevent', function(e) { + console.log("myevent", e.data); + }, false); +} +``` + +## Scanning for available WiFi Networks +```cpp +//First request will return 0 results unless you start scan from somewhere else (loop/setup) +//Do not request more often than 3-5 seconds +server.on("/scan", HTTP_GET, [](AsyncWebServerRequest *request){ + String json = "["; + int n = WiFi.scanComplete(); + if(n == -2){ + WiFi.scanNetworks(true); + } else if(n){ + for (int i = 0; i < n; ++i){ + if(i) json += ","; + json += "{"; + json += "\"rssi\":"+String(WiFi.RSSI(i)); + json += ",\"ssid\":\""+WiFi.SSID(i)+"\""; + json += ",\"bssid\":\""+WiFi.BSSIDstr(i)+"\""; + json += ",\"channel\":"+String(WiFi.channel(i)); + json += ",\"secure\":"+String(WiFi.encryptionType(i)); + json += ",\"hidden\":"+String(WiFi.isHidden(i)?"true":"false"); + json += "}"; + } + WiFi.scanDelete(); + if(WiFi.scanComplete() == -2){ + WiFi.scanNetworks(true); + } + } + json += "]"; + request->send(200, "text/json", json); + json = String(); +}); +``` + +## Remove handlers and rewrites + +Server goes through handlers in same order as they were added. You can't simple add handler with same path to override them. +To remove handler: +```arduino +// save callback for particular URL path +auto handler = server.on("/some/path", [](AsyncWebServerRequest *request){ + //do something useful +}); +// when you don't need handler anymore remove it +server.removeHandler(&handler); + +// same with rewrites +server.removeRewrite(&someRewrite); + +server.onNotFound([](AsyncWebServerRequest *request){ + request->send(404); +}); + +// remove server.onNotFound handler +server.onNotFound(NULL); + +// remove all rewrites, handlers and onNotFound/onFileUpload/onRequestBody callbacks +server.reset(); +``` + +## Setting up the server +```cpp +#include "ESPAsyncTCP.h" +#include "ESPAsyncWebServer.h" + +AsyncWebServer server(80); +AsyncWebSocket ws("/ws"); // access at ws://[esp ip]/ws +AsyncEventSource events("/events"); // event source (Server-Sent events) + +const char* ssid = "your-ssid"; +const char* password = "your-pass"; +const char* http_username = "admin"; +const char* http_password = "admin"; + +//flag to use from web update to reboot the ESP +bool shouldReboot = false; + +void onRequest(AsyncWebServerRequest *request){ + //Handle Unknown Request + request->send(404); +} + +void onBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ + //Handle body +} + +void onUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ + //Handle upload +} + +void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ + //Handle WebSocket event +} + +void setup(){ + Serial.begin(115200); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.printf("WiFi Failed!\n"); + return; + } + + // attach AsyncWebSocket + ws.onEvent(onEvent); + server.addHandler(&ws); + + // attach AsyncEventSource + server.addHandler(&events); + + // respond to GET requests on URL /heap + server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/plain", String(ESP.getFreeHeap())); + }); + + // upload a file to /upload + server.on("/upload", HTTP_POST, [](AsyncWebServerRequest *request){ + request->send(200); + }, onUpload); + + // send a file when /index is requested + server.on("/index", HTTP_ANY, [](AsyncWebServerRequest *request){ + request->send(SPIFFS, "/index.htm"); + }); + + // HTTP basic authentication + server.on("/login", HTTP_GET, [](AsyncWebServerRequest *request){ + if(!request->authenticate(http_username, http_password)) + return request->requestAuthentication(); + request->send(200, "text/plain", "Login Success!"); + }); + + // Simple Firmware Update Form + server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/html", "
"); + }); + server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request){ + shouldReboot = !Update.hasError(); + AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", shouldReboot?"OK":"FAIL"); + response->addHeader("Connection", "close"); + request->send(response); + },[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ + if(!index){ + Serial.printf("Update Start: %s\n", filename.c_str()); + Update.runAsync(true); + if(!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)){ + Update.printError(Serial); + } + } + if(!Update.hasError()){ + if(Update.write(data, len) != len){ + Update.printError(Serial); + } + } + if(final){ + if(Update.end(true)){ + Serial.printf("Update Success: %uB\n", index+len); + } else { + Update.printError(Serial); + } + } + }); + + // attach filesystem root at URL /fs + server.serveStatic("/fs", SPIFFS, "/"); + + // Catch-All Handlers + // Any request that can not find a Handler that canHandle it + // ends in the callbacks below. + server.onNotFound(onRequest); + server.onFileUpload(onUpload); + server.onRequestBody(onBody); + + server.begin(); +} + +void loop(){ + if(shouldReboot){ + Serial.println("Rebooting..."); + delay(100); + ESP.restart(); + } + static char temp[128]; + sprintf(temp, "Seconds since boot: %u", millis()/1000); + events.send(temp, "time"); //send event "time" +} +``` + +### Setup global and class functions as request handlers + +```arduino +#include +#include +#include +#include + +void handleRequest(AsyncWebServerRequest *request) +{ +} + +class WebClass +{ +public : + WebClass(){ + }; + + AsyncWebServer classWebServer = AsyncWebServer(80); + + void classRequest (AsyncWebServerRequest *request) + { + } + + void begin(){ + + // attach global request handler + classWebServer.on("/example", HTTP_ANY, handleRequest); + + // attach class request handler + classWebServer.on("/example", HTTP_ANY, std::bind(&WebClass::classRequest, this, std::placeholders::_1)); + } +}; + +AsyncWebServer globalWebServer(80); +WebClass webClassInstance; + +void setup() { + + // attach global request handler + globalWebServer.on("/example", HTTP_ANY, handleRequest); + + // attach class request handler + globalWebServer.on("/example", HTTP_ANY, std::bind(&WebClass::classRequest, webClassInstance, std::placeholders::_1)); +} + +void loop() { + +} +``` + +### Methods for controlling websocket connections + +```arduino + // Disable client connections if it was activated + if ( ws.enabled() ) + ws.enable(false); + + // enable client connections if it was disabled + if ( !ws.enabled() ) + ws.enable(true); +``` + +Example of OTA code + +```arduino + // OTA callbacks + ArduinoOTA.onStart([]() { + // Clean SPIFFS + SPIFFS.end(); + + // Disable client connections + ws.enable(false); + + // Advertise connected clients what's going on + ws.textAll("OTA Update Started"); + + // Close them + ws.closeAll(); + + }); + +``` + +### Adding Default Headers + +In some cases, such as when working with CORS, or with some sort of custom authentication system, +you might need to define a header that should get added to all responses (including static, websocket and EventSource). +The DefaultHeaders singleton allows you to do this. + +Example: + +```arduino +DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); +webServer.begin(); +``` + +*NOTE*: You will still need to respond to the OPTIONS method for CORS pre-flight in most cases. (unless you are only using GET) + +This is one option: + +```arduino +webServer.onNotFound([](AsyncWebServerRequest *request) { + if (request->method() == HTTP_OPTIONS) { + request->send(200); + } else { + request->send(404); + } +}); +``` diff --git a/libraries/ESPAsyncWebServer/component.mk b/libraries/ESPAsyncWebServer/component.mk new file mode 100644 index 00000000..bb5bb161 --- /dev/null +++ b/libraries/ESPAsyncWebServer/component.mk @@ -0,0 +1,3 @@ +COMPONENT_ADD_INCLUDEDIRS := src +COMPONENT_SRCDIRS := src +CXXFLAGS += -fno-rtti diff --git a/libraries/ESPAsyncWebServer/espressif32_library.json b/libraries/ESPAsyncWebServer/espressif32_library.json new file mode 100644 index 00000000..baea1397 --- /dev/null +++ b/libraries/ESPAsyncWebServer/espressif32_library.json @@ -0,0 +1,23 @@ +{ + "name":"AsyncWebServer", + "description":"Asynchronous HTTP and WebSocket Server Library for ESP32", + "keywords":"http,async,websocket,webserver", + "authors": + { + "name": "Hristo Gochkov", + "maintainer": true + }, + "repository": + { + "type": "git", + "url": "https://github.com/me-no-dev/ESPAsyncWebServer.git" + }, + "version": "1.1.0", + "license": "LGPL-3.0", + "frameworks": "arduino", + "platforms": ["espressif32", "espressif32_stage"], + "dependencies": + { + "name": "AsyncTCP" + } +} diff --git a/libraries/ESPAsyncWebServer/espressif8266_library.json b/libraries/ESPAsyncWebServer/espressif8266_library.json new file mode 100644 index 00000000..fee6410e --- /dev/null +++ b/libraries/ESPAsyncWebServer/espressif8266_library.json @@ -0,0 +1,23 @@ +{ + "name":"ESPAsyncWebServer", + "description":"Asynchronous HTTP and WebSocket Server Library for ESP8266", + "keywords":"http,async,websocket,webserver", + "authors": + { + "name": "Hristo Gochkov", + "maintainer": true + }, + "repository": + { + "type": "git", + "url": "https://github.com/me-no-dev/ESPAsyncWebServer.git" + }, + "version": "1.1.0", + "license": "LGPL-3.0", + "frameworks": "arduino", + "platforms": ["espressif8266", "espressif8266_stage"], + "dependencies": + { + "name": "ESPAsyncTCP" + } +} diff --git a/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/.esp31b.skip b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/.esp31b.skip new file mode 100644 index 00000000..e69de29b diff --git a/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino new file mode 100644 index 00000000..651c5577 --- /dev/null +++ b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +// SKETCH BEGIN +AsyncWebServer server(80); +AsyncWebSocket ws("/ws"); +AsyncEventSource events("/events"); + +void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ + if(type == WS_EVT_CONNECT){ + Serial.printf("ws[%s][%u] connect\n", server->url(), client->id()); + client->printf("Hello Client %u :)", client->id()); + client->ping(); + } else if(type == WS_EVT_DISCONNECT){ + Serial.printf("ws[%s][%u] disconnect: %u\n", server->url(), client->id()); + } else if(type == WS_EVT_ERROR){ + Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data); + } else if(type == WS_EVT_PONG){ + Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len)?(char*)data:""); + } else if(type == WS_EVT_DATA){ + AwsFrameInfo * info = (AwsFrameInfo*)arg; + String msg = ""; + if(info->final && info->index == 0 && info->len == len){ + //the whole message is in a single frame and we got all of it's data + Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len); + + if(info->opcode == WS_TEXT){ + for(size_t i=0; i < info->len; i++) { + msg += (char) data[i]; + } + } else { + char buff[3]; + for(size_t i=0; i < info->len; i++) { + sprintf(buff, "%02x ", (uint8_t) data[i]); + msg += buff ; + } + } + Serial.printf("%s\n",msg.c_str()); + + if(info->opcode == WS_TEXT) + client->text("I got your text message"); + else + client->binary("I got your binary message"); + } else { + //message is comprised of multiple frames or the frame is split into multiple packets + if(info->index == 0){ + if(info->num == 0) + Serial.printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); + Serial.printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); + } + + Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len); + + if(info->opcode == WS_TEXT){ + for(size_t i=0; i < info->len; i++) { + msg += (char) data[i]; + } + } else { + char buff[3]; + for(size_t i=0; i < info->len; i++) { + sprintf(buff, "%02x ", (uint8_t) data[i]); + msg += buff ; + } + } + Serial.printf("%s\n",msg.c_str()); + + if((info->index + len) == info->len){ + Serial.printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); + if(info->final){ + Serial.printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); + if(info->message_opcode == WS_TEXT) + client->text("I got your text message"); + else + client->binary("I got your binary message"); + } + } + } + } +} + + +const char* ssid = "*******"; +const char* password = "*******"; +const char * hostName = "esp-async"; +const char* http_username = "admin"; +const char* http_password = "admin"; + +void setup(){ + Serial.begin(115200); + Serial.setDebugOutput(true); + WiFi.hostname(hostName); + WiFi.mode(WIFI_AP_STA); + WiFi.softAP(hostName); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.printf("STA: Failed!\n"); + WiFi.disconnect(false); + delay(1000); + WiFi.begin(ssid, password); + } + + //Send OTA events to the browser + ArduinoOTA.onStart([]() { events.send("Update Start", "ota"); }); + ArduinoOTA.onEnd([]() { events.send("Update End", "ota"); }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + char p[32]; + sprintf(p, "Progress: %u%%\n", (progress/(total/100))); + events.send(p, "ota"); + }); + ArduinoOTA.onError([](ota_error_t error) { + if(error == OTA_AUTH_ERROR) events.send("Auth Failed", "ota"); + else if(error == OTA_BEGIN_ERROR) events.send("Begin Failed", "ota"); + else if(error == OTA_CONNECT_ERROR) events.send("Connect Failed", "ota"); + else if(error == OTA_RECEIVE_ERROR) events.send("Recieve Failed", "ota"); + else if(error == OTA_END_ERROR) events.send("End Failed", "ota"); + }); + ArduinoOTA.setHostname(hostName); + ArduinoOTA.begin(); + + MDNS.addService("http","tcp",80); + + SPIFFS.begin(); + + ws.onEvent(onWsEvent); + server.addHandler(&ws); + + events.onConnect([](AsyncEventSourceClient *client){ + client->send("hello!",NULL,millis(),1000); + }); + server.addHandler(&events); + + server.addHandler(new SPIFFSEditor(http_username,http_password)); + + server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/plain", String(ESP.getFreeHeap())); + }); + + server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.htm"); + + server.onNotFound([](AsyncWebServerRequest *request){ + Serial.printf("NOT_FOUND: "); + if(request->method() == HTTP_GET) + Serial.printf("GET"); + else if(request->method() == HTTP_POST) + Serial.printf("POST"); + else if(request->method() == HTTP_DELETE) + Serial.printf("DELETE"); + else if(request->method() == HTTP_PUT) + Serial.printf("PUT"); + else if(request->method() == HTTP_PATCH) + Serial.printf("PATCH"); + else if(request->method() == HTTP_HEAD) + Serial.printf("HEAD"); + else if(request->method() == HTTP_OPTIONS) + Serial.printf("OPTIONS"); + else + Serial.printf("UNKNOWN"); + Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str()); + + if(request->contentLength()){ + Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str()); + Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength()); + } + + int headers = request->headers(); + int i; + for(i=0;igetHeader(i); + Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); + } + + int params = request->params(); + for(i=0;igetParam(i); + if(p->isFile()){ + Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); + } else if(p->isPost()){ + Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); + } else { + Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); + } + } + + request->send(404); + }); + server.onFileUpload([](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){ + if(!index) + Serial.printf("UploadStart: %s\n", filename.c_str()); + Serial.printf("%s", (const char*)data); + if(final) + Serial.printf("UploadEnd: %s (%u)\n", filename.c_str(), index+len); + }); + server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ + if(!index) + Serial.printf("BodyStart: %u\n", total); + Serial.printf("%s", (const char*)data); + if(index + len == total) + Serial.printf("BodyEnd: %u\n", total); + }); + server.begin(); +} + +void loop(){ + ArduinoOTA.handle(); +} diff --git a/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/.exclude.files b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/.exclude.files new file mode 100644 index 00000000..955397fa --- /dev/null +++ b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/.exclude.files @@ -0,0 +1,2 @@ +/*.js.gz +/.exclude.files diff --git a/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ace.js.gz b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ace.js.gz new file mode 100644 index 00000000..7b175c1c Binary files /dev/null and b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ace.js.gz differ diff --git a/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz new file mode 100644 index 00000000..cf5b49f6 Binary files /dev/null and b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz differ diff --git a/libraries/WebServer/examples/FSBrowser/data/favicon.ico b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/favicon.ico similarity index 100% rename from libraries/WebServer/examples/FSBrowser/data/favicon.ico rename to libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/favicon.ico diff --git a/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/index.htm b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/index.htm new file mode 100644 index 00000000..28f47e99 --- /dev/null +++ b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/index.htm @@ -0,0 +1,131 @@ + + + + + + WebSocketTester + + + + +

+    
+ $ +
+ + diff --git a/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz new file mode 100644 index 00000000..ebd6fe94 Binary files /dev/null and b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz differ diff --git a/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz new file mode 100644 index 00000000..26b53532 Binary files /dev/null and b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz differ diff --git a/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz new file mode 100644 index 00000000..c0451c1c Binary files /dev/null and b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz differ diff --git a/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz new file mode 100644 index 00000000..ec8aa87a Binary files /dev/null and b/libraries/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz differ diff --git a/libraries/ESPAsyncWebServer/keywords.txt b/libraries/ESPAsyncWebServer/keywords.txt new file mode 100644 index 00000000..c391e6c4 --- /dev/null +++ b/libraries/ESPAsyncWebServer/keywords.txt @@ -0,0 +1,3 @@ +JsonArray KEYWORD1 +add KEYWORD2 +createArray KEYWORD3 diff --git a/libraries/ESPAsyncWebServer/library.properties b/libraries/ESPAsyncWebServer/library.properties new file mode 100644 index 00000000..174bb2f5 --- /dev/null +++ b/libraries/ESPAsyncWebServer/library.properties @@ -0,0 +1,9 @@ +name=ESP Async WebServer +version=1.1.0 +author=Me-No-Dev +maintainer=Me-No-Dev +sentence=Async Web Server for ESP8266 and ESP31B +paragraph=Async Web Server for ESP8266 and ESP31B +category=Other +url=https://github.com/me-no-dev/ESPAsyncWebServer +architectures=* diff --git a/libraries/ESPAsyncWebServer/src/AsyncEventSource.cpp b/libraries/ESPAsyncWebServer/src/AsyncEventSource.cpp new file mode 100644 index 00000000..ecb6b019 --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/AsyncEventSource.cpp @@ -0,0 +1,345 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. 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 +*/ +#include "Arduino.h" +#include "AsyncEventSource.h" + +static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){ + String ev = ""; + + if(reconnect){ + ev += "retry: "; + ev += String(reconnect); + ev += "\r\n"; + } + + if(id){ + ev += "id: "; + ev += String(id); + ev += "\r\n"; + } + + if(event != NULL){ + ev += "event: "; + ev += String(event); + ev += "\r\n"; + } + + if(message != NULL){ + size_t messageLen = strlen(message); + char * lineStart = (char *)message; + char * lineEnd; + do { + char * nextN = strchr(lineStart, '\n'); + char * nextR = strchr(lineStart, '\r'); + if(nextN == NULL && nextR == NULL){ + size_t llen = ((char *)message + messageLen) - lineStart; + char * ldata = (char *)malloc(llen+1); + if(ldata != NULL){ + memcpy(ldata, lineStart, llen); + ldata[llen] = 0; + ev += "data: "; + ev += ldata; + ev += "\r\n\r\n"; + free(ldata); + } + lineStart = (char *)message + messageLen; + } else { + char * nextLine = NULL; + if(nextN != NULL && nextR != NULL){ + if(nextR < nextN){ + lineEnd = nextR; + if(nextN == (nextR + 1)) + nextLine = nextN + 1; + else + nextLine = nextR + 1; + } else { + lineEnd = nextN; + if(nextR == (nextN + 1)) + nextLine = nextR + 1; + else + nextLine = nextN + 1; + } + } else if(nextN != NULL){ + lineEnd = nextN; + nextLine = nextN + 1; + } else { + lineEnd = nextR; + nextLine = nextR + 1; + } + + size_t llen = lineEnd - lineStart; + char * ldata = (char *)malloc(llen+1); + if(ldata != NULL){ + memcpy(ldata, lineStart, llen); + ldata[llen] = 0; + ev += "data: "; + ev += ldata; + ev += "\r\n"; + free(ldata); + } + lineStart = nextLine; + if(lineStart == ((char *)message + messageLen)) + ev += "\r\n"; + } + } while(lineStart < ((char *)message + messageLen)); + } + + return ev; +} + +// Message + +AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len) +: _data(nullptr), _len(len), _sent(0), _acked(0) +{ + _data = (uint8_t*)malloc(_len+1); + if(_data == nullptr){ + _len = 0; + } else { + memcpy(_data, data, len); + _data[_len] = 0; + } +} + +AsyncEventSourceMessage::~AsyncEventSourceMessage() { + if(_data != NULL) + free(_data); +} + +size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) { + // If the whole message is now acked... + if(_acked + len > _len){ + // Return the number of extra bytes acked (they will be carried on to the next message) + const size_t extra = _acked + len - _len; + _acked = _len; + return extra; + } + // Return that no extra bytes left. + _acked += len; + return 0; +} + +size_t AsyncEventSourceMessage::send(AsyncClient *client) { + const size_t len = _len - _sent; + if(client->space() < len){ + return 0; + } + size_t sent = client->add((const char *)_data, len); + if(client->canSend()) + client->send(); + _sent += sent; + return sent; +} + +// Client + +AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server) +: _messageQueue(LinkedList([](AsyncEventSourceMessage *m){ delete m; })) +{ + _client = request->client(); + _server = server; + _lastId = 0; + if(request->hasHeader("Last-Event-ID")) + _lastId = atoi(request->getHeader("Last-Event-ID")->value().c_str()); + + _client->setRxTimeout(0); + _client->onError(NULL, NULL); + _client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this); + _client->onPoll([](void *r, AsyncClient* c){ ((AsyncEventSourceClient*)(r))->_onPoll(); }, this); + _client->onData(NULL, NULL); + _client->onTimeout([this](void *r, AsyncClient* c __attribute__((unused)), uint32_t time){ ((AsyncEventSourceClient*)(r))->_onTimeout(time); }, this); + _client->onDisconnect([this](void *r, AsyncClient* c){ ((AsyncEventSourceClient*)(r))->_onDisconnect(); delete c; }, this); + + _server->_addClient(this); + delete request; +} + +AsyncEventSourceClient::~AsyncEventSourceClient(){ + _messageQueue.free(); + close(); +} + +void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage){ + if(dataMessage == NULL) + return; + if(!connected()){ + delete dataMessage; + return; + } + + _messageQueue.add(dataMessage); + + _runQueue(); +} + +void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){ + while(len && !_messageQueue.isEmpty()){ + len = _messageQueue.front()->ack(len, time); + if(_messageQueue.front()->finished()) + _messageQueue.remove(_messageQueue.front()); + } + + _runQueue(); +} + +void AsyncEventSourceClient::_onPoll(){ + if(!_messageQueue.isEmpty()){ + _runQueue(); + } +} + + +void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){ + _client->close(true); +} + +void AsyncEventSourceClient::_onDisconnect(){ + _client = NULL; + _server->_handleDisconnect(this); +} + +void AsyncEventSourceClient::close(){ + if(_client != NULL) + _client->close(); +} + +void AsyncEventSourceClient::write(const char * message, size_t len){ + _queueMessage(new AsyncEventSourceMessage(message, len)); +} + +void AsyncEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){ + String ev = generateEventMessage(message, event, id, reconnect); + _queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length())); +} + +void AsyncEventSourceClient::_runQueue(){ + while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){ + _messageQueue.remove(_messageQueue.front()); + } + + for(auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) + { + if(!(*i)->sent()) + (*i)->send(_client); + } +} + + +// Handler + +AsyncEventSource::AsyncEventSource(const String& url) + : _url(url) + , _clients(LinkedList([](AsyncEventSourceClient *c){ delete c; })) + , _connectcb(NULL) +{} + +AsyncEventSource::~AsyncEventSource(){ + close(); +} + +void AsyncEventSource::onConnect(ArEventHandlerFunction cb){ + _connectcb = cb; +} + +void AsyncEventSource::_addClient(AsyncEventSourceClient * client){ + /*char * temp = (char *)malloc(2054); + if(temp != NULL){ + memset(temp+1,' ',2048); + temp[0] = ':'; + temp[2049] = '\r'; + temp[2050] = '\n'; + temp[2051] = '\r'; + temp[2052] = '\n'; + temp[2053] = 0; + client->write((const char *)temp, 2053); + free(temp); + }*/ + + _clients.add(client); + if(_connectcb) + _connectcb(client); +} + +void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){ + _clients.remove(client); +} + +void AsyncEventSource::close(){ + for(const auto &c: _clients){ + if(c->connected()) + c->close(); + } +} + +void AsyncEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){ + if(_clients.isEmpty()) + return; + + String ev = generateEventMessage(message, event, id, reconnect); + for(const auto &c: _clients){ + if(c->connected()) { + c->write(ev.c_str(), ev.length()); + } + } +} + +size_t AsyncEventSource::count() const { + return _clients.count_if([](AsyncEventSourceClient *c){ + return c->connected(); + }); +} + +bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){ + if(request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_EVENT)) + return false; + request->addInterestingHeader("Last-Event-ID"); + return true; +} + +void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){ + if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) + return request->requestAuthentication(); + request->send(new AsyncEventSourceResponse(this)); +} + +// Response + +AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){ + _server = server; + _code = 200; + _contentType = "text/event-stream"; + _sendContentLength = false; + addHeader("Cache-Control", "no-cache"); + addHeader("Connection","keep-alive"); +} + +void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){ + String out = _assembleHead(request->version()); + request->client()->write(out.c_str(), _headLength); + _state = RESPONSE_WAIT_ACK; +} + +size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time __attribute__((unused))){ + if(len){ + new AsyncEventSourceClient(request, _server); + } + return 0; +} + diff --git a/libraries/ESPAsyncWebServer/src/AsyncEventSource.h b/libraries/ESPAsyncWebServer/src/AsyncEventSource.h new file mode 100644 index 00000000..1b4d604d --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/AsyncEventSource.h @@ -0,0 +1,114 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. 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 +*/ +#ifndef ASYNCEVENTSOURCE_H_ +#define ASYNCEVENTSOURCE_H_ + +#include +#ifdef ESP32 +#include +#else +#include +#endif +#include + +class AsyncEventSource; +class AsyncEventSourceResponse; +class AsyncEventSourceClient; +typedef std::function ArEventHandlerFunction; + +class AsyncEventSourceMessage { + private: + uint8_t * _data; + size_t _len; + size_t _sent; + //size_t _ack; + size_t _acked; + public: + AsyncEventSourceMessage(const char * data, size_t len); + ~AsyncEventSourceMessage(); + size_t ack(size_t len, uint32_t time __attribute__((unused))); + size_t send(AsyncClient *client); + bool finished(){ return _acked == _len; } + bool sent() { return _sent == _len; } +}; + +class AsyncEventSourceClient { + private: + AsyncClient *_client; + AsyncEventSource *_server; + uint32_t _lastId; + LinkedList _messageQueue; + void _queueMessage(AsyncEventSourceMessage *dataMessage); + void _runQueue(); + + public: + + AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server); + ~AsyncEventSourceClient(); + + AsyncClient* client(){ return _client; } + void close(); + void write(const char * message, size_t len); + void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0); + bool connected() const { return (_client != NULL) && _client->connected(); } + uint32_t lastId() const { return _lastId; } + + //system callbacks (do not call) + void _onAck(size_t len, uint32_t time); + void _onPoll(); + void _onTimeout(uint32_t time); + void _onDisconnect(); +}; + +class AsyncEventSource: public AsyncWebHandler { + private: + String _url; + LinkedList _clients; + ArEventHandlerFunction _connectcb; + public: + AsyncEventSource(const String& url); + ~AsyncEventSource(); + + const char * url() const { return _url.c_str(); } + void close(); + void onConnect(ArEventHandlerFunction cb); + void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0); + size_t count() const; //number clinets connected + + //system callbacks (do not call) + void _addClient(AsyncEventSourceClient * client); + void _handleDisconnect(AsyncEventSourceClient * client); + virtual bool canHandle(AsyncWebServerRequest *request) override final; + virtual void handleRequest(AsyncWebServerRequest *request) override final; +}; + +class AsyncEventSourceResponse: public AsyncWebServerResponse { + private: + String _content; + AsyncEventSource *_server; + public: + AsyncEventSourceResponse(AsyncEventSource *server); + void _respond(AsyncWebServerRequest *request); + size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); + bool _sourceValid() const { return true; } +}; + + +#endif /* ASYNCEVENTSOURCE_H_ */ diff --git a/libraries/ESPAsyncWebServer/src/AsyncJson.h b/libraries/ESPAsyncWebServer/src/AsyncJson.h new file mode 100644 index 00000000..4d37062d --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/AsyncJson.h @@ -0,0 +1,83 @@ +// ESPasyncJson.h +/* + Async Response to use with arduinoJson and asyncwebserver + Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon. + + example of callback in use + + server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) { + + AsyncJsonResponse * response = new AsyncJsonResponse(); + JsonObject& root = response->getRoot(); + root["key1"] = "key number one"; + JsonObject& nested = root.createNestedObject("nested"); + nested["key1"] = "key number one"; + + response->setLength(); + request->send(response); + }); + +*/ +#ifndef ASYNC_JSON_H_ +#define ASYNC_JSON_H_ +#include + +/* + * Json Response + * */ + +class ChunkPrint : public Print { + private: + uint8_t* _destination; + size_t _to_skip; + size_t _to_write; + size_t _pos; + public: + ChunkPrint(uint8_t* destination, size_t from, size_t len) + : _destination(destination), _to_skip(from), _to_write(len), _pos{0} {} + virtual ~ChunkPrint(){} + size_t write(uint8_t c){ + if (_to_skip > 0) { + _to_skip--; + return 1; + } else if (_to_write > 0) { + _to_write--; + _destination[_pos++] = c; + return 1; + } + return 0; + } +}; + +class AsyncJsonResponse: public AsyncAbstractResponse { + private: + DynamicJsonBuffer _jsonBuffer; + JsonVariant _root; + bool _isValid; + public: + AsyncJsonResponse(bool isArray=false): _isValid{false} { + _code = 200; + _contentType = "text/json"; + if(isArray) + _root = _jsonBuffer.createArray(); + else + _root = _jsonBuffer.createObject(); + } + ~AsyncJsonResponse() {} + JsonVariant & getRoot() { return _root; } + bool _sourceValid() const { return _isValid; } + size_t setLength() { + _contentLength = _root.measureLength(); + if (_contentLength) { _isValid = true; } + return _contentLength; + } + + size_t getSize() { return _jsonBuffer.size(); } + + size_t _fillBuffer(uint8_t *data, size_t len){ + ChunkPrint dest(data, _sentLength, len); + _root.printTo( dest ) ; + return len; + } +}; +#endif diff --git a/libraries/ESPAsyncWebServer/src/AsyncWebSocket.cpp b/libraries/ESPAsyncWebServer/src/AsyncWebSocket.cpp new file mode 100644 index 00000000..7eeadac4 --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/AsyncWebSocket.cpp @@ -0,0 +1,1206 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +#include "Arduino.h" +#include "AsyncWebSocket.h" + +#include + +#ifndef ESP8266 +extern "C" { +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]); +void SHA1Init(SHA1_CTX* context); +void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len); +void SHA1Final(unsigned char digest[20], SHA1_CTX* context); +} +#else +#include +#endif + +#define MAX_PRINTF_LEN 64 + +size_t webSocketSendFrameWindow(AsyncClient *client){ + if(!client->canSend()) + return 0; + size_t space = client->space(); + if(space < 9) + return 0; + return space - 8; +} + +size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool mask, uint8_t *data, size_t len){ + if(!client->canSend()) + return 0; + size_t space = client->space(); + if(space < 2) + return 0; + uint8_t mbuf[4] = {0,0,0,0}; + uint8_t headLen = 2; + if(len && mask){ + headLen += 4; + mbuf[0] = rand() % 0xFF; + mbuf[1] = rand() % 0xFF; + mbuf[2] = rand() % 0xFF; + mbuf[3] = rand() % 0xFF; + } + if(len > 125) + headLen += 2; + if(space < headLen) + return 0; + space -= headLen; + + if(len > space) len = space; + + uint8_t *buf = (uint8_t*)malloc(headLen); + if(buf == NULL){ + //os_printf("could not malloc %u bytes for frame header\n", headLen); + return 0; + } + + buf[0] = opcode & 0x0F; + if(final) + buf[0] |= 0x80; + if(len < 126) + buf[1] = len & 0x7F; + else { + buf[1] = 126; + buf[2] = (uint8_t)((len >> 8) & 0xFF); + buf[3] = (uint8_t)(len & 0xFF); + } + if(len && mask){ + buf[1] |= 0x80; + memcpy(buf + (headLen - 4), mbuf, 4); + } + if(client->add((const char *)buf, headLen) != headLen){ + //os_printf("error adding %lu header bytes\n", headLen); + free(buf); + return 0; + } + free(buf); + + if(len){ + if(len && mask){ + size_t i; + for(i=0;iadd((const char *)data, len) != len){ + //os_printf("error adding %lu data bytes\n", len); + return 0; + } + } + if(!client->send()){ + //os_printf("error sending frame: %lu\n", headLen+len); + return 0; + } + return len; +} + + +/* + * AsyncWebSocketMessageBuffer + */ + + + +AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer() + :_data(nullptr) + ,_len(0) + ,_lock(false) + ,_count(0) +{ + +} + +AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t size) + :_data(nullptr) + ,_len(size) + ,_lock(false) + ,_count(0) +{ + + if (!data) { + return; + } + + _data = new uint8_t[size + 1]; + + if (_data) { + memcpy(_data, data, _len); + _data[_len] = 0; + } +} + + +AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) + :_data(nullptr) + ,_len(size) + ,_lock(false) + ,_count(0) +{ + _data = new uint8_t[_len + 1]; + + if (_data) { + _data[_len] = 0; + } + +} + +AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer & copy) + :_data(nullptr) + ,_len(0) + ,_lock(false) + ,_count(0) +{ + _len = copy._len; + _lock = copy._lock; + _count = 0; + + if (_len) { + _data = new uint8_t[_len + 1]; + _data[_len] = 0; + } + + if (_data) { + memcpy(_data, copy._data, _len); + _data[_len] = 0; + } + +} + +AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer && copy) + :_data(nullptr) + ,_len(0) + ,_lock(false) + ,_count(0) +{ + _len = copy._len; + _lock = copy._lock; + _count = 0; + + if (copy._data) { + _data = copy._data; + copy._data = nullptr; + } + +} + +AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer() +{ + if (_data) { + delete[] _data; + } +} + +bool AsyncWebSocketMessageBuffer::reserve(size_t size) +{ + _len = size; + + if (_data) { + delete[] _data; + _data = nullptr; + } + + _data = new uint8_t[size]; + + if (_data) { + _data[_len] = 0; + return true; + } else { + return false; + } + +} + + + +/* + * Control Frame + */ + +class AsyncWebSocketControl { + private: + uint8_t _opcode; + uint8_t *_data; + size_t _len; + bool _mask; + bool _finished; + public: + AsyncWebSocketControl(uint8_t opcode, uint8_t *data=NULL, size_t len=0, bool mask=false) + :_opcode(opcode) + ,_len(len) + ,_mask(len && mask) + ,_finished(false) + { + if(data == NULL) + _len = 0; + if(_len){ + if(_len > 125) + _len = 125; + _data = (uint8_t*)malloc(_len); + if(_data == NULL) + _len = 0; + else memcpy(_data, data, len); + } else _data = NULL; + } + virtual ~AsyncWebSocketControl(){ + if(_data != NULL) + free(_data); + } + virtual bool finished() const { return _finished; } + uint8_t opcode(){ return _opcode; } + uint8_t len(){ return _len + 2; } + size_t send(AsyncClient *client){ + _finished = true; + return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len); + } +}; + +/* + * Basic Buffered Message + */ + + +AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode, bool mask) + :_len(len) + ,_sent(0) + ,_ack(0) + ,_acked(0) +{ + _opcode = opcode & 0x07; + _mask = mask; + _data = (uint8_t*)malloc(_len+1); + if(_data == NULL){ + _len = 0; + _status = WS_MSG_ERROR; + } else { + _status = WS_MSG_SENDING; + memcpy(_data, data, _len); + _data[_len] = 0; + } +} +AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(uint8_t opcode, bool mask) + :_len(0) + ,_sent(0) + ,_ack(0) + ,_acked(0) + ,_data(NULL) +{ + _opcode = opcode & 0x07; + _mask = mask; + +} + + +AsyncWebSocketBasicMessage::~AsyncWebSocketBasicMessage() { + if(_data != NULL) + free(_data); +} + + void AsyncWebSocketBasicMessage::ack(size_t len, uint32_t time) { + _acked += len; + if(_sent == _len && _acked == _ack){ + _status = WS_MSG_SENT; + } +} + size_t AsyncWebSocketBasicMessage::send(AsyncClient *client) { + if(_status != WS_MSG_SENDING) + return 0; + if(_acked < _ack){ + return 0; + } + if(_sent == _len){ + if(_acked == _ack) + _status = WS_MSG_SENT; + return 0; + } + size_t window = webSocketSendFrameWindow(client); + size_t toSend = _len - _sent; + if(window < toSend) toSend = window; + bool final = ((toSend + _sent) == _len); + size_t sent = webSocketSendFrame(client, final, (_sent == 0)?_opcode:(int)WS_CONTINUATION, _mask, (uint8_t*)(_data+_sent), toSend); + _sent += sent; + uint8_t headLen = ((sent < 126)?2:4)+(_mask*4); + _ack += sent + headLen; + return sent; +} + +// bool AsyncWebSocketBasicMessage::reserve(size_t size) { +// if (size) { +// _data = (uint8_t*)malloc(size +1); +// if (_data) { +// memset(_data, 0, size); +// _len = size; +// _status = WS_MSG_SENDING; +// return true; +// } +// } +// return false; +// } + + +/* + * AsyncWebSocketMultiMessage Message + */ + + +AsyncWebSocketMultiMessage::AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode, bool mask) + :_len(0) + ,_sent(0) + ,_ack(0) + ,_acked(0) + ,_WSbuffer(nullptr) +{ + + _opcode = opcode & 0x07; + _mask = mask; + + if (buffer) { + _WSbuffer = buffer; + (*_WSbuffer)++; + _data = buffer->get(); + _len = buffer->length(); + _status = WS_MSG_SENDING; + } else { + _status = WS_MSG_ERROR; + } + +} + + +AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() { + if (_WSbuffer) { + (*_WSbuffer)--; // decreases the counter. + } +} + + void AsyncWebSocketMultiMessage::ack(size_t len, uint32_t time) { + _acked += len; + if(_sent == _len && _acked == _ack){ + _status = WS_MSG_SENT; + } +} + size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) { + if(_status != WS_MSG_SENDING) + return 0; + if(_acked < _ack){ + return 0; + } + if(_sent == _len){ + if(_acked == _ack) + _status = WS_MSG_SENT; + return 0; + } + size_t window = webSocketSendFrameWindow(client); + size_t toSend = _len - _sent; + if(window < toSend) toSend = window; + bool final = ((toSend + _sent) == _len); + size_t sent = webSocketSendFrame(client, final, (_sent == 0)?_opcode:(int)WS_CONTINUATION, _mask, (uint8_t*)(_data+_sent), toSend); + _sent += sent; + uint8_t headLen = ((sent < 126)?2:4)+(_mask*4); + _ack += sent + headLen; + return sent; +} + + +/* + * Async WebSocket Client + */ + const char * AWSC_PING_PAYLOAD = "ESPAsyncWebServer-PING"; + const size_t AWSC_PING_PAYLOAD_LEN = 22; + +AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server) + : _controlQueue(LinkedList([](AsyncWebSocketControl *c){ delete c; })) + , _messageQueue(LinkedList([](AsyncWebSocketMessage *m){ delete m; })) + , _tempObject(NULL) +{ + _client = request->client(); + _server = server; + _clientId = _server->_getNextId(); + _status = WS_CONNECTED; + _pstate = 0; + _lastMessageTime = millis(); + _keepAlivePeriod = 0; + _client->setRxTimeout(0); + _client->onError([](void *r, AsyncClient* c, int8_t error){ ((AsyncWebSocketClient*)(r))->_onError(error); }, this); + _client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ ((AsyncWebSocketClient*)(r))->_onAck(len, time); }, this); + _client->onDisconnect([](void *r, AsyncClient* c){ ((AsyncWebSocketClient*)(r))->_onDisconnect(); delete c; }, this); + _client->onTimeout([](void *r, AsyncClient* c, uint32_t time){ ((AsyncWebSocketClient*)(r))->_onTimeout(time); }, this); + _client->onData([](void *r, AsyncClient* c, void *buf, size_t len){ ((AsyncWebSocketClient*)(r))->_onData(buf, len); }, this); + _client->onPoll([](void *r, AsyncClient* c){ ((AsyncWebSocketClient*)(r))->_onPoll(); }, this); + _server->_addClient(this); + _server->_handleEvent(this, WS_EVT_CONNECT, NULL, NULL, 0); + delete request; +} + +AsyncWebSocketClient::~AsyncWebSocketClient(){ + _messageQueue.free(); + _controlQueue.free(); + _server->_handleEvent(this, WS_EVT_DISCONNECT, NULL, NULL, 0); +} + +void AsyncWebSocketClient::_onAck(size_t len, uint32_t time){ + _lastMessageTime = millis(); + if(!_controlQueue.isEmpty()){ + auto head = _controlQueue.front(); + if(head->finished()){ + len -= head->len(); + if(_status == WS_DISCONNECTING && head->opcode() == WS_DISCONNECT){ + _controlQueue.remove(head); + _status = WS_DISCONNECTED; + _client->close(true); + return; + } + _controlQueue.remove(head); + } + } + if(len && !_messageQueue.isEmpty()){ + _messageQueue.front()->ack(len, time); + } + _server->_cleanBuffers(); + _runQueue(); +} + +void AsyncWebSocketClient::_onPoll(){ + if(_client->canSend() && (!_controlQueue.isEmpty() || !_messageQueue.isEmpty())){ + _runQueue(); + } else if(_keepAlivePeriod > 0 && _controlQueue.isEmpty() && _messageQueue.isEmpty() && (millis() - _lastMessageTime) >= _keepAlivePeriod){ + ping((uint8_t *)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN); + } +} + +void AsyncWebSocketClient::_runQueue(){ + while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){ + _messageQueue.remove(_messageQueue.front()); + } + + if(!_controlQueue.isEmpty() && (_messageQueue.isEmpty() || _messageQueue.front()->betweenFrames()) && webSocketSendFrameWindow(_client) > (size_t)(_controlQueue.front()->len() - 1)){ + _controlQueue.front()->send(_client); + } else if(!_messageQueue.isEmpty() && _messageQueue.front()->betweenFrames() && webSocketSendFrameWindow(_client)){ + _messageQueue.front()->send(_client); + } +} + +void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage){ + if(dataMessage == NULL) + return; + if(_status != WS_CONNECTED){ + delete dataMessage; + return; + } + _messageQueue.add(dataMessage); + if(_client->canSend()) + _runQueue(); +} + +void AsyncWebSocketClient::_queueControl(AsyncWebSocketControl *controlMessage){ + if(controlMessage == NULL) + return; + _controlQueue.add(controlMessage); + if(_client->canSend()) + _runQueue(); +} + +void AsyncWebSocketClient::close(uint16_t code, const char * message){ + if(_status != WS_CONNECTED) + return; + if(code){ + uint8_t packetLen = 2; + if(message != NULL){ + size_t mlen = strlen(message); + if(mlen > 123) mlen = 123; + packetLen += mlen; + } + char * buf = (char*)malloc(packetLen); + if(buf != NULL){ + buf[0] = (uint8_t)(code >> 8); + buf[1] = (uint8_t)(code & 0xFF); + if(message != NULL){ + memcpy(buf+2, message, packetLen -2); + } + _queueControl(new AsyncWebSocketControl(WS_DISCONNECT,(uint8_t*)buf,packetLen)); + free(buf); + return; + } + } + _queueControl(new AsyncWebSocketControl(WS_DISCONNECT)); +} + +void AsyncWebSocketClient::ping(uint8_t *data, size_t len){ + if(_status == WS_CONNECTED) + _queueControl(new AsyncWebSocketControl(WS_PING, data, len)); +} + +void AsyncWebSocketClient::_onError(int8_t){} + +void AsyncWebSocketClient::_onTimeout(uint32_t time){ + _client->close(true); +} + +void AsyncWebSocketClient::_onDisconnect(){ + _client = NULL; + _server->_handleDisconnect(this); +} + +void AsyncWebSocketClient::_onData(void *buf, size_t plen){ + _lastMessageTime = millis(); + uint8_t *fdata = (uint8_t*)buf; + uint8_t * data = fdata; + if(!_pstate){ + _pinfo.index = 0; + _pinfo.final = (fdata[0] & 0x80) != 0; + _pinfo.opcode = fdata[0] & 0x0F; + _pinfo.masked = (fdata[1] & 0x80) != 0; + _pinfo.len = fdata[1] & 0x7F; + data += 2; + plen = plen - 2; + if(_pinfo.len == 126){ + _pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8; + data += 2; + plen = plen - 2; + } else if(_pinfo.len == 127){ + _pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56; + data += 8; + plen = plen - 8; + } + + if(_pinfo.masked){ + memcpy(_pinfo.mask, data, 4); + data += 4; + plen = plen - 4; + size_t i; + for(i=0;i_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, plen); + + _pinfo.index += plen; + } else if((plen + _pinfo.index) == _pinfo.len){ + _pstate = 0; + if(_pinfo.opcode == WS_DISCONNECT){ + if(plen){ + uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1]; + char * reasonString = (char*)(data+2); + if(reasonCode > 1001){ + _server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t*)reasonString, strlen(reasonString)); + } + } + if(_status == WS_DISCONNECTING){ + _status = WS_DISCONNECTED; + _client->close(true); + } else { + _status = WS_DISCONNECTING; + _queueControl(new AsyncWebSocketControl(WS_DISCONNECT, data, plen)); + } + } else if(_pinfo.opcode == WS_PING){ + _queueControl(new AsyncWebSocketControl(WS_PONG, data, plen)); + } else if(_pinfo.opcode == WS_PONG){ + if(plen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) + _server->_handleEvent(this, WS_EVT_PONG, NULL, (uint8_t*)data, plen); + } else if(_pinfo.opcode < 8){//continuation or text/binary frame + _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, plen); + } + } else { + //os_printf("frame error: len: %u, index: %llu, total: %llu\n", plen, _pinfo.index, _pinfo.len); + //what should we do? + } +} + +size_t AsyncWebSocketClient::printf(const char *format, ...) { + va_list arg; + va_start(arg, format); + char* temp = new char[MAX_PRINTF_LEN]; + if(!temp){ + return 0; + } + char* buffer = temp; + size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg); + va_end(arg); + + if (len > (MAX_PRINTF_LEN - 1)) { + buffer = new char[len + 1]; + if (!buffer) { + delete[] temp; + return 0; + } + va_start(arg, format); + vsnprintf(buffer, len + 1, format, arg); + va_end(arg); + } + text(buffer, len); + if (buffer != temp) { + delete[] buffer; + } + delete[] temp; + return len; +} + +#ifndef ESP32 +size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) { + va_list arg; + va_start(arg, formatP); + char* temp = new char[MAX_PRINTF_LEN]; + if(!temp){ + return 0; + } + char* buffer = temp; + size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg); + va_end(arg); + + if (len > (MAX_PRINTF_LEN - 1)) { + buffer = new char[len + 1]; + if (!buffer) { + delete[] temp; + return 0; + } + va_start(arg, formatP); + vsnprintf_P(buffer, len + 1, formatP, arg); + va_end(arg); + } + text(buffer, len); + if (buffer != temp) { + delete[] buffer; + } + delete[] temp; + return len; +} +#endif + +void AsyncWebSocketClient::text(const char * message, size_t len){ + _queueMessage(new AsyncWebSocketBasicMessage(message, len)); +} +void AsyncWebSocketClient::text(const char * message){ + text(message, strlen(message)); +} +void AsyncWebSocketClient::text(uint8_t * message, size_t len){ + text((const char *)message, len); +} +void AsyncWebSocketClient::text(char * message){ + text(message, strlen(message)); +} +void AsyncWebSocketClient::text(const String &message){ + text(message.c_str(), message.length()); +} +void AsyncWebSocketClient::text(const __FlashStringHelper *data){ + PGM_P p = reinterpret_cast(data); + size_t n = 0; + while (1) { + if (pgm_read_byte(p+n) == 0) break; + n += 1; + } + char * message = (char*) malloc(n+1); + if(message){ + for(size_t b=0; b(data); + char * message = (char*) malloc(len); + if(message){ + for(size_t b=0; bremoteIP(); +} + +uint16_t AsyncWebSocketClient::remotePort() { + if(!_client) { + return 0; + } + return _client->remotePort(); +} + + + +/* + * Async Web Socket - Each separate socket location + */ + +AsyncWebSocket::AsyncWebSocket(const String& url) + :_url(url) + ,_clients(LinkedList([](AsyncWebSocketClient *c){ delete c; })) + ,_cNextId(1) + ,_enabled(true) + ,_buffers(LinkedList([](AsyncWebSocketMessageBuffer *b){ delete b; })) +{ + _eventHandler = NULL; +} + +AsyncWebSocket::~AsyncWebSocket(){} + +void AsyncWebSocket::_handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ + if(_eventHandler != NULL){ + _eventHandler(this, client, type, arg, data, len); + } +} + +void AsyncWebSocket::_addClient(AsyncWebSocketClient * client){ + _clients.add(client); +} + +void AsyncWebSocket::_handleDisconnect(AsyncWebSocketClient * client){ + + _clients.remove_first([=](AsyncWebSocketClient * c){ + return c->id() == client->id(); + }); +} + +size_t AsyncWebSocket::count() const { + return _clients.count_if([](AsyncWebSocketClient * c){ + return c->status() == WS_CONNECTED; + }); +} + +AsyncWebSocketClient * AsyncWebSocket::client(uint32_t id){ + for(const auto &c: _clients){ + if(c->id() == id && c->status() == WS_CONNECTED){ + return c; + } + } + return nullptr; +} + + +void AsyncWebSocket::close(uint32_t id, uint16_t code, const char * message){ + AsyncWebSocketClient * c = client(id); + if(c) + c->close(code, message); +} + +void AsyncWebSocket::closeAll(uint16_t code, const char * message){ + for(const auto& c: _clients){ + if(c->status() == WS_CONNECTED) + c->close(code, message); + } +} + +void AsyncWebSocket::ping(uint32_t id, uint8_t *data, size_t len){ + AsyncWebSocketClient * c = client(id); + if(c) + c->ping(data, len); +} + +void AsyncWebSocket::pingAll(uint8_t *data, size_t len){ + for(const auto& c: _clients){ + if(c->status() == WS_CONNECTED) + c->ping(data, len); + } +} + +void AsyncWebSocket::text(uint32_t id, const char * message, size_t len){ + AsyncWebSocketClient * c = client(id); + if(c) + c->text(message, len); +} + +void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer){ + if (!buffer) return; + buffer->lock(); + for(const auto& c: _clients){ + if(c->status() == WS_CONNECTED) + c->text(buffer); + } + buffer->unlock(); + _cleanBuffers(); +} + + +void AsyncWebSocket::textAll(const char * message, size_t len){ + AsyncWebSocketMessageBuffer * WSBuffer = makeBuffer((uint8_t *)message, len); + textAll(WSBuffer); +} + +void AsyncWebSocket::binary(uint32_t id, const char * message, size_t len){ + AsyncWebSocketClient * c = client(id); + if(c) + c->binary(message, len); +} + +void AsyncWebSocket::binaryAll(const char * message, size_t len){ + AsyncWebSocketMessageBuffer * buffer = makeBuffer((uint8_t *)message, len); + binaryAll(buffer); +} + +void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer * buffer) +{ + if (!buffer) return; + buffer->lock(); + for(const auto& c: _clients){ + if(c->status() == WS_CONNECTED) + c->binary(buffer); + } + buffer->unlock(); + _cleanBuffers(); +} + +void AsyncWebSocket::message(uint32_t id, AsyncWebSocketMessage *message){ + AsyncWebSocketClient * c = client(id); + if(c) + c->message(message); +} + +void AsyncWebSocket::messageAll(AsyncWebSocketMultiMessage *message){ + for(const auto& c: _clients){ + if(c->status() == WS_CONNECTED) + c->message(message); + } + _cleanBuffers(); +} + +size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...){ + AsyncWebSocketClient * c = client(id); + if(c){ + va_list arg; + va_start(arg, format); + size_t len = c->printf(format, arg); + va_end(arg); + return len; + } + return 0; +} + +size_t AsyncWebSocket::printfAll(const char *format, ...) { + va_list arg; + char* temp = new char[MAX_PRINTF_LEN]; + if(!temp){ + return 0; + } + va_start(arg, format); + size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg); + va_end(arg); + delete[] temp; + + AsyncWebSocketMessageBuffer * buffer = makeBuffer(len + 1); + if (!buffer) { + return 0; + } + + va_start(arg, format); + vsnprintf( (char *)buffer->get(), len + 1, format, arg); + va_end(arg); + + textAll(buffer); + return len; +} + +#ifndef ESP32 +size_t AsyncWebSocket::printf_P(uint32_t id, PGM_P formatP, ...){ + AsyncWebSocketClient * c = client(id); + if(c != NULL){ + va_list arg; + va_start(arg, formatP); + size_t len = c->printf_P(formatP, arg); + va_end(arg); + return len; + } + return 0; +} +#endif + +size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) { + va_list arg; + char* temp = new char[MAX_PRINTF_LEN]; + if(!temp){ + return 0; + } + va_start(arg, formatP); + size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg); + va_end(arg); + delete[] temp; + + AsyncWebSocketMessageBuffer * buffer = makeBuffer(len + 1); + if (!buffer) { + return 0; + } + + va_start(arg, formatP); + vsnprintf_P((char *)buffer->get(), len + 1, formatP, arg); + va_end(arg); + + textAll(buffer); + return len; +} + +void AsyncWebSocket::text(uint32_t id, const char * message){ + text(id, message, strlen(message)); +} +void AsyncWebSocket::text(uint32_t id, uint8_t * message, size_t len){ + text(id, (const char *)message, len); +} +void AsyncWebSocket::text(uint32_t id, char * message){ + text(id, message, strlen(message)); +} +void AsyncWebSocket::text(uint32_t id, const String &message){ + text(id, message.c_str(), message.length()); +} +void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper *message){ + AsyncWebSocketClient * c = client(id); + if(c != NULL) + c->text(message); +} +void AsyncWebSocket::textAll(const char * message){ + textAll(message, strlen(message)); +} +void AsyncWebSocket::textAll(uint8_t * message, size_t len){ + textAll((const char *)message, len); +} +void AsyncWebSocket::textAll(char * message){ + textAll(message, strlen(message)); +} +void AsyncWebSocket::textAll(const String &message){ + textAll(message.c_str(), message.length()); +} +void AsyncWebSocket::textAll(const __FlashStringHelper *message){ + for(const auto& c: _clients){ + if(c->status() == WS_CONNECTED) + c->text(message); + } +} +void AsyncWebSocket::binary(uint32_t id, const char * message){ + binary(id, message, strlen(message)); +} +void AsyncWebSocket::binary(uint32_t id, uint8_t * message, size_t len){ + binary(id, (const char *)message, len); +} +void AsyncWebSocket::binary(uint32_t id, char * message){ + binary(id, message, strlen(message)); +} +void AsyncWebSocket::binary(uint32_t id, const String &message){ + binary(id, message.c_str(), message.length()); +} +void AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper *message, size_t len){ + AsyncWebSocketClient * c = client(id); + if(c != NULL) + c-> binary(message, len); +} +void AsyncWebSocket::binaryAll(const char * message){ + binaryAll(message, strlen(message)); +} +void AsyncWebSocket::binaryAll(uint8_t * message, size_t len){ + binaryAll((const char *)message, len); +} +void AsyncWebSocket::binaryAll(char * message){ + binaryAll(message, strlen(message)); +} +void AsyncWebSocket::binaryAll(const String &message){ + binaryAll(message.c_str(), message.length()); +} +void AsyncWebSocket::binaryAll(const __FlashStringHelper *message, size_t len){ + for(const auto& c: _clients){ + if(c->status() == WS_CONNECTED) + c-> binary(message, len); + } + } + +const char * WS_STR_CONNECTION = "Connection"; +const char * WS_STR_UPGRADE = "Upgrade"; +const char * WS_STR_ORIGIN = "Origin"; +const char * WS_STR_VERSION = "Sec-WebSocket-Version"; +const char * WS_STR_KEY = "Sec-WebSocket-Key"; +const char * WS_STR_PROTOCOL = "Sec-WebSocket-Protocol"; +const char * WS_STR_ACCEPT = "Sec-WebSocket-Accept"; +const char * WS_STR_UUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + +bool AsyncWebSocket::canHandle(AsyncWebServerRequest *request){ + if(!_enabled) + return false; + + if(request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS)) + return false; + + request->addInterestingHeader(WS_STR_CONNECTION); + request->addInterestingHeader(WS_STR_UPGRADE); + request->addInterestingHeader(WS_STR_ORIGIN); + request->addInterestingHeader(WS_STR_VERSION); + request->addInterestingHeader(WS_STR_KEY); + request->addInterestingHeader(WS_STR_PROTOCOL); + return true; +} + +void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request){ + if(!request->hasHeader(WS_STR_VERSION) || !request->hasHeader(WS_STR_KEY)){ + request->send(400); + return; + } + if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())){ + return request->requestAuthentication(); + } + AsyncWebHeader* version = request->getHeader(WS_STR_VERSION); + if(version->value().toInt() != 13){ + AsyncWebServerResponse *response = request->beginResponse(400); + response->addHeader(WS_STR_VERSION,"13"); + request->send(response); + return; + } + AsyncWebHeader* key = request->getHeader(WS_STR_KEY); + AsyncWebServerResponse *response = new AsyncWebSocketResponse(key->value(), this); + if(request->hasHeader(WS_STR_PROTOCOL)){ + AsyncWebHeader* protocol = request->getHeader(WS_STR_PROTOCOL); + //ToDo: check protocol + response->addHeader(WS_STR_PROTOCOL, protocol->value()); + } + request->send(response); +} + +AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size) +{ + AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(size); + if (buffer) { + _buffers.add(buffer); + } + return buffer; +} + +AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t size) +{ + AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(data, size); + + if (buffer) { + _buffers.add(buffer); + } + + return buffer; +} + +void AsyncWebSocket::_cleanBuffers() +{ + for(const auto& c: _buffers){ + if(c->canDelete()) + _buffers.remove(c); + } +} + + +/* + * Response to Web Socket request - sends the authorization and detaches the TCP Client from the web server + * Authentication code from https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSockets.cpp#L480 + */ + +AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket *server){ + _server = server; + _code = 101; + _sendContentLength = false; + + uint8_t * hash = (uint8_t*)malloc(20); + if(hash == NULL){ + _state = RESPONSE_FAILED; + return; + } + char * buffer = (char *) malloc(33); + if(buffer == NULL){ + free(hash); + _state = RESPONSE_FAILED; + return; + } +#ifdef ESP8266 + sha1(key + WS_STR_UUID, hash); +#else + (String&)key += WS_STR_UUID; + SHA1_CTX ctx; + SHA1Init(&ctx); + SHA1Update(&ctx, (const unsigned char*)key.c_str(), key.length()); + SHA1Final(hash, &ctx); +#endif + base64_encodestate _state; + base64_init_encodestate(&_state); + int len = base64_encode_block((const char *) hash, 20, buffer, &_state); + len = base64_encode_blockend((buffer + len), &_state); + addHeader(WS_STR_CONNECTION, WS_STR_UPGRADE); + addHeader(WS_STR_UPGRADE, "websocket"); + addHeader(WS_STR_ACCEPT,buffer); + free(buffer); + free(hash); +} + +void AsyncWebSocketResponse::_respond(AsyncWebServerRequest *request){ + if(_state == RESPONSE_FAILED){ + request->client()->close(true); + return; + } + String out = _assembleHead(request->version()); + request->client()->write(out.c_str(), _headLength); + _state = RESPONSE_WAIT_ACK; +} + +size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ + if(len){ + new AsyncWebSocketClient(request, _server); + } + return 0; +} diff --git a/libraries/ESPAsyncWebServer/src/AsyncWebSocket.h b/libraries/ESPAsyncWebServer/src/AsyncWebSocket.h new file mode 100644 index 00000000..5242ba3c --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/AsyncWebSocket.h @@ -0,0 +1,306 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +#ifndef ASYNCWEBSOCKET_H_ +#define ASYNCWEBSOCKET_H_ + +#include +#ifdef ESP32 +#include +#else +#include +#endif +#include + +class AsyncWebSocket; +class AsyncWebSocketResponse; +class AsyncWebSocketClient; +class AsyncWebSocketControl; + +typedef struct { + uint8_t message_opcode; + uint32_t num; + uint8_t final; + uint8_t masked; + uint8_t opcode; + uint64_t len; + uint8_t mask[4]; + uint64_t index; +} AwsFrameInfo; + +typedef enum { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } AwsClientStatus; +typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PING, WS_PONG } AwsFrameType; +typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus; +typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType; + +class AsyncWebSocketMessageBuffer { + private: + uint8_t * _data; + size_t _len; + bool _lock; + uint32_t _count; + + public: + AsyncWebSocketMessageBuffer(); + AsyncWebSocketMessageBuffer(size_t size); + AsyncWebSocketMessageBuffer(uint8_t * data, size_t size); + AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer &); + AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer &&); + ~AsyncWebSocketMessageBuffer(); + void operator ++(int i) { _count++; } + void operator --(int i) { if (_count > 0) { _count--; } ; } + bool reserve(size_t size); + void lock() { _lock = true; } + void unlock() { _lock = false; } + uint8_t * get() { return _data; } + size_t length() { return _len; } + uint32_t count() { return _count; } + bool canDelete() { return (!_count && !_lock); } + + friend AsyncWebSocket; + +}; + +class AsyncWebSocketMessage { + protected: + uint8_t _opcode; + bool _mask; + AwsMessageStatus _status; + public: + AsyncWebSocketMessage():_opcode(WS_TEXT),_mask(false),_status(WS_MSG_ERROR){} + virtual ~AsyncWebSocketMessage(){} + virtual void ack(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))){} + virtual size_t send(AsyncClient *client __attribute__((unused))){ return 0; } + virtual bool finished(){ return _status != WS_MSG_SENDING; } + virtual bool betweenFrames() const { return false; } +}; + +class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage { + private: + size_t _len; + size_t _sent; + size_t _ack; + size_t _acked; + uint8_t * _data; +public: + AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode=WS_TEXT, bool mask=false); + AsyncWebSocketBasicMessage(uint8_t opcode=WS_TEXT, bool mask=false); + virtual ~AsyncWebSocketBasicMessage() override; + virtual bool betweenFrames() const override { return _acked == _ack; } + virtual void ack(size_t len, uint32_t time) override ; + virtual size_t send(AsyncClient *client) override ; +}; + +class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage { + private: + uint8_t * _data; + size_t _len; + size_t _sent; + size_t _ack; + size_t _acked; + AsyncWebSocketMessageBuffer * _WSbuffer; +public: + AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode=WS_TEXT, bool mask=false); + virtual ~AsyncWebSocketMultiMessage() override; + virtual bool betweenFrames() const override { return _acked == _ack; } + virtual void ack(size_t len, uint32_t time) override ; + virtual size_t send(AsyncClient *client) override ; +}; + +class AsyncWebSocketClient { + private: + AsyncClient *_client; + AsyncWebSocket *_server; + uint32_t _clientId; + AwsClientStatus _status; + + LinkedList _controlQueue; + LinkedList _messageQueue; + + uint8_t _pstate; + AwsFrameInfo _pinfo; + + uint32_t _lastMessageTime; + uint32_t _keepAlivePeriod; + + void _queueMessage(AsyncWebSocketMessage *dataMessage); + void _queueControl(AsyncWebSocketControl *controlMessage); + void _runQueue(); + + public: + void *_tempObject; + + AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server); + ~AsyncWebSocketClient(); + + //client id increments for the given server + uint32_t id(){ return _clientId; } + AwsClientStatus status(){ return _status; } + AsyncClient* client(){ return _client; } + + IPAddress remoteIP(); + uint16_t remotePort(); + + //control frames + void close(uint16_t code=0, const char * message=NULL); + void ping(uint8_t *data=NULL, size_t len=0); + + //set auto-ping period in seconds. disabled if zero (default) + void keepAlivePeriod(uint16_t seconds){ + _keepAlivePeriod = seconds * 1000; + } + uint16_t keepAlivePeriod(){ + return (uint16_t)(_keepAlivePeriod / 1000); + } + + //data packets + void message(AsyncWebSocketMessage *message){ _queueMessage(message); } + + size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3))); +#ifndef ESP32 + size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3))); +#endif + void text(const char * message, size_t len); + void text(const char * message); + void text(uint8_t * message, size_t len); + void text(char * message); + void text(const String &message); + void text(const __FlashStringHelper *data); + void text(AsyncWebSocketMessageBuffer *buffer); + + void binary(const char * message, size_t len); + void binary(const char * message); + void binary(uint8_t * message, size_t len); + void binary(char * message); + void binary(const String &message); + void binary(const __FlashStringHelper *data, size_t len); + void binary(AsyncWebSocketMessageBuffer *buffer); + + //system callbacks (do not call) + void _onAck(size_t len, uint32_t time); + void _onError(int8_t); + void _onPoll(); + void _onTimeout(uint32_t time); + void _onDisconnect(); + void _onData(void *buf, size_t plen); +}; + +typedef std::function AwsEventHandler; + +//WebServer Handler implementation that plays the role of a socket server +class AsyncWebSocket: public AsyncWebHandler { + private: + String _url; + LinkedList _clients; + uint32_t _cNextId; + AwsEventHandler _eventHandler; + bool _enabled; + public: + AsyncWebSocket(const String& url); + ~AsyncWebSocket(); + const char * url() const { return _url.c_str(); } + void enable(bool e){ _enabled = e; } + bool enabled() const { return _enabled; } + + size_t count() const; + AsyncWebSocketClient * client(uint32_t id); + bool hasClient(uint32_t id){ return client(id) != NULL; } + + void close(uint32_t id, uint16_t code=0, const char * message=NULL); + void closeAll(uint16_t code=0, const char * message=NULL); + + void ping(uint32_t id, uint8_t *data=NULL, size_t len=0); + void pingAll(uint8_t *data=NULL, size_t len=0); // done + + void text(uint32_t id, const char * message, size_t len); + void text(uint32_t id, const char * message); + void text(uint32_t id, uint8_t * message, size_t len); + void text(uint32_t id, char * message); + void text(uint32_t id, const String &message); + void text(uint32_t id, const __FlashStringHelper *message); + + void textAll(const char * message, size_t len); + void textAll(const char * message); + void textAll(uint8_t * message, size_t len); + void textAll(char * message); + void textAll(const String &message); + void textAll(const __FlashStringHelper *message); // need to convert + void textAll(AsyncWebSocketMessageBuffer * buffer); + + void binary(uint32_t id, const char * message, size_t len); + void binary(uint32_t id, const char * message); + void binary(uint32_t id, uint8_t * message, size_t len); + void binary(uint32_t id, char * message); + void binary(uint32_t id, const String &message); + void binary(uint32_t id, const __FlashStringHelper *message, size_t len); + + void binaryAll(const char * message, size_t len); + void binaryAll(const char * message); + void binaryAll(uint8_t * message, size_t len); + void binaryAll(char * message); + void binaryAll(const String &message); + void binaryAll(const __FlashStringHelper *message, size_t len); + void binaryAll(AsyncWebSocketMessageBuffer * buffer); + + void message(uint32_t id, AsyncWebSocketMessage *message); + void messageAll(AsyncWebSocketMultiMessage *message); + + size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3))); +#ifndef ESP32 + size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__ ((format (printf, 3, 4))); +#endif + size_t printfAll_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3))); + + //event listener + void onEvent(AwsEventHandler handler){ + _eventHandler = handler; + } + + //system callbacks (do not call) + uint32_t _getNextId(){ return _cNextId++; } + void _addClient(AsyncWebSocketClient * client); + void _handleDisconnect(AsyncWebSocketClient * client); + void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len); + virtual bool canHandle(AsyncWebServerRequest *request) override final; + virtual void handleRequest(AsyncWebServerRequest *request) override final; + + + // messagebuffer functions/objects. + AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0); + AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size); + LinkedList _buffers; + void _cleanBuffers(); +}; + +//WebServer response to authenticate the socket and detach the tcp client from the web server request +class AsyncWebSocketResponse: public AsyncWebServerResponse { + private: + String _content; + AsyncWebSocket *_server; + public: + AsyncWebSocketResponse(const String& key, AsyncWebSocket *server); + void _respond(AsyncWebServerRequest *request); + size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); + bool _sourceValid() const { return true; } +}; + + +#endif /* ASYNCWEBSOCKET_H_ */ diff --git a/libraries/ESPAsyncWebServer/src/ESPAsyncWebServer.h b/libraries/ESPAsyncWebServer/src/ESPAsyncWebServer.h new file mode 100644 index 00000000..447eb34e --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/ESPAsyncWebServer.h @@ -0,0 +1,448 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +#ifndef _ESPAsyncWebServer_H_ +#define _ESPAsyncWebServer_H_ + +#include "Arduino.h" + +#include +#include "FS.h" + +#include "StringArray.h" + +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#else +#error Platform not supported +#endif + +#define DEBUGF(...) //Serial.printf(__VA_ARGS__) + +class AsyncWebServer; +class AsyncWebServerRequest; +class AsyncWebServerResponse; +class AsyncWebHeader; +class AsyncWebParameter; +class AsyncWebRewrite; +class AsyncWebHandler; +class AsyncStaticWebHandler; +class AsyncCallbackWebHandler; +class AsyncResponseStream; + +typedef enum { + HTTP_GET = 0b00000001, + HTTP_POST = 0b00000010, + HTTP_DELETE = 0b00000100, + HTTP_PUT = 0b00001000, + HTTP_PATCH = 0b00010000, + HTTP_HEAD = 0b00100000, + HTTP_OPTIONS = 0b01000000, + HTTP_ANY = 0b01111111, +} WebRequestMethod; +typedef uint8_t WebRequestMethodComposite; +typedef std::function ArDisconnectHandler; + +/* + * PARAMETER :: Chainable object to hold GET/POST and FILE parameters + * */ + +class AsyncWebParameter { + private: + String _name; + String _value; + size_t _size; + bool _isForm; + bool _isFile; + + public: + + AsyncWebParameter(const String& name, const String& value, bool form=false, bool file=false, size_t size=0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file){} + const String& name() const { return _name; } + const String& value() const { return _value; } + size_t size() const { return _size; } + bool isPost() const { return _isForm; } + bool isFile() const { return _isFile; } +}; + +/* + * HEADER :: Chainable object to hold the headers + * */ + +class AsyncWebHeader { + private: + String _name; + String _value; + + public: + AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){} + AsyncWebHeader(const String& data): _name(), _value(){ + if(!data) return; + int index = data.indexOf(':'); + if (index < 0) return; + _name = data.substring(0, index); + _value = data.substring(index + 2); + } + ~AsyncWebHeader(){} + const String& name() const { return _name; } + const String& value() const { return _value; } + String toString() const { return String(_name+": "+_value+"\r\n"); } +}; + +/* + * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect + * */ + +typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType; + +typedef std::function AwsResponseFiller; +typedef std::function AwsTemplateProcessor; + +class AsyncWebServerRequest { + using File = fs::File; + using FS = fs::FS; + friend class AsyncWebServer; + private: + AsyncClient* _client; + AsyncWebServer* _server; + AsyncWebHandler* _handler; + AsyncWebServerResponse* _response; + StringArray _interestingHeaders; + ArDisconnectHandler _onDisconnectfn; + String _temp; + uint8_t _parseState; + + uint8_t _version; + WebRequestMethodComposite _method; + String _url; + String _host; + String _contentType; + String _boundary; + String _authorization; + RequestedConnectionType _reqconntype; + void _removeNotInterestingHeaders(); + bool _isDigest; + bool _isMultipart; + bool _isPlainPost; + bool _expectingContinue; + size_t _contentLength; + size_t _parsedLength; + + LinkedList _headers; + LinkedList _params; + + uint8_t _multiParseState; + uint8_t _boundaryPosition; + size_t _itemStartIndex; + size_t _itemSize; + String _itemName; + String _itemFilename; + String _itemType; + String _itemValue; + uint8_t *_itemBuffer; + size_t _itemBufferIndex; + bool _itemIsFile; + + void _onPoll(); + void _onAck(size_t len, uint32_t time); + void _onError(int8_t error); + void _onTimeout(uint32_t time); + void _onDisconnect(); + void _onData(void *buf, size_t len); + + void _addParam(AsyncWebParameter*); + + bool _parseReqHead(); + bool _parseReqHeader(); + void _parseLine(); + void _parsePlainPostChar(uint8_t data); + void _parseMultipartPostByte(uint8_t data, bool last); + void _addGetParams(const String& params); + + void _handleUploadStart(); + void _handleUploadByte(uint8_t data, bool last); + void _handleUploadEnd(); + + public: + File _tempFile; + void *_tempObject; + + AsyncWebServerRequest(AsyncWebServer*, AsyncClient*); + ~AsyncWebServerRequest(); + + AsyncClient* client(){ return _client; } + uint8_t version() const { return _version; } + WebRequestMethodComposite method() const { return _method; } + const String& url() const { return _url; } + const String& host() const { return _host; } + const String& contentType() const { return _contentType; } + size_t contentLength() const { return _contentLength; } + bool multipart() const { return _isMultipart; } + const char * methodToString() const; + const char * requestedConnTypeToString() const; + RequestedConnectionType requestedConnType() const { return _reqconntype; } + bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); + void onDisconnect (ArDisconnectHandler fn); + //hash is the string representation of: + // base64(user:pass) for basic or + // user:realm:md5(user:realm:pass) for digest + bool authenticate(const char * hash); + bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false); + void requestAuthentication(const char * realm = NULL, bool isDigest = true); + + void setHandler(AsyncWebHandler *handler){ _handler = handler; } + void addInterestingHeader(const String& name); + + void redirect(const String& url); + + void send(AsyncWebServerResponse *response); + void send(int code, const String& contentType=String(), const String& content=String()); + void send(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); + void send(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); + void send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); + void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); + void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); + void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); + void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr); + + AsyncWebServerResponse *beginResponse(int code, const String& contentType=String(), const String& content=String()); + AsyncWebServerResponse *beginResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); + AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); + AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); + AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); + AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); + AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize=1460); + AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); + AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr); + + size_t headers() const; // get header count + bool hasHeader(const String& name) const; // check if header exists + bool hasHeader(const __FlashStringHelper * data) const; // check if header exists + + AsyncWebHeader* getHeader(const String& name) const; + AsyncWebHeader* getHeader(const __FlashStringHelper * data) const; + AsyncWebHeader* getHeader(size_t num) const; + + size_t params() const; // get arguments count + bool hasParam(const String& name, bool post=false, bool file=false) const; + bool hasParam(const __FlashStringHelper * data, bool post=false, bool file=false) const; + + AsyncWebParameter* getParam(const String& name, bool post=false, bool file=false) const; + AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const; + AsyncWebParameter* getParam(size_t num) const; + + size_t args() const { return params(); } // get arguments count + const String& arg(const String& name) const; // get request argument value by name + const String& arg(const __FlashStringHelper * data) const; // get request argument value by F(name) + const String& arg(size_t i) const; // get request argument value by number + const String& argName(size_t i) const; // get request argument name by number + bool hasArg(const char* name) const; // check if argument exists + bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists + + const String& header(const char* name) const;// get request header value by name + const String& header(const __FlashStringHelper * data) const;// get request header value by F(name) + const String& header(size_t i) const; // get request header value by number + const String& headerName(size_t i) const; // get request header name by number + String urlDecode(const String& text) const; +}; + +/* + * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server) + * */ + +typedef std::function ArRequestFilterFunction; + +bool ON_STA_FILTER(AsyncWebServerRequest *request); + +bool ON_AP_FILTER(AsyncWebServerRequest *request); + +/* + * REWRITE :: One instance can be handle any Request (done by the Server) + * */ + +class AsyncWebRewrite { + protected: + String _from; + String _toUrl; + String _params; + ArRequestFilterFunction _filter; + public: + AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL){ + int index = _toUrl.indexOf('?'); + if (index > 0) { + _params = _toUrl.substring(index +1); + _toUrl = _toUrl.substring(0, index); + } + } + AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } + bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); } + const String& from(void) const { return _from; } + const String& toUrl(void) const { return _toUrl; } + const String& params(void) const { return _params; } +}; + +/* + * HANDLER :: One instance can be attached to any Request (done by the Server) + * */ + +class AsyncWebHandler { + protected: + ArRequestFilterFunction _filter; + String _username; + String _password; + public: + AsyncWebHandler():_username(""), _password(""){} + AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } + AsyncWebHandler& setAuthentication(const char *username, const char *password){ _username = String(username);_password = String(password); return *this; }; + bool filter(AsyncWebServerRequest *request){ return _filter == NULL || _filter(request); } + virtual ~AsyncWebHandler(){} + virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))){ + return false; + } + virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){} + virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))){} + virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))){} +}; + +/* + * RESPONSE :: One instance is created for each Request (attached by the Handler) + * */ + +typedef enum { + RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED +} WebResponseState; + +class AsyncWebServerResponse { + protected: + int _code; + LinkedList _headers; + String _contentType; + size_t _contentLength; + bool _sendContentLength; + bool _chunked; + size_t _headLength; + size_t _sentLength; + size_t _ackedLength; + size_t _writtenLength; + WebResponseState _state; + const char* _responseCodeToString(int code); + + public: + AsyncWebServerResponse(); + virtual ~AsyncWebServerResponse(); + virtual void setCode(int code); + virtual void setContentLength(size_t len); + virtual void setContentType(const String& type); + virtual void addHeader(const String& name, const String& value); + virtual String _assembleHead(uint8_t version); + virtual bool _started() const; + virtual bool _finished() const; + virtual bool _failed() const; + virtual bool _sourceValid() const; + virtual void _respond(AsyncWebServerRequest *request); + virtual size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); +}; + +/* + * SERVER :: One instance + * */ + +typedef std::function ArRequestHandlerFunction; +typedef std::function ArUploadHandlerFunction; +typedef std::function ArBodyHandlerFunction; + +class AsyncWebServer { + protected: + AsyncServer _server; + LinkedList _rewrites; + LinkedList _handlers; + AsyncCallbackWebHandler* _catchAllHandler; + + public: + AsyncWebServer(uint16_t port); + ~AsyncWebServer(); + + void begin(); + +#if ASYNC_TCP_SSL_ENABLED + void onSslFileRequest(AcSSlFileHandler cb, void* arg); + void beginSecure(const char *cert, const char *private_key_file, const char *password); +#endif + + AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite); + bool removeRewrite(AsyncWebRewrite* rewrite); + AsyncWebRewrite& rewrite(const char* from, const char* to); + + AsyncWebHandler& addHandler(AsyncWebHandler* handler); + bool removeHandler(AsyncWebHandler* handler); + + AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest); + AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest); + AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload); + AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody); + + AsyncStaticWebHandler& serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL); + + void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned + void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads + void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request) + + void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody + + void _handleDisconnect(AsyncWebServerRequest *request); + void _attachHandler(AsyncWebServerRequest *request); + void _rewriteRequest(AsyncWebServerRequest *request); +}; + +class DefaultHeaders { + using headers_t = LinkedList; + headers_t _headers; + + DefaultHeaders() + :_headers(headers_t([](AsyncWebHeader *h){ delete h; })) + {} +public: + using ConstIterator = headers_t::ConstIterator; + + void addHeader(const String& name, const String& value){ + _headers.add(new AsyncWebHeader(name, value)); + } + + ConstIterator begin() const { return _headers.begin(); } + ConstIterator end() const { return _headers.end(); } + + DefaultHeaders(DefaultHeaders const &) = delete; + DefaultHeaders &operator=(DefaultHeaders const &) = delete; + static DefaultHeaders &Instance() { + static DefaultHeaders instance; + return instance; + } +}; + +#include "WebResponseImpl.h" +#include "WebHandlerImpl.h" +#include "AsyncWebSocket.h" +#include "AsyncEventSource.h" + +#endif /* _AsyncWebServer_H_ */ diff --git a/libraries/ESPAsyncWebServer/src/SPIFFSEditor.cpp b/libraries/ESPAsyncWebServer/src/SPIFFSEditor.cpp new file mode 100644 index 00000000..a385a974 --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/SPIFFSEditor.cpp @@ -0,0 +1,544 @@ +#include "SPIFFSEditor.h" +#include + +//File: edit.htm.gz, Size: 4151 +#define edit_htm_gz_len 4151 +const uint8_t edit_htm_gz[] PROGMEM = { + 0x1F, 0x8B, 0x08, 0x08, 0xB8, 0x94, 0xB1, 0x59, 0x00, 0x03, 0x65, 0x64, 0x69, 0x74, 0x2E, 0x68, + 0x74, 0x6D, 0x00, 0xB5, 0x3A, 0x0B, 0x7B, 0xDA, 0xB8, 0xB2, 0x7F, 0xC5, 0x71, 0xCF, 0x66, 0xED, + 0x83, 0x31, 0x90, 0xA4, 0xD9, 0xD6, 0xC4, 0xC9, 0x42, 0x92, 0x36, 0x6D, 0xF3, 0x6A, 0x80, 0xB6, + 0x69, 0x4F, 0xEE, 0x7E, 0xC2, 0x16, 0xA0, 0xC6, 0x96, 0x5D, 0x5B, 0x0E, 0x49, 0x59, 0xFE, 0xFB, + 0x9D, 0x91, 0x6C, 0xB0, 0x09, 0x69, 0x77, 0xCF, 0xBD, 0xBB, 0xDD, 0x2D, 0x92, 0x46, 0x33, 0x9A, + 0x19, 0xCD, 0x53, 0xDE, 0xBD, 0x8D, 0xA3, 0x8B, 0xC3, 0xFE, 0xF5, 0xE5, 0xB1, 0x36, 0x11, 0x61, + 0xB0, 0xBF, 0x87, 0x7F, 0x6B, 0x01, 0xE1, 0x63, 0x97, 0xF2, 0xFD, 0x3D, 0xC1, 0x44, 0x40, 0xF7, + 0x8F, 0x7B, 0x97, 0xDA, 0xB1, 0xCF, 0x44, 0x94, 0xEC, 0x35, 0xD4, 0xCA, 0x5E, 0x2A, 0x1E, 0x02, + 0xAA, 0x85, 0xD4, 0x67, 0xC4, 0x4D, 0xBD, 0x84, 0xC2, 0x66, 0xDB, 0x0B, 0x67, 0xDF, 0xEB, 0x8C, + 0xFB, 0xF4, 0xDE, 0xD9, 0x6E, 0x36, 0xDB, 0x71, 0x94, 0x32, 0xC1, 0x22, 0xEE, 0x90, 0x61, 0x1A, + 0x05, 0x99, 0xA0, 0xED, 0x80, 0x8E, 0x84, 0xF3, 0x3C, 0xBE, 0x6F, 0x0F, 0xA3, 0xC4, 0xA7, 0x89, + 0xD3, 0x8A, 0xEF, 0x35, 0x00, 0x31, 0x5F, 0x7B, 0xB6, 0xB3, 0xB3, 0xD3, 0x1E, 0x12, 0xEF, 0x76, + 0x9C, 0x44, 0x19, 0xF7, 0xEB, 0x5E, 0x14, 0x44, 0x89, 0xF3, 0x6C, 0xF4, 0x1C, 0xFF, 0xB4, 0x7D, + 0x96, 0xC6, 0x01, 0x79, 0x70, 0x78, 0xC4, 0x29, 0xE0, 0xDE, 0xD7, 0xD3, 0x09, 0xF1, 0xA3, 0xA9, + 0xD3, 0xD4, 0x9A, 0x5A, 0xAB, 0x09, 0x44, 0x92, 0xF1, 0x90, 0x18, 0x4D, 0x0B, 0xFF, 0xD8, 0x3B, + 0x66, 0x7B, 0x14, 0x71, 0x51, 0x4F, 0xD9, 0x77, 0xEA, 0xB4, 0xB6, 0xE0, 0x34, 0x39, 0x1D, 0x91, + 0x90, 0x05, 0x0F, 0x4E, 0x4A, 0x78, 0x5A, 0x4F, 0x69, 0xC2, 0x46, 0x6A, 0x79, 0x4A, 0xD9, 0x78, + 0x22, 0x9C, 0xDF, 0x9A, 0xCD, 0x39, 0xF0, 0xAF, 0x65, 0xC1, 0x2C, 0x60, 0x29, 0x20, 0xA3, 0x78, + 0xEA, 0x3C, 0x11, 0xC5, 0x4E, 0x53, 0xB1, 0xDE, 0x6C, 0x87, 0x24, 0x19, 0x33, 0x0E, 0x83, 0x98, + 0xF8, 0x3E, 0xE3, 0x63, 0x47, 0xA1, 0x05, 0x6C, 0xB6, 0x90, 0x36, 0xA1, 0x01, 0x11, 0xEC, 0x8E, + 0xB6, 0x43, 0xC6, 0xEB, 0x53, 0xE6, 0x8B, 0x89, 0xB3, 0x0B, 0x3C, 0xB6, 0xBD, 0x2C, 0x49, 0x41, + 0xA6, 0x38, 0x62, 0x5C, 0xD0, 0x44, 0xA2, 0xA5, 0x31, 0xE1, 0xB3, 0x5C, 0x54, 0x54, 0x40, 0x21, + 0x27, 0xE3, 0x01, 0xE3, 0xB4, 0x3E, 0x0C, 0x22, 0xEF, 0x76, 0x71, 0xD2, 0x6E, 0x7C, 0x9F, 0x9F, + 0xE5, 0x4C, 0xA2, 0x3B, 0x9A, 0xCC, 0x96, 0xEA, 0x92, 0xD8, 0x15, 0x60, 0x85, 0x34, 0xA5, 0x74, + 0x6E, 0x8B, 0xBB, 0x0C, 0xA0, 0x96, 0xFC, 0x05, 0x29, 0x17, 0xFC, 0x2F, 0x45, 0x5A, 0x11, 0x5C, + 0xA1, 0x30, 0x1E, 0x67, 0x62, 0xF6, 0xF8, 0x2A, 0xA3, 0x98, 0x78, 0x4C, 0x3C, 0xA0, 0xFC, 0xB0, + 0x6D, 0x86, 0xBA, 0x04, 0xAC, 0x24, 0x24, 0x81, 0x86, 0x3A, 0xD7, 0x3E, 0xD0, 0xC4, 0x27, 0x9C, + 0x58, 0x9D, 0x84, 0x91, 0xC0, 0xEA, 0x2D, 0xB5, 0x5E, 0x0F, 0xA3, 0xEF, 0xF5, 0x0C, 0xC6, 0x30, + 0x0F, 0xA8, 0x27, 0x94, 0x92, 0xE1, 0x1E, 0x86, 0xB7, 0x4C, 0x3C, 0x06, 0x3C, 0x5A, 0x28, 0xA9, + 0x4B, 0x2A, 0x69, 0xA2, 0x2E, 0xB0, 0x25, 0xD5, 0x83, 0x1C, 0x4B, 0xC9, 0x95, 0x50, 0xF5, 0x61, + 0x24, 0x44, 0x14, 0x4A, 0x93, 0x5B, 0x08, 0xAC, 0x49, 0xAB, 0x79, 0xF1, 0xE8, 0x46, 0xD6, 0x6B, + 0xBF, 0x44, 0xBE, 0x0D, 0x7A, 0x15, 0xCC, 0x23, 0x41, 0x9D, 0x04, 0x6C, 0xCC, 0x9D, 0x90, 0xF9, + 0x7E, 0x40, 0x4B, 0x56, 0xEB, 0x64, 0x49, 0x60, 0xF8, 0x44, 0x10, 0x87, 0x85, 0x64, 0x4C, 0x1B, + 0x31, 0x1F, 0x03, 0x34, 0xA5, 0xBB, 0x3B, 0x16, 0xFB, 0xD0, 0xBD, 0xB8, 0x9A, 0x36, 0xDF, 0xBD, + 0x1E, 0x47, 0x1D, 0xF8, 0xE7, 0xBC, 0x37, 0x98, 0x1C, 0x0F, 0xC6, 0x30, 0xEA, 0xE2, 0xB4, 0xF3, + 0xFE, 0xB0, 0xF3, 0x1E, 0x7E, 0x0E, 0x5B, 0xB5, 0xAF, 0xA3, 0x6F, 0xB8, 0xD0, 0x7D, 0xED, 0x77, + 0xFB, 0x83, 0xE3, 0x4E, 0xE7, 0x5D, 0xE3, 0xCD, 0xF9, 0xF4, 0xE3, 0xBB, 0x5D, 0x04, 0x77, 0x83, + 0xE6, 0xD5, 0x87, 0x49, 0x73, 0xB0, 0xF5, 0x32, 0xF4, 0x4F, 0xFC, 0x89, 0x17, 0x0E, 0x3A, 0xEF, + 0x3F, 0x5E, 0xDD, 0x5D, 0x87, 0x83, 0x71, 0xEF, 0x63, 0x6B, 0xF2, 0x79, 0xEB, 0x43, 0xEF, 0xF3, + 0xC7, 0x57, 0xB7, 0xF4, 0xD3, 0xC9, 0xDB, 0xCF, 0xFD, 0x29, 0x20, 0x1C, 0x45, 0xBD, 0xC1, 0x55, + 0xF7, 0x43, 0x77, 0xFC, 0xB9, 0xEB, 0x1D, 0xDF, 0x0F, 0x83, 0xF3, 0xEE, 0xEB, 0xCE, 0xB0, 0xB3, + 0xE5, 0x51, 0x3A, 0xEE, 0x5F, 0x75, 0xB3, 0x37, 0xEF, 0x2E, 0xC6, 0x8C, 0x4D, 0x7A, 0x9F, 0xCF, + 0xFB, 0xDE, 0xE1, 0xF3, 0xD3, 0xC1, 0x49, 0x87, 0x4D, 0xCE, 0xDF, 0x5E, 0x35, 0x6F, 0x5F, 0xBF, + 0x3B, 0x3C, 0xF2, 0xAE, 0xDF, 0x5E, 0xEF, 0x1E, 0x6D, 0x37, 0x7E, 0xFB, 0xED, 0xCC, 0xBF, 0x60, + 0xBC, 0x7F, 0xF7, 0xBD, 0x33, 0x3E, 0x9C, 0xBE, 0x78, 0x48, 0xFB, 0x93, 0x37, 0x77, 0xBC, 0xF1, + 0x21, 0xFA, 0xFA, 0xE6, 0xE1, 0x0C, 0xFE, 0xBB, 0xBC, 0xAC, 0x0D, 0x7B, 0xAD, 0x74, 0xF0, 0xFE, + 0xCD, 0x87, 0xAD, 0xF4, 0xE5, 0xF3, 0xB8, 0x7B, 0x74, 0x74, 0x17, 0x0E, 0x2F, 0x1B, 0xA1, 0x7F, + 0x3B, 0x12, 0x2F, 0xB6, 0x45, 0x7C, 0x3D, 0xCE, 0x3E, 0x7F, 0x7B, 0xFE, 0x76, 0xD2, 0xB8, 0xA0, + 0xE4, 0x7A, 0x52, 0x7B, 0xF8, 0xFE, 0xF0, 0x62, 0xD2, 0x3F, 0xB9, 0x3B, 0x0F, 0xC8, 0xFD, 0xF9, + 0xB9, 0xF7, 0x3D, 0xAC, 0x05, 0xE4, 0xE5, 0x45, 0x3F, 0x20, 0x49, 0x6B, 0xE0, 0x77, 0x1A, 0xB5, + 0xC3, 0xAD, 0xCE, 0x8E, 0x48, 0xAE, 0x0E, 0xF9, 0xD1, 0xF6, 0xD7, 0xDE, 0x8B, 0x6E, 0xB7, 0x15, + 0x0D, 0xBF, 0x6D, 0xBD, 0xBE, 0xDD, 0x7D, 0x3D, 0xD8, 0x7D, 0x3F, 0x7C, 0xDF, 0xE9, 0xED, 0x74, + 0x07, 0xE4, 0xBA, 0xF7, 0xBE, 0x33, 0xDA, 0x19, 0x4E, 0x26, 0xEF, 0xDE, 0xF5, 0x5F, 0xF9, 0x9D, + 0xEF, 0x49, 0xE7, 0x62, 0xDA, 0xB9, 0x3F, 0x1E, 0x74, 0x4E, 0x6A, 0xEF, 0x8E, 0xCF, 0x9A, 0xAD, + 0xDE, 0xF5, 0xF6, 0xF8, 0x6C, 0x77, 0xDA, 0x4D, 0x8F, 0x3B, 0xEF, 0xBB, 0xCD, 0xF1, 0xDB, 0x5A, + 0x48, 0x3E, 0x47, 0x87, 0xDB, 0xE3, 0x37, 0xBB, 0xEC, 0xF2, 0x9A, 0x74, 0xDE, 0x74, 0xDF, 0xA6, + 0xEC, 0x2A, 0x3C, 0x19, 0x34, 0x3B, 0x9D, 0xD3, 0x0B, 0xFA, 0xEA, 0x70, 0x9B, 0xBC, 0xDB, 0xF2, + 0x3E, 0x82, 0xFE, 0x07, 0x9F, 0xE8, 0x6F, 0xB5, 0xCE, 0xF4, 0xA2, 0x19, 0x78, 0x2F, 0x69, 0xFF, + 0xE4, 0xBA, 0x2F, 0x6F, 0xE7, 0x38, 0x78, 0xD5, 0xBF, 0xED, 0x65, 0xEF, 0xC3, 0xC3, 0x43, 0x53, + 0xE3, 0x51, 0x3D, 0xA1, 0x31, 0x25, 0xA2, 0x1C, 0xAE, 0x16, 0xFE, 0x01, 0xB6, 0xB5, 0xB4, 0xC2, + 0xDC, 0x4F, 0x05, 0xBD, 0x17, 0x75, 0x9F, 0x7A, 0x51, 0x42, 0xE4, 0x1E, 0x40, 0xA0, 0x09, 0x9A, + 0xD8, 0xFC, 0x77, 0x19, 0x3F, 0x35, 0x15, 0x3F, 0x35, 0xC2, 0x7D, 0xCD, 0x28, 0x1C, 0x01, 0x83, + 0x87, 0x4F, 0xEF, 0x98, 0x47, 0xEB, 0x31, 0xBB, 0xA7, 0x41, 0x5D, 0x22, 0x3B, 0x4D, 0x73, 0x26, + 0xFD, 0xAD, 0xD8, 0x46, 0x38, 0x98, 0x9A, 0xA4, 0x5A, 0x2C, 0xF8, 0x5F, 0x89, 0x47, 0x21, 0xB0, + 0x81, 0xCB, 0x84, 0xF8, 0xAB, 0x7C, 0x27, 0x4A, 0xEA, 0xC3, 0x6C, 0x3C, 0x62, 0xF7, 0xE0, 0xD0, + 0x23, 0xC6, 0x99, 0xA0, 0x5A, 0x2B, 0x9D, 0xFF, 0x5E, 0x90, 0xB9, 0xA5, 0x0F, 0xA3, 0x84, 0x84, + 0x34, 0xD5, 0xFE, 0x22, 0x99, 0xD9, 0x28, 0x89, 0xC2, 0x65, 0x10, 0x99, 0x8B, 0xA8, 0x34, 0x99, + 0xCF, 0x9F, 0x65, 0x71, 0x10, 0x11, 0x10, 0x73, 0x4D, 0xE4, 0x50, 0xF1, 0x34, 0x91, 0x6E, 0xB5, + 0x88, 0xAB, 0xB9, 0x9B, 0x6D, 0xA1, 0x5B, 0x96, 0xDD, 0x7A, 0x6B, 0x67, 0xE9, 0xBA, 0x75, 0xB9, + 0x17, 0xE3, 0xFD, 0x9A, 0x4C, 0x81, 0xF1, 0xA0, 0x14, 0xEE, 0x9E, 0x09, 0x50, 0xE9, 0x13, 0x87, + 0xCB, 0x43, 0xF2, 0xC8, 0xB0, 0x60, 0x40, 0x05, 0xEA, 0x96, 0x8C, 0xD4, 0x85, 0x24, 0xB0, 0x6F, + 0xFE, 0x8C, 0xCA, 0xBC, 0x67, 0x3D, 0x8B, 0x13, 0xB8, 0x0D, 0x3A, 0xFD, 0x11, 0xCD, 0x42, 0xA6, + 0x2A, 0x6D, 0x45, 0x53, 0x65, 0xBC, 0x5C, 0x84, 0x65, 0xDA, 0x93, 0xBC, 0x16, 0xA4, 0x1F, 0x4B, + 0x05, 0xE0, 0x05, 0x37, 0xCF, 0x91, 0x9B, 0x1F, 0x6A, 0x75, 0x7B, 0xF7, 0x97, 0x9C, 0x87, 0x9D, + 0xE6, 0x2F, 0x73, 0x3B, 0xDF, 0x5B, 0xA4, 0xE4, 0x56, 0x13, 0xFE, 0x29, 0x32, 0xEF, 0x8B, 0x25, + 0x0B, 0xC3, 0xE7, 0xF8, 0xA7, 0x60, 0x10, 0xE9, 0x94, 0x80, 0xDB, 0x3B, 0x2F, 0x5F, 0xF8, 0xC3, + 0x02, 0x98, 0x0B, 0xF6, 0x24, 0x3C, 0x21, 0x3E, 0xCB, 0x52, 0xE7, 0x79, 0xF3, 0x97, 0x5C, 0x9F, + 0x5B, 0x3B, 0x28, 0xFB, 0xE2, 0x2E, 0x71, 0xB2, 0xB4, 0xD8, 0x34, 0x66, 0x5C, 0xDB, 0x4A, 0x35, + 0xBC, 0x6F, 0x92, 0x2C, 0x0C, 0xB3, 0x92, 0xED, 0xE7, 0xBF, 0x2F, 0x4D, 0x13, 0xF7, 0xCF, 0x9A, + 0xBF, 0xCC, 0x44, 0x02, 0xD9, 0x64, 0x04, 0xB9, 0xC6, 0x49, 0x22, 0x41, 0x04, 0x35, 0x9A, 0xE6, + 0x1C, 0x84, 0x5B, 0x03, 0xD8, 0xDE, 0x6D, 0xFA, 0x74, 0x6C, 0xCE, 0xE7, 0x7B, 0x0D, 0x99, 0xD7, + 0xA0, 0x6C, 0xF1, 0x12, 0x16, 0x8B, 0xFD, 0x51, 0xC6, 0x3D, 0xE4, 0x41, 0x1B, 0x53, 0x83, 0x9A, + 0xB3, 0x84, 0x8A, 0x2C, 0xE1, 0x9A, 0x1F, 0x79, 0x19, 0x1A, 0xBB, 0x3D, 0xA6, 0xE2, 0x58, 0xD9, + 0x7D, 0xF7, 0xE1, 0x8D, 0x0F, 0x3B, 0xE6, 0x0B, 0x04, 0x6F, 0x2D, 0x02, 0x38, 0x30, 0x9C, 0x97, + 0xE3, 0x54, 0xF6, 0x43, 0x82, 0x01, 0x22, 0xEF, 0xE8, 0x83, 0x41, 0x2D, 0xB1, 0x40, 0xA4, 0x36, + 0xAE, 0x1B, 0xC5, 0x2E, 0x80, 0x71, 0x73, 0x76, 0x07, 0x4A, 0x20, 0x2E, 0xFD, 0x22, 0x6E, 0x2C, + 0xE6, 0x72, 0xF8, 0x69, 0xE7, 0xBB, 0xC9, 0x1E, 0x3B, 0xA8, 0xB7, 0x1C, 0xB2, 0xCF, 0x0E, 0x5A, + 0xE0, 0x5E, 0x65, 0x6E, 0xE4, 0xB9, 0xAF, 0x58, 0x40, 0x07, 0xB9, 0xC3, 0xE1, 0x31, 0x48, 0x6C, + 0xB1, 0x85, 0x28, 0xE2, 0x5B, 0xCD, 0xE6, 0x86, 0x4B, 0x0F, 0x48, 0x00, 0x39, 0xCC, 0xD0, 0x8F, + 0xAF, 0xAE, 0x2E, 0xAE, 0xBE, 0xE8, 0x35, 0x5A, 0xD3, 0x6F, 0x1C, 0x4D, 0xAF, 0x71, 0xD3, 0x11, + 0x76, 0x42, 0x47, 0x09, 0x4D, 0x27, 0x97, 0x44, 0x4C, 0x8C, 0xD4, 0xBE, 0x23, 0x41, 0x56, 0x16, + 0x84, 0xA1, 0xDC, 0xC8, 0xA2, 0x70, 0x39, 0x9D, 0x6A, 0xAF, 0x40, 0xCD, 0x47, 0x90, 0xEA, 0xDA, + 0xC2, 0x26, 0x71, 0x4C, 0xB9, 0x6F, 0xE8, 0x31, 0x20, 0xEA, 0x16, 0x35, 0xAD, 0x84, 0x7E, 0xCB, + 0x68, 0x2A, 0x52, 0x1B, 0x2C, 0xD7, 0xD0, 0x2F, 0x07, 0x7D, 0xDD, 0xD2, 0x1B, 0xE8, 0x47, 0x3A, + 0xF0, 0x46, 0xCC, 0x39, 0x52, 0x89, 0x5C, 0xD0, 0xA4, 0x3E, 0xCC, 0xC0, 0xA0, 0xB8, 0x6E, 0xB6, + 0x23, 0x9B, 0x71, 0x4E, 0x93, 0x93, 0xFE, 0xD9, 0xA9, 0xAB, 0x5F, 0x29, 0x46, 0xB4, 0x53, 0x28, + 0x48, 0x74, 0x4B, 0x5E, 0x51, 0x7E, 0xC8, 0xE1, 0x84, 0x05, 0xBE, 0x11, 0x99, 0x6D, 0x24, 0xE1, + 0x49, 0x12, 0xB2, 0x40, 0x01, 0x0A, 0x9E, 0x2D, 0x1E, 0x62, 0xEA, 0xEA, 0x23, 0x50, 0x86, 0x6E, + 0x79, 0x76, 0x98, 0x05, 0x82, 0xC5, 0x01, 0x75, 0x37, 0x5A, 0x30, 0xE3, 0x60, 0x41, 0xAE, 0x8E, + 0xB9, 0x19, 0x61, 0xCC, 0x77, 0x75, 0x15, 0xA1, 0xF2, 0xB8, 0xB6, 0xEE, 0x14, 0x4F, 0x9D, 0x92, + 0x56, 0x4E, 0x49, 0xCB, 0xB8, 0x4A, 0xE0, 0x34, 0x3F, 0x18, 0xC3, 0x3C, 0xCE, 0xD4, 0x51, 0x05, + 0xCC, 0xA7, 0x23, 0x02, 0x9C, 0x7C, 0x40, 0x6D, 0xBA, 0x7A, 0x63, 0xDD, 0x41, 0xA9, 0x3A, 0xC8, + 0xAF, 0x6A, 0xC4, 0x2F, 0x6B, 0x44, 0xDD, 0xEE, 0x3A, 0x64, 0x5F, 0x21, 0x07, 0x55, 0xE4, 0xA0, + 0x8C, 0x7C, 0x28, 0x8D, 0x64, 0x1D, 0x72, 0xA0, 0x90, 0x93, 0x8A, 0x88, 0x89, 0x14, 0x51, 0x85, + 0xBD, 0x3A, 0x6A, 0x13, 0x05, 0xD2, 0xAD, 0xA4, 0x22, 0x66, 0x62, 0x83, 0x97, 0x92, 0x61, 0x40, + 0x7D, 0x77, 0xA3, 0x09, 0x33, 0x2C, 0xB6, 0xDD, 0xAD, 0xE6, 0x9A, 0x33, 0x12, 0x75, 0x46, 0x56, + 0x65, 0x30, 0x2B, 0x33, 0xA8, 0xF5, 0xC8, 0x1D, 0xD5, 0xD6, 0x31, 0x98, 0x99, 0x56, 0x60, 0x47, + 0xDC, 0x0B, 0x98, 0x77, 0xEB, 0x2E, 0xBD, 0xC5, 0x9C, 0xB1, 0x85, 0x85, 0x5A, 0x5C, 0x06, 0xBA, + 0x01, 0x94, 0x5E, 0x8B, 0xA5, 0x7C, 0x80, 0xFA, 0x9E, 0x5B, 0xD9, 0x5A, 0x02, 0xDC, 0xA6, 0xF7, + 0xD4, 0x3B, 0x8C, 0xC2, 0x90, 0xA0, 0xED, 0xA6, 0xC0, 0x41, 0x3E, 0xD1, 0xCD, 0xB9, 0x15, 0xAD, + 0xC5, 0x79, 0xC2, 0x45, 0x2C, 0x7F, 0x3D, 0x8B, 0x23, 0x03, 0x5C, 0xCE, 0xF5, 0x6C, 0xD4, 0x61, + 0x6A, 0x83, 0x1E, 0xC7, 0x62, 0xF2, 0x13, 0x17, 0x2A, 0x0C, 0x54, 0xA2, 0x7C, 0x69, 0xDE, 0x58, + 0x0B, 0x91, 0x56, 0x7C, 0xEA, 0xA2, 0xB7, 0xE2, 0x54, 0xA8, 0xBC, 0x8A, 0x5D, 0x9A, 0x4B, 0x1D, + 0x94, 0x61, 0xB9, 0xBD, 0x2F, 0xA0, 0xFA, 0x7C, 0x0E, 0xE7, 0x01, 0xFF, 0x13, 0x68, 0xF9, 0xE8, + 0x5F, 0x17, 0x60, 0xC9, 0xA3, 0x34, 0x78, 0x8B, 0xBB, 0x0D, 0xE3, 0xC0, 0xF9, 0x8F, 0x6D, 0x7C, + 0xF9, 0x1F, 0xFB, 0xA6, 0x66, 0x9A, 0x07, 0xFF, 0x6A, 0x48, 0x0D, 0x1B, 0xC2, 0xFC, 0xD2, 0xBA, + 0xB1, 0x08, 0x80, 0xED, 0x7F, 0x9B, 0xFF, 0xB1, 0x25, 0xB8, 0x02, 0x6B, 0xDF, 0x45, 0x90, 0x49, + 0xF0, 0x24, 0x34, 0xB0, 0x68, 0xA4, 0x91, 0xCD, 0x4D, 0x43, 0xB8, 0xA4, 0x72, 0x8D, 0x35, 0x51, + 0xD3, 0x6D, 0x88, 0x53, 0x50, 0x5B, 0xAC, 0x04, 0xBF, 0x3E, 0x24, 0x7A, 0x15, 0x5B, 0x17, 0x00, + 0xC9, 0x3D, 0xCA, 0x0C, 0x3D, 0x22, 0x97, 0x52, 0xCB, 0x0C, 0x02, 0x42, 0xA7, 0x89, 0xE7, 0x2A, + 0xAD, 0x1D, 0x14, 0x30, 0x17, 0xA2, 0xE0, 0xBC, 0x1C, 0x2D, 0x15, 0xEA, 0xAA, 0xFD, 0x17, 0x0A, + 0xA3, 0xD6, 0x12, 0x8A, 0x04, 0x31, 0xAD, 0xD8, 0x79, 0xC6, 0x72, 0x75, 0x4C, 0x59, 0xBA, 0x35, + 0x59, 0x5D, 0x96, 0xAD, 0x04, 0xAE, 0x2F, 0x8D, 0xFE, 0xD7, 0x3D, 0x16, 0x8E, 0xB5, 0x12, 0x3F, + 0xF8, 0x97, 0xFB, 0x2B, 0x46, 0xE4, 0xCD, 0x3F, 0xBC, 0x21, 0x70, 0x05, 0xA6, 0x41, 0x6D, 0x1E, + 0x4D, 0x0D, 0xB3, 0xF6, 0xAB, 0xAE, 0x49, 0x8A, 0xAE, 0x1E, 0x92, 0xFB, 0xBC, 0xA7, 0xC4, 0x8C, + 0xD7, 0xD6, 0x70, 0x5E, 0xB4, 0x28, 0xF9, 0x82, 0xEC, 0xE6, 0x48, 0x26, 0xA2, 0xB6, 0x56, 0x64, + 0x52, 0xD5, 0xCA, 0xE8, 0x5A, 0x63, 0xFF, 0xD7, 0x4A, 0x40, 0xB7, 0x98, 0xBA, 0x4E, 0x15, 0x8C, + 0xB3, 0x00, 0x1C, 0x93, 0x3E, 0x1D, 0x69, 0x03, 0x26, 0x03, 0x75, 0x35, 0x46, 0x5A, 0x81, 0xC1, + 0xCC, 0x03, 0xC3, 0x2B, 0xFB, 0xF3, 0x1E, 0x16, 0xBF, 0xFB, 0x97, 0xAA, 0xAA, 0x81, 0xD4, 0x8B, + 0x33, 0x5D, 0x59, 0x59, 0xD5, 0x4B, 0xE0, 0xD2, 0x08, 0xA0, 0x5B, 0x8B, 0x3C, 0x3A, 0x8C, 0xFC, + 0x87, 0x52, 0xF6, 0x4D, 0xBB, 0x0F, 0x87, 0x01, 0x49, 0xD3, 0x73, 0xB8, 0x01, 0x43, 0xF7, 0x42, + 0x50, 0xB8, 0xB2, 0xC2, 0xFD, 0xE6, 0xE6, 0x66, 0x15, 0x29, 0xA1, 0x21, 0x14, 0xDB, 0x8A, 0x2B, + 0xF0, 0x49, 0xD3, 0xF1, 0x81, 0x30, 0x18, 0xD2, 0x1A, 0xC6, 0xF0, 0x25, 0xE3, 0x47, 0x5C, 0x71, + 0xF4, 0xF4, 0x22, 0xA6, 0xFC, 0x33, 0xDC, 0x95, 0x32, 0xCB, 0x1A, 0xAD, 0xA6, 0x68, 0xFA, 0x8F, + 0xD8, 0x3E, 0xCA, 0x0D, 0x76, 0xC1, 0x7A, 0xBA, 0x56, 0xA1, 0xFC, 0x9F, 0x61, 0xB9, 0x94, 0x28, + 0xD6, 0x70, 0x9C, 0x40, 0x80, 0x5A, 0xC3, 0x31, 0xC4, 0x1A, 0x41, 0x17, 0xFC, 0x26, 0x6B, 0xF9, + 0xCD, 0xFE, 0x19, 0x7E, 0x97, 0x76, 0x1E, 0x15, 0x25, 0x91, 0xAA, 0xAF, 0x50, 0x02, 0x9F, 0xDD, + 0xE9, 0xA6, 0x15, 0xB9, 0x55, 0x0A, 0x50, 0x1B, 0x46, 0x41, 0xD0, 0x8F, 0xE2, 0x83, 0x27, 0xD6, + 0x9D, 0xC5, 0x7A, 0x31, 0xC8, 0xD9, 0x5C, 0x6E, 0xB1, 0xBC, 0xB5, 0x44, 0x4F, 0xA1, 0xEC, 0x5F, + 0x4B, 0x15, 0x01, 0x3F, 0x23, 0x8B, 0x7B, 0xAC, 0xD4, 0xA5, 0x36, 0x28, 0x0F, 0x56, 0x3F, 0xD5, + 0x3C, 0xCB, 0x5F, 0xCC, 0xAE, 0x6B, 0x51, 0x9B, 0xC0, 0x38, 0x57, 0x92, 0x8B, 0x4A, 0xB2, 0xC8, + 0x13, 0x01, 0xA8, 0x58, 0xC7, 0x2E, 0xC4, 0x4D, 0x6B, 0x7A, 0x7C, 0xBF, 0x5C, 0x83, 0xC2, 0xDF, + 0xF5, 0xD5, 0x12, 0x33, 0x08, 0xC4, 0xD3, 0x95, 0x4B, 0x29, 0x5F, 0x37, 0x29, 0x8A, 0x0E, 0x62, + 0x47, 0xA3, 0x51, 0x4A, 0xC5, 0x47, 0x0C, 0x49, 0x56, 0xB2, 0x98, 0x9F, 0xC8, 0x90, 0x04, 0x8C, + 0x45, 0x3C, 0x8C, 0xB2, 0x94, 0x46, 0x99, 0xA8, 0xA4, 0x16, 0x63, 0x21, 0xCC, 0x5E, 0xFA, 0xE7, + 0x9F, 0x8B, 0xC9, 0x7E, 0x5A, 0x0B, 0x96, 0xD3, 0xEB, 0x3D, 0xBF, 0x34, 0xD9, 0xF7, 0x6B, 0x89, + 0xB9, 0x7A, 0xE9, 0xFF, 0x67, 0x4B, 0x21, 0x65, 0x4B, 0xF1, 0xB0, 0x54, 0x2E, 0x62, 0x62, 0x29, + 0xE6, 0xC9, 0x82, 0x91, 0x97, 0x7C, 0x16, 0x0D, 0x1A, 0x2B, 0x25, 0x55, 0x9E, 0x97, 0x7D, 0x95, + 0x43, 0x40, 0x59, 0x71, 0xE5, 0x35, 0x11, 0x06, 0x34, 0xE0, 0x63, 0x64, 0xF2, 0x41, 0xEB, 0xA7, + 0xD1, 0x94, 0x26, 0x87, 0x24, 0xA5, 0x06, 0x24, 0xCD, 0x65, 0xDC, 0x41, 0xA8, 0xE9, 0x04, 0xEB, + 0x76, 0x6D, 0x6E, 0x12, 0x05, 0xCE, 0x33, 0x77, 0xC4, 0xB1, 0x26, 0x03, 0xF9, 0xB2, 0xCA, 0x09, + 0xD4, 0xC6, 0xBE, 0x12, 0xA4, 0x3E, 0x52, 0x25, 0xA8, 0x61, 0x5A, 0xD0, 0x76, 0xC0, 0x35, 0x5F, + 0x26, 0x51, 0x4C, 0xC6, 0xB2, 0x07, 0x83, 0x35, 0x74, 0x0F, 0xA4, 0x66, 0x6D, 0x34, 0x91, 0x60, + 0xA9, 0x73, 0x29, 0xFC, 0x66, 0xD9, 0xC2, 0x70, 0x4B, 0x57, 0xC9, 0xB0, 0xBD, 0xF4, 0xA5, 0x35, + 0x59, 0x83, 0xE0, 0x0B, 0x6C, 0x62, 0xE0, 0x1E, 0x68, 0x64, 0xF2, 0x7B, 0x00, 0x77, 0x6B, 0xB6, + 0xA3, 0x3D, 0xD6, 0x8E, 0x6A, 0x35, 0x53, 0x55, 0xE9, 0xAE, 0x0B, 0x6D, 0x4E, 0x74, 0x23, 0x0B, + 0x4B, 0x10, 0xAA, 0x9A, 0x59, 0x0C, 0x38, 0x1B, 0x81, 0xAA, 0xBA, 0xC0, 0x11, 0xD6, 0x98, 0x66, + 0xA9, 0x23, 0xF1, 0x97, 0x1D, 0xC9, 0x13, 0xB5, 0x07, 0x95, 0xF5, 0x05, 0xD4, 0x31, 0xAB, 0x25, + 0x86, 0x30, 0xD3, 0x29, 0x13, 0xDE, 0x04, 0x03, 0x90, 0x07, 0x5A, 0xD5, 0x05, 0x14, 0xB5, 0x8E, + 0x1C, 0x4D, 0x44, 0xB8, 0x1C, 0x05, 0xF9, 0xF0, 0x6B, 0x9A, 0x0F, 0xBC, 0xB4, 0x18, 0xDD, 0x97, + 0x80, 0x50, 0xD2, 0xE6, 0xE0, 0x88, 0x8F, 0xF2, 0x21, 0xF4, 0xB2, 0x05, 0x9D, 0x02, 0x58, 0xFC, + 0xC6, 0x71, 0x3E, 0x8A, 0x27, 0xC5, 0x68, 0x42, 0xEF, 0x17, 0x78, 0x51, 0x01, 0xF5, 0xA9, 0xEE, + 0x28, 0x1B, 0xDB, 0x68, 0xCE, 0xF3, 0x41, 0x6B, 0x29, 0x7F, 0xF0, 0xFF, 0x28, 0x7F, 0xCC, 0xC7, + 0x85, 0x34, 0x71, 0x31, 0x1A, 0xB3, 0x42, 0x96, 0x61, 0x18, 0xFF, 0x90, 0x93, 0xA4, 0xD4, 0x13, + 0x97, 0x7A, 0x5A, 0xF1, 0xB3, 0xB6, 0x53, 0x98, 0x8E, 0x31, 0xAA, 0xF8, 0xE3, 0xC8, 0xF6, 0xF0, + 0xF7, 0x3C, 0xF2, 0x65, 0x6D, 0x69, 0x5A, 0xA1, 0x31, 0x82, 0x3A, 0x57, 0x37, 0xCB, 0x7E, 0x9A, + 0xFD, 0xB7, 0xAD, 0xE8, 0xD1, 0xF1, 0xE9, 0x71, 0xFF, 0xB8, 0x5C, 0x38, 0x23, 0xE7, 0x25, 0x93, + 0x8A, 0x2B, 0x5D, 0xFA, 0xB2, 0x22, 0x80, 0x02, 0x1B, 0x45, 0x01, 0x7B, 0xDD, 0xDC, 0x54, 0x7E, + 0xF1, 0xB6, 0x77, 0x71, 0x6E, 0xC7, 0x24, 0x01, 0x8F, 0x24, 0x15, 0xE6, 0xC2, 0x82, 0x44, 0xF9, + 0xE0, 0xD7, 0xC7, 0xA5, 0x72, 0x5D, 0x7E, 0x61, 0x70, 0xC4, 0xDC, 0x52, 0xA7, 0xA9, 0x7E, 0x78, + 0xE2, 0x62, 0x5D, 0x99, 0xBF, 0x04, 0x41, 0x72, 0x1A, 0x2D, 0x13, 0x55, 0x11, 0x67, 0x46, 0xE5, + 0x30, 0x2F, 0xEE, 0xB2, 0x75, 0x0D, 0xD3, 0xC8, 0xB4, 0xC4, 0x84, 0xA5, 0xE5, 0x46, 0xA5, 0x12, + 0x14, 0xFE, 0xA2, 0xB6, 0xE7, 0x8B, 0x91, 0x24, 0xB7, 0x5A, 0x73, 0xAB, 0x6F, 0x41, 0x2A, 0x3E, + 0x58, 0x04, 0x23, 0x66, 0x39, 0xDB, 0x16, 0x77, 0xA3, 0x43, 0xEE, 0x61, 0x5C, 0x7F, 0xBA, 0x35, + 0x78, 0xD2, 0x3C, 0x79, 0x61, 0x9E, 0xFC, 0xB1, 0x7B, 0x2E, 0x1C, 0x45, 0xF9, 0xDA, 0xE2, 0x98, + 0xF6, 0x10, 0x58, 0xBB, 0x6D, 0x2F, 0x7D, 0x18, 0x20, 0xD2, 0x83, 0xCB, 0x00, 0xF4, 0x63, 0x58, + 0xFF, 0x4A, 0xEE, 0x88, 0x7A, 0x09, 0xAA, 0xA2, 0xAD, 0x73, 0x54, 0xD8, 0xEE, 0xFD, 0x81, 0xA3, + 0xF2, 0xCE, 0x65, 0x18, 0x48, 0x97, 0xC3, 0x92, 0x37, 0x8B, 0x75, 0xC1, 0x61, 0x19, 0x31, 0x64, + 0x6C, 0x00, 0xE3, 0xCD, 0x5D, 0x49, 0x13, 0xD5, 0x1C, 0xB4, 0xF0, 0x1B, 0x08, 0x8A, 0x4F, 0x39, + 0xCE, 0x9A, 0x38, 0xAD, 0x62, 0x72, 0xC5, 0x23, 0xC8, 0x4A, 0x67, 0x89, 0xC0, 0x6E, 0x10, 0x0D, + 0x0D, 0x7C, 0x64, 0x9A, 0xA1, 0xB6, 0x1D, 0x3E, 0x37, 0xD7, 0xBC, 0xD9, 0x54, 0xFA, 0x4B, 0x62, + 0x79, 0xD5, 0xB0, 0x8B, 0x1C, 0x56, 0xCC, 0x75, 0x7D, 0x1F, 0xF4, 0xA3, 0x4E, 0x29, 0xAF, 0x48, + 0xA4, 0x53, 0xD1, 0x83, 0xC4, 0x86, 0xA2, 0x41, 0xBE, 0x91, 0x40, 0x44, 0x72, 0x4A, 0x33, 0x5D, + 0xC7, 0xCA, 0xD2, 0x0B, 0x28, 0x49, 0x7A, 0xB2, 0x73, 0x95, 0x49, 0x6B, 0x25, 0x06, 0xFE, 0xC8, + 0xD7, 0xF0, 0xC7, 0xA1, 0xD0, 0xA3, 0x83, 0x9B, 0x49, 0x2B, 0x83, 0xA4, 0x23, 0x64, 0x83, 0xA9, + 0x37, 0xE4, 0xBB, 0xA8, 0x2D, 0x2F, 0xCB, 0xB4, 0x16, 0x50, 0x70, 0x71, 0x83, 0xBB, 0x11, 0x30, + 0x52, 0x5A, 0xC4, 0x9E, 0x94, 0xA8, 0xC7, 0x8F, 0x10, 0x1F, 0x53, 0x4A, 0x20, 0x06, 0x20, 0xA6, + 0x40, 0xD0, 0xA7, 0x42, 0x8A, 0x54, 0xE6, 0x92, 0x53, 0x2A, 0x20, 0xCA, 0x48, 0xCD, 0xE2, 0xC1, + 0x85, 0x78, 0xD4, 0x46, 0xD6, 0x80, 0xFD, 0xDC, 0xBD, 0x73, 0x33, 0xDE, 0x90, 0x68, 0x09, 0x56, + 0x36, 0x3D, 0x9A, 0xA6, 0x52, 0x5C, 0x54, 0xC7, 0x19, 0xF8, 0xA8, 0xA1, 0x03, 0x5A, 0x23, 0x84, + 0x11, 0x1E, 0x84, 0x8A, 0x01, 0x40, 0x7F, 0x42, 0xC3, 0x1C, 0x22, 0x70, 0x08, 0x20, 0x82, 0xA0, + 0x7F, 0x49, 0x0D, 0xF7, 0x64, 0x05, 0xC9, 0xF8, 0xD8, 0x6D, 0x35, 0xF0, 0x9D, 0x66, 0x95, 0xEC, + 0x20, 0xA5, 0xBD, 0x68, 0x24, 0xFA, 0x64, 0x98, 0x1A, 0x50, 0x00, 0xAC, 0xD9, 0x01, 0xA0, 0x1E, + 0x24, 0x5E, 0x63, 0x2B, 0x3F, 0xEF, 0x04, 0x2A, 0xBB, 0x00, 0xAB, 0xBB, 0x8E, 0x87, 0x5F, 0x39, + 0x4F, 0x19, 0xA7, 0x39, 0x26, 0x00, 0x7B, 0x93, 0x68, 0x7A, 0x99, 0x30, 0x2E, 0xCE, 0x64, 0x1B, + 0x6A, 0x6C, 0xB4, 0xE4, 0xF5, 0xA9, 0x87, 0x15, 0x79, 0x3F, 0xC5, 0x8B, 0xCB, 0x0C, 0xF3, 0xBA, + 0x53, 0x79, 0x77, 0xB1, 0x86, 0x70, 0x21, 0x50, 0x66, 0x38, 0xB3, 0x29, 0x74, 0xB0, 0xFA, 0xA1, + 0x48, 0x82, 0x7A, 0x4F, 0xB7, 0x42, 0xE2, 0xC1, 0x44, 0xED, 0x81, 0xF9, 0xDC, 0xC2, 0xD8, 0xE1, + 0x94, 0x83, 0x5A, 0x0A, 0xB5, 0x02, 0x45, 0xC6, 0x95, 0xCD, 0x98, 0x35, 0x1D, 0x6A, 0x58, 0x88, + 0x61, 0xE0, 0xAF, 0xFE, 0x05, 0x0F, 0x1E, 0x1C, 0xC8, 0x55, 0x3F, 0xE1, 0x23, 0xE3, 0x7E, 0xF4, + 0x23, 0x3E, 0x3E, 0xAF, 0xF0, 0xF1, 0x79, 0x1D, 0x1F, 0xB4, 0xAA, 0x3C, 0x98, 0x0C, 0x80, 0xEC, + 0x19, 0xE1, 0x64, 0x4C, 0x13, 0x58, 0xC0, 0x43, 0x50, 0x25, 0x7F, 0x8B, 0xB3, 0x84, 0xFE, 0x98, + 0xB3, 0xDE, 0x84, 0x8D, 0xC4, 0x23, 0xFE, 0x8A, 0xD5, 0xFF, 0x82, 0x4B, 0x3C, 0x70, 0x3D, 0x97, + 0x79, 0x6D, 0x5A, 0x49, 0x28, 0x3F, 0x7E, 0x2B, 0x91, 0x7E, 0xE4, 0x42, 0x78, 0xA9, 0x38, 0xC8, + 0xDF, 0xB7, 0xF4, 0x00, 0xBC, 0x11, 0xF8, 0x29, 0x35, 0x75, 0xBC, 0x0B, 0xA5, 0xFC, 0x29, 0x30, + 0x64, 0xA8, 0xC0, 0x47, 0xDD, 0xD9, 0xDC, 0x12, 0xAE, 0x01, 0x8A, 0xF1, 0xA3, 0x29, 0xB0, 0xEA, + 0xC9, 0x02, 0xD7, 0x9E, 0x40, 0x26, 0x04, 0x91, 0xE0, 0x48, 0xC8, 0xA7, 0x8D, 0x2F, 0x07, 0x9B, + 0x37, 0x35, 0xC8, 0x43, 0x2E, 0xFC, 0x98, 0x2E, 0x0C, 0x36, 0x6F, 0xFE, 0x6D, 0x36, 0xC6, 0xCC, + 0x5A, 0x76, 0xA4, 0x96, 0x4C, 0xF6, 0xF4, 0x0B, 0xBF, 0x71, 0x09, 0x48, 0x5D, 0x49, 0x78, 0x45, + 0x34, 0x03, 0x6B, 0x43, 0x61, 0xE1, 0x07, 0xFF, 0x47, 0x09, 0xF8, 0x91, 0x9E, 0x07, 0xCE, 0xBD, + 0xE6, 0x3D, 0x5E, 0x2F, 0x3E, 0x85, 0xE9, 0x56, 0xE9, 0xC1, 0x4A, 0xC7, 0xEF, 0x53, 0x3A, 0x76, + 0x59, 0xA2, 0x14, 0x4A, 0x14, 0x59, 0x88, 0x1A, 0x6A, 0x50, 0x0E, 0x51, 0x98, 0x89, 0x17, 0xCD, + 0x81, 0x02, 0x9B, 0x73, 0x34, 0x5B, 0x3A, 0x02, 0x0F, 0xF4, 0xF5, 0x45, 0xEE, 0xFC, 0x74, 0x76, + 0x7A, 0x22, 0x44, 0x7C, 0xA5, 0x62, 0x22, 0xD0, 0xAA, 0x2E, 0x2C, 0x2F, 0xCF, 0x9C, 0x89, 0xE4, + 0xA1, 0x28, 0x75, 0x30, 0x31, 0x28, 0x87, 0xFE, 0x74, 0x31, 0xFC, 0x0A, 0x71, 0xD6, 0xD0, 0xCF, + 0x52, 0x48, 0x58, 0x5B, 0x36, 0xA2, 0xF7, 0xFB, 0x97, 0xF6, 0xAE, 0xDD, 0x84, 0xBA, 0x00, 0xB4, + 0x0A, 0x69, 0x19, 0xEE, 0x7D, 0xFE, 0xB7, 0x90, 0xB7, 0xFF, 0x1E, 0x32, 0x83, 0xA8, 0x95, 0x42, + 0x58, 0x2A, 0xF0, 0xAB, 0xB8, 0x93, 0x24, 0x9A, 0x4A, 0xB4, 0xE3, 0x24, 0xC1, 0x4B, 0xE9, 0x43, + 0x85, 0xA2, 0x0D, 0x61, 0x31, 0xA5, 0x89, 0xE6, 0x47, 0x34, 0xD5, 0x78, 0x24, 0xB4, 0x34, 0x8B, + 0x63, 0x68, 0x5C, 0x56, 0xF4, 0x61, 0xEB, 0xC5, 0xEB, 0xCB, 0xFB, 0x8C, 0x66, 0xD4, 0xCF, 0x97, + 0x69, 0x52, 0xD1, 0x0B, 0x56, 0x50, 0xDF, 0x10, 0xEE, 0x7E, 0xB9, 0xC9, 0xEB, 0xA9, 0x8C, 0x73, + 0x8C, 0xA2, 0x1B, 0x2D, 0x35, 0x07, 0xE9, 0x26, 0x40, 0xD5, 0xE5, 0x59, 0x10, 0xCC, 0xDB, 0x2B, + 0xB4, 0xA0, 0xF1, 0x8A, 0x44, 0x24, 0x9F, 0xCB, 0x67, 0x7F, 0xE4, 0xC9, 0xA9, 0xE2, 0x82, 0x50, + 0xF2, 0x54, 0xA9, 0x36, 0xAD, 0x0D, 0x63, 0x83, 0x6A, 0x8C, 0xA7, 0x82, 0x70, 0x0F, 0xAF, 0x51, + 0xE9, 0xC2, 0x2C, 0x6A, 0x29, 0xDC, 0xDE, 0x46, 0x5F, 0xCB, 0x6D, 0xE9, 0x89, 0x7C, 0x2A, 0x25, + 0xE3, 0xAE, 0xAE, 0x63, 0x55, 0x45, 0xB1, 0x3E, 0x25, 0x61, 0x5A, 0x26, 0x5B, 0x54, 0x06, 0x26, + 0x77, 0x0B, 0x70, 0x9B, 0x06, 0x29, 0x1C, 0xBD, 0x7E, 0x7F, 0xCE, 0x46, 0xD1, 0xCE, 0x11, 0x80, + 0x69, 0xC5, 0x3E, 0x93, 0xD7, 0xE0, 0x24, 0xCC, 0x73, 0x07, 0x32, 0xE9, 0x4A, 0x03, 0x0E, 0xA9, + 0x98, 0x44, 0xFE, 0x81, 0x7E, 0xA0, 0x3B, 0x3A, 0xFC, 0xBB, 0x09, 0x35, 0x47, 0xCD, 0xA5, 0xD0, + 0xA4, 0xFA, 0x74, 0x70, 0xF5, 0x06, 0xC2, 0x53, 0x0C, 0xA5, 0x01, 0x17, 0x50, 0x34, 0xD7, 0x74, + 0x7C, 0x7A, 0x7D, 0x0C, 0x29, 0xC8, 0x7F, 0x21, 0x37, 0x66, 0xBB, 0xAA, 0x6C, 0xB8, 0xF3, 0xEA, + 0x75, 0x56, 0x2E, 0x03, 0x7A, 0x61, 0x8C, 0x58, 0x0F, 0x29, 0x7E, 0xFB, 0x7B, 0xF4, 0x9E, 0x8D, + 0x15, 0xD2, 0x6A, 0x5D, 0x6F, 0xCE, 0x76, 0x90, 0x67, 0x89, 0xD5, 0x43, 0x2C, 0x70, 0x97, 0x1F, + 0x29, 0x59, 0x95, 0x35, 0xDC, 0xF6, 0x48, 0x10, 0xE0, 0xC7, 0x5A, 0x03, 0x1B, 0x6A, 0x22, 0xB2, + 0xD4, 0x42, 0x22, 0x29, 0x08, 0x90, 0xD2, 0x3E, 0x84, 0x39, 0xD3, 0x92, 0x65, 0x86, 0xB2, 0xA1, + 0xBC, 0xFF, 0xC5, 0x9A, 0xA3, 0x64, 0x46, 0xE8, 0xCE, 0xF9, 0x6C, 0x73, 0x53, 0xD8, 0x85, 0x99, + 0x18, 0x05, 0x52, 0x8A, 0x01, 0x1C, 0x9A, 0x7D, 0x68, 0x2D, 0x8C, 0xB2, 0x90, 0x58, 0xAB, 0x3D, + 0xD2, 0xB6, 0x51, 0x55, 0x03, 0x54, 0x7C, 0x46, 0x01, 0x03, 0xCE, 0xB2, 0x24, 0x80, 0xA8, 0x8B, + 0x39, 0xBA, 0xB2, 0x2D, 0xC5, 0xBA, 0xD0, 0x84, 0x0E, 0xEC, 0x67, 0xC8, 0x12, 0x95, 0x97, 0xAD, + 0xA2, 0x27, 0x12, 0xC5, 0x77, 0x95, 0x9E, 0xC8, 0x6F, 0xE5, 0x84, 0xAA, 0xC8, 0x77, 0x88, 0x2F, + 0x13, 0x5C, 0xD4, 0xD1, 0x13, 0xA0, 0x24, 0x83, 0x52, 0x34, 0x60, 0x2A, 0x2C, 0x37, 0xEE, 0xEB, + 0xD3, 0xE9, 0xB4, 0x8E, 0xDF, 0x6A, 0xEB, 0x70, 0x82, 0xB2, 0x02, 0x5F, 0x5F, 0xC7, 0x21, 0x47, + 0x15, 0x58, 0xF8, 0x6E, 0xE1, 0xAC, 0xBA, 0xE8, 0x42, 0x7F, 0x2B, 0xDE, 0xD4, 0xAA, 0xD2, 0x59, + 0xE1, 0x73, 0x79, 0xDB, 0x7B, 0x3B, 0x2B, 0x20, 0x32, 0xC4, 0xAF, 0xB2, 0x90, 0x69, 0x20, 0x0D, + 0x3B, 0xE5, 0x46, 0x56, 0x25, 0x85, 0x65, 0x5C, 0xB0, 0xE3, 0x2C, 0x9D, 0x18, 0x33, 0x60, 0xDD, + 0x11, 0x96, 0xD2, 0x95, 0x43, 0x2D, 0x65, 0xB7, 0x0E, 0xB7, 0x0A, 0xFB, 0x70, 0x30, 0x83, 0x94, + 0x79, 0xFB, 0xF3, 0x4F, 0x39, 0x5B, 0xDE, 0xF6, 0x92, 0x62, 0x71, 0xE1, 0xF3, 0xFC, 0xA9, 0x35, + 0xAF, 0x69, 0xA5, 0xD1, 0xAF, 0xC4, 0x97, 0xBD, 0x46, 0xFE, 0x19, 0x3B, 0xFF, 0x9C, 0xAD, 0x81, + 0xB1, 0x43, 0x23, 0x2A, 0xDC, 0x4C, 0x8C, 0xEA, 0x2F, 0x34, 0xE6, 0x63, 0x79, 0x29, 0xBF, 0x2D, + 0xA0, 0x54, 0xA9, 0xD3, 0x68, 0x78, 0x3E, 0xFF, 0x9A, 0x42, 0x19, 0x1D, 0x65, 0xFE, 0x28, 0x20, + 0x09, 0xC5, 0x82, 0xA3, 0x41, 0xBE, 0x92, 0xFB, 0x46, 0xC0, 0x86, 0x69, 0x03, 0x93, 0x6D, 0xCB, + 0xDE, 0xB2, 0x77, 0x71, 0x64, 0x7F, 0x4D, 0xF7, 0x57, 0x4F, 0xD8, 0x5F, 0x34, 0x69, 0x58, 0x0B, + 0xE7, 0xB5, 0xAB, 0x8A, 0x4D, 0x6A, 0x83, 0xFB, 0xC4, 0xA7, 0x70, 0x3D, 0x6F, 0xB3, 0xCC, 0xB6, + 0x1A, 0xE4, 0x5F, 0x60, 0xD4, 0x31, 0xBA, 0x95, 0x2F, 0x92, 0xF4, 0x81, 0x7B, 0x18, 0x5B, 0x17, + 0x54, 0x26, 0x70, 0x49, 0xD5, 0x87, 0x34, 0xB9, 0xD3, 0x9C, 0x2F, 0x39, 0xC3, 0xB7, 0x3C, 0xA8, + 0x03, 0xE4, 0x37, 0x9C, 0x72, 0x39, 0xB0, 0xBF, 0x07, 0x5D, 0x33, 0x2A, 0x41, 0x79, 0xB1, 0x26, + 0x9B, 0xE6, 0x7C, 0x02, 0x82, 0x01, 0x70, 0xB1, 0xA3, 0x48, 0xCD, 0x2B, 0xCB, 0x98, 0x9B, 0x57, + 0x96, 0x54, 0xE2, 0x5F, 0x59, 0xCC, 0xDB, 0x9F, 0xFC, 0xDB, 0x4C, 0xF9, 0x7F, 0x5B, 0x28, 0x36, + 0x32, 0xF9, 0xE1, 0x09, 0xF7, 0x56, 0x3F, 0x45, 0xAD, 0x47, 0x51, 0xBB, 0xF7, 0xFF, 0x17, 0x53, + 0xE8, 0x9D, 0x36, 0x92, 0x29, 0x00, 0x00 +}; + +#define SPIFFS_MAXLENGTH_FILEPATH 32 +const char *excludeListFile = "/.exclude.files"; + +typedef struct ExcludeListS { + char *item; + ExcludeListS *next; +} ExcludeList; + +static ExcludeList *excludes = NULL; + +static bool matchWild(const char *pattern, const char *testee) { + const char *nxPat = NULL, *nxTst = NULL; + + while (*testee) { + if (( *pattern == '?' ) || (*pattern == *testee)){ + pattern++;testee++; + continue; + } + if (*pattern=='*'){ + nxPat=pattern++; nxTst=testee; + continue; + } + if (nxPat){ + pattern = nxPat+1; testee=++nxTst; + continue; + } + return false; + } + while (*pattern=='*'){pattern++;} + return (*pattern == 0); +} + +static bool addExclude(const char *item){ + size_t len = strlen(item); + if(!len){ + return false; + } + ExcludeList *e = (ExcludeList *)malloc(sizeof(ExcludeList)); + if(!e){ + return false; + } + e->item = (char *)malloc(len+1); + if(!e->item){ + free(e); + return false; + } + memcpy(e->item, item, len+1); + e->next = excludes; + excludes = e; + return true; +} + +static void loadExcludeList(fs::FS &_fs, const char *filename){ + static char linebuf[SPIFFS_MAXLENGTH_FILEPATH]; + fs::File excludeFile=_fs.open(filename, "r"); + if(!excludeFile){ + //addExclude("/*.js.gz"); + return; + } +#ifdef ESP32 + if(excludeFile.isDirectory()){ + excludeFile.close(); + return; + } +#endif + if (excludeFile.size() > 0){ + uint8_t idx; + bool isOverflowed = false; + while (excludeFile.available()){ + linebuf[0] = '\0'; + idx = 0; + int lastChar; + do { + lastChar = excludeFile.read(); + if(lastChar != '\r'){ + linebuf[idx++] = (char) lastChar; + } + } while ((lastChar >= 0) && (lastChar != '\n') && (idx < SPIFFS_MAXLENGTH_FILEPATH)); + + if(isOverflowed){ + isOverflowed = (lastChar != '\n'); + continue; + } + isOverflowed = (idx >= SPIFFS_MAXLENGTH_FILEPATH); + linebuf[idx-1] = '\0'; + if(!addExclude(linebuf)){ + excludeFile.close(); + return; + } + } + } + excludeFile.close(); +} + +static bool isExcluded(fs::FS &_fs, const char *filename) { + if(excludes == NULL){ + loadExcludeList(_fs, excludeListFile); + } + ExcludeList *e = excludes; + while(e){ + if (matchWild(e->item, filename)){ + return true; + } + e = e->next; + } + return false; +} + +// WEB HANDLER IMPLEMENTATION + +#ifdef ESP32 +SPIFFSEditor::SPIFFSEditor(const fs::FS& fs, const String& username, const String& password) +#else +SPIFFSEditor::SPIFFSEditor(const String& username, const String& password, const fs::FS& fs) +#endif +:_fs(fs) +,_username(username) +,_password(password) +,_authenticated(false) +,_startTime(0) +{} + +bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request){ + if(request->url().equalsIgnoreCase("/edit")){ + if(request->method() == HTTP_GET){ + if(request->hasParam("list")) + return true; + if(request->hasParam("edit")){ + request->_tempFile = _fs.open(request->arg("edit"), "r"); + if(!request->_tempFile){ + return false; + } +#ifdef ESP32 + if(request->_tempFile.isDirectory()){ + request->_tempFile.close(); + return false; + } +#endif + } + if(request->hasParam("download")){ + request->_tempFile = _fs.open(request->arg("download"), "r"); + if(!request->_tempFile){ + return false; + } +#ifdef ESP32 + if(request->_tempFile.isDirectory()){ + request->_tempFile.close(); + return false; + } +#endif + } + request->addInterestingHeader("If-Modified-Since"); + return true; + } + else if(request->method() == HTTP_POST) + return true; + else if(request->method() == HTTP_DELETE) + return true; + else if(request->method() == HTTP_PUT) + return true; + + } + return false; +} + + +void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){ + if(_username.length() && _password.length() && !request->authenticate(_username.c_str(), _password.c_str())) + return request->requestAuthentication(); + + if(request->method() == HTTP_GET){ + if(request->hasParam("list")){ + String path = request->getParam("list")->value(); +#ifdef ESP32 + File dir = _fs.open(path); +#else + Dir dir = _fs.openDir(path); +#endif + path = String(); + String output = "["; +#ifdef ESP32 + File entry = dir.openNextFile(); + while(entry){ +#else + while(dir.next()){ + fs::File entry = dir.openFile("r"); +#endif + if (isExcluded(_fs, entry.name())) { +#ifdef ESP32 + entry = dir.openNextFile(); +#endif + continue; + } + if (output != "[") output += ','; + output += "{\"type\":\""; + output += "file"; + output += "\",\"name\":\""; + output += String(entry.name()); + output += "\",\"size\":"; + output += String(entry.size()); + output += "}"; +#ifdef ESP32 + entry = dir.openNextFile(); +#else + entry.close(); +#endif + } +#ifdef ESP32 + dir.close(); +#endif + output += "]"; + request->send(200, "text/json", output); + output = String(); + } + else if(request->hasParam("edit") || request->hasParam("download")){ + request->send(request->_tempFile, request->_tempFile.name(), String(), request->hasParam("download")); + } + else { + const char * buildTime = __DATE__ " " __TIME__ " GMT"; + if (request->header("If-Modified-Since").equals(buildTime)) { + request->send(304); + } else { + AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", edit_htm_gz, edit_htm_gz_len); + response->addHeader("Content-Encoding", "gzip"); + response->addHeader("Last-Modified", buildTime); + request->send(response); + } + } + } else if(request->method() == HTTP_DELETE){ + if(request->hasParam("path", true)){ + _fs.remove(request->getParam("path", true)->value()); + request->send(200, "", "DELETE: "+request->getParam("path", true)->value()); + } else + request->send(404); + } else if(request->method() == HTTP_POST){ + if(request->hasParam("data", true, true) && _fs.exists(request->getParam("data", true, true)->value())) + request->send(200, "", "UPLOADED: "+request->getParam("data", true, true)->value()); + else + request->send(500); + } else if(request->method() == HTTP_PUT){ + if(request->hasParam("path", true)){ + String filename = request->getParam("path", true)->value(); + if(_fs.exists(filename)){ + request->send(200); + } else { + fs::File f = _fs.open(filename, "w"); + if(f){ + f.write((uint8_t)0x00); + f.close(); + request->send(200, "", "CREATE: "+filename); + } else { + request->send(500); + } + } + } else + request->send(400); + } +} + +void SPIFFSEditor::handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){ + if(!index){ + if(!_username.length() || request->authenticate(_username.c_str(),_password.c_str())){ + _authenticated = true; + request->_tempFile = _fs.open(filename, "w"); + _startTime = millis(); + } + } + if(_authenticated && request->_tempFile){ + if(len){ + request->_tempFile.write(data,len); + } + if(final){ + request->_tempFile.close(); + } + } +} diff --git a/libraries/ESPAsyncWebServer/src/SPIFFSEditor.h b/libraries/ESPAsyncWebServer/src/SPIFFSEditor.h new file mode 100644 index 00000000..115a3df4 --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/SPIFFSEditor.h @@ -0,0 +1,23 @@ +#ifndef SPIFFSEditor_H_ +#define SPIFFSEditor_H_ +#include + +class SPIFFSEditor: public AsyncWebHandler { + private: + fs::FS _fs; + String _username; + String _password; + bool _authenticated; + uint32_t _startTime; + public: +#ifdef ESP32 + SPIFFSEditor(const fs::FS& fs, const String& username=String(), const String& password=String()); +#else + SPIFFSEditor(const String& username=String(), const String& password=String(), const fs::FS& fs=SPIFFS); +#endif + virtual bool canHandle(AsyncWebServerRequest *request) override final; + virtual void handleRequest(AsyncWebServerRequest *request) override final; + virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final; +}; + +#endif diff --git a/libraries/ESPAsyncWebServer/src/StringArray.h b/libraries/ESPAsyncWebServer/src/StringArray.h new file mode 100644 index 00000000..4c0aa701 --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/StringArray.h @@ -0,0 +1,193 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +#ifndef STRINGARRAY_H_ +#define STRINGARRAY_H_ + +#include "stddef.h" +#include "WString.h" + +template +class LinkedListNode { + T _value; + public: + LinkedListNode* next; + LinkedListNode(const T val): _value(val), next(nullptr) {} + ~LinkedListNode(){} + const T& value() const { return _value; }; + T& value(){ return _value; } +}; + +template class Item = LinkedListNode> +class LinkedList { + public: + typedef Item ItemType; + typedef std::function OnRemove; + typedef std::function Predicate; + private: + ItemType* _root; + OnRemove _onRemove; + + class Iterator { + ItemType* _node; + public: + Iterator(ItemType* current = nullptr) : _node(current) {} + Iterator(const Iterator& i) : _node(i._node) {} + Iterator& operator ++() { _node = _node->next; return *this; } + bool operator != (const Iterator& i) const { return _node != i._node; } + const T& operator * () const { return _node->value(); } + const T* operator -> () const { return &_node->value(); } + }; + + public: + typedef const Iterator ConstIterator; + ConstIterator begin() const { return ConstIterator(_root); } + ConstIterator end() const { return ConstIterator(nullptr); } + + LinkedList(OnRemove onRemove) : _root(nullptr), _onRemove(onRemove) {} + ~LinkedList(){} + void add(const T& t){ + auto it = new ItemType(t); + if(!_root){ + _root = it; + } else { + auto i = _root; + while(i->next) i = i->next; + i->next = it; + } + } + T& front() const { + return _root->value(); + } + + bool isEmpty() const { + return _root == nullptr; + } + size_t length() const { + size_t i = 0; + auto it = _root; + while(it){ + i++; + it = it->next; + } + return i; + } + size_t count_if(Predicate predicate) const { + size_t i = 0; + auto it = _root; + while(it){ + if (!predicate){ + i++; + } + else if (predicate(it->value())) { + i++; + } + it = it->next; + } + return i; + } + const T* nth(size_t N) const { + size_t i = 0; + auto it = _root; + while(it){ + if(i++ == N) + return &(it->value()); + it = it->next; + } + return nullptr; + } + bool remove(const T& t){ + auto it = _root; + auto pit = _root; + while(it){ + if(it->value() == t){ + if(it == _root){ + _root = _root->next; + } else { + pit->next = it->next; + } + + if (_onRemove) { + _onRemove(it->value()); + } + + delete it; + return true; + } + pit = it; + it = it->next; + } + return false; + } + bool remove_first(Predicate predicate){ + auto it = _root; + auto pit = _root; + while(it){ + if(predicate(it->value())){ + if(it == _root){ + _root = _root->next; + } else { + pit->next = it->next; + } + if (_onRemove) { + _onRemove(it->value()); + } + delete it; + return true; + } + pit = it; + it = it->next; + } + return false; + } + + void free(){ + while(_root != nullptr){ + auto it = _root; + _root = _root->next; + if (_onRemove) { + _onRemove(it->value()); + } + delete it; + } + _root = nullptr; + } +}; + + +class StringArray : public LinkedList { +public: + + StringArray() : LinkedList(nullptr) {} + + bool containsIgnoreCase(const String& str){ + for (const auto& s : *this) { + if (str.equalsIgnoreCase(s)) { + return true; + } + } + return false; + } +}; + + + + +#endif /* STRINGARRAY_H_ */ diff --git a/libraries/ESPAsyncWebServer/src/WebAuthentication.cpp b/libraries/ESPAsyncWebServer/src/WebAuthentication.cpp new file mode 100644 index 00000000..2feca542 --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/WebAuthentication.cpp @@ -0,0 +1,235 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +#include "WebAuthentication.h" +#include +#ifdef ESP32 +#include "mbedtls/md5.h" +#else +#include "md5.h" +#endif + + +// Basic Auth hash = base64("username:password") + +bool checkBasicAuthentication(const char * hash, const char * username, const char * password){ + if(username == NULL || password == NULL || hash == NULL) + return false; + + size_t toencodeLen = strlen(username)+strlen(password)+1; + size_t encodedLen = base64_encode_expected_len(toencodeLen); + if(strlen(hash) != encodedLen) + return false; + + char *toencode = new char[toencodeLen+1]; + if(toencode == NULL){ + return false; + } + char *encoded = new char[base64_encode_expected_len(toencodeLen)+1]; + if(encoded == NULL){ + delete[] toencode; + return false; + } + sprintf(toencode, "%s:%s", username, password); + if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){ + delete[] toencode; + delete[] encoded; + return true; + } + delete[] toencode; + delete[] encoded; + return false; +} + +static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or more +#ifdef ESP32 + mbedtls_md5_context _ctx; +#else + md5_context_t _ctx; +#endif + uint8_t i; + uint8_t * _buf = (uint8_t*)malloc(16); + if(_buf == NULL) + return false; + memset(_buf, 0x00, 16); +#ifdef ESP32 + mbedtls_md5_init(&_ctx); + mbedtls_md5_starts(&_ctx); + mbedtls_md5_update(&_ctx, data, len); + mbedtls_md5_finish(&_ctx, _buf); +#else + MD5Init(&_ctx); + MD5Update(&_ctx, data, len); + MD5Final(_buf, &_ctx); +#endif + for(i = 0; i < 16; i++) { + sprintf(output + (i * 2), "%02x", _buf[i]); + } + free(_buf); + return true; +} + +static String genRandomMD5(){ +#ifdef ESP8266 + uint32_t r = RANDOM_REG32; +#else + uint32_t r = rand(); +#endif + char * out = (char*)malloc(33); + if(out == NULL || !getMD5((uint8_t*)(&r), 4, out)) + return ""; + String res = String(out); + free(out); + return res; +} + +static String stringMD5(const String& in){ + char * out = (char*)malloc(33); + if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) + return ""; + String res = String(out); + free(out); + return res; +} + +String generateDigestHash(const char * username, const char * password, const char * realm){ + if(username == NULL || password == NULL || realm == NULL){ + return ""; + } + char * out = (char*)malloc(33); + String res = String(username); + res.concat(":"); + res.concat(realm); + res.concat(":"); + String in = res; + in.concat(password); + if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) + return ""; + res.concat(out); + free(out); + return res; +} + +String requestDigestAuthentication(const char * realm){ + String header = "realm=\""; + if(realm == NULL) + header.concat("asyncesp"); + else + header.concat(realm); + header.concat( "\", qop=\"auth\", nonce=\""); + header.concat(genRandomMD5()); + header.concat("\", opaque=\""); + header.concat(genRandomMD5()); + header.concat("\""); + return header; +} + +bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri){ + if(username == NULL || password == NULL || header == NULL || method == NULL){ + //os_printf("AUTH FAIL: missing requred fields\n"); + return false; + } + + String myHeader = String(header); + int nextBreak = myHeader.indexOf(","); + if(nextBreak < 0){ + //os_printf("AUTH FAIL: no variables\n"); + return false; + } + + String myUsername = String(); + String myRealm = String(); + String myNonce = String(); + String myUri = String(); + String myResponse = String(); + String myQop = String(); + String myNc = String(); + String myCnonce = String(); + + myHeader += ", "; + do { + String avLine = myHeader.substring(0, nextBreak); + avLine.trim(); + myHeader = myHeader.substring(nextBreak+1); + nextBreak = myHeader.indexOf(","); + + int eqSign = avLine.indexOf("="); + if(eqSign < 0){ + //os_printf("AUTH FAIL: no = sign\n"); + return false; + } + String varName = avLine.substring(0, eqSign); + avLine = avLine.substring(eqSign + 1); + if(avLine.startsWith("\"")){ + avLine = avLine.substring(1, avLine.length() - 1); + } + + if(varName.equals("username")){ + if(!avLine.equals(username)){ + //os_printf("AUTH FAIL: username\n"); + return false; + } + myUsername = avLine; + } else if(varName.equals("realm")){ + if(realm != NULL && !avLine.equals(realm)){ + //os_printf("AUTH FAIL: realm\n"); + return false; + } + myRealm = avLine; + } else if(varName.equals("nonce")){ + if(nonce != NULL && !avLine.equals(nonce)){ + //os_printf("AUTH FAIL: nonce\n"); + return false; + } + myNonce = avLine; + } else if(varName.equals("opaque")){ + if(opaque != NULL && !avLine.equals(opaque)){ + //os_printf("AUTH FAIL: opaque\n"); + return false; + } + } else if(varName.equals("uri")){ + if(uri != NULL && !avLine.equals(uri)){ + //os_printf("AUTH FAIL: uri\n"); + return false; + } + myUri = avLine; + } else if(varName.equals("response")){ + myResponse = avLine; + } else if(varName.equals("qop")){ + myQop = avLine; + } else if(varName.equals("nc")){ + myNc = avLine; + } else if(varName.equals("cnonce")){ + myCnonce = avLine; + } + } while(nextBreak > 0); + + String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ":" + myRealm + ":" + String(password)); + String ha2 = String(method) + ":" + myUri; + String response = ha1 + ":" + myNonce + ":" + myNc + ":" + myCnonce + ":" + myQop + ":" + stringMD5(ha2); + + if(myResponse.equals(stringMD5(response))){ + //os_printf("AUTH SUCCESS\n"); + return true; + } + + //os_printf("AUTH FAIL: password\n"); + return false; +} diff --git a/libraries/ESPAsyncWebServer/src/WebAuthentication.h b/libraries/ESPAsyncWebServer/src/WebAuthentication.h new file mode 100644 index 00000000..ff68265d --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/WebAuthentication.h @@ -0,0 +1,34 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ + +#ifndef WEB_AUTHENTICATION_H_ +#define WEB_AUTHENTICATION_H_ + +#include "Arduino.h" + +bool checkBasicAuthentication(const char * header, const char * username, const char * password); +String requestDigestAuthentication(const char * realm); +bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri); + +//for storing hashed versions on the device that can be authenticated against +String generateDigestHash(const char * username, const char * password, const char * realm); + +#endif diff --git a/libraries/ESPAsyncWebServer/src/WebHandlerImpl.h b/libraries/ESPAsyncWebServer/src/WebHandlerImpl.h new file mode 100644 index 00000000..b3c84e31 --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/WebHandlerImpl.h @@ -0,0 +1,109 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +#ifndef ASYNCWEBSERVERHANDLERIMPL_H_ +#define ASYNCWEBSERVERHANDLERIMPL_H_ + + +#include "stddef.h" +#include + +class AsyncStaticWebHandler: public AsyncWebHandler { + using File = fs::File; + using FS = fs::FS; + private: + bool _getFile(AsyncWebServerRequest *request); + bool _fileExists(AsyncWebServerRequest *request, const String& path); + uint8_t _countBits(const uint8_t value) const; + protected: + FS _fs; + String _uri; + String _path; + String _default_file; + String _cache_control; + String _last_modified; + AwsTemplateProcessor _callback; + bool _isDir; + bool _gzipFirst; + uint8_t _gzipStats; + public: + AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control); + virtual bool canHandle(AsyncWebServerRequest *request) override final; + virtual void handleRequest(AsyncWebServerRequest *request) override final; + AsyncStaticWebHandler& setIsDir(bool isDir); + AsyncStaticWebHandler& setDefaultFile(const char* filename); + AsyncStaticWebHandler& setCacheControl(const char* cache_control); + AsyncStaticWebHandler& setLastModified(const char* last_modified); + AsyncStaticWebHandler& setLastModified(struct tm* last_modified); + #ifdef ESP8266 + AsyncStaticWebHandler& setLastModified(time_t last_modified); + AsyncStaticWebHandler& setLastModified(); //sets to current time. Make sure sntp is runing and time is updated + #endif + AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;} +}; + +class AsyncCallbackWebHandler: public AsyncWebHandler { + private: + protected: + String _uri; + WebRequestMethodComposite _method; + ArRequestHandlerFunction _onRequest; + ArUploadHandlerFunction _onUpload; + ArBodyHandlerFunction _onBody; + public: + AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL){} + void setUri(const String& uri){ _uri = uri; } + void setMethod(WebRequestMethodComposite method){ _method = method; } + void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; } + void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; } + void onBody(ArBodyHandlerFunction fn){ _onBody = fn; } + + virtual bool canHandle(AsyncWebServerRequest *request) override final{ + + if(!_onRequest) + return false; + + if(!(_method & request->method())) + return false; + + if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/"))) + return false; + + request->addInterestingHeader("ANY"); + return true; + } + + virtual void handleRequest(AsyncWebServerRequest *request) override final { + if(_onRequest) + _onRequest(request); + else + request->send(500); + } + virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final { + if(_onUpload) + _onUpload(request, filename, index, data, len, final); + } + virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final { + if(_onBody) + _onBody(request, data, len, index, total); + } +}; + +#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */ diff --git a/libraries/ESPAsyncWebServer/src/WebHandlers.cpp b/libraries/ESPAsyncWebServer/src/WebHandlers.cpp new file mode 100644 index 00000000..1f435e68 --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/WebHandlers.cpp @@ -0,0 +1,220 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +#include "ESPAsyncWebServer.h" +#include "WebHandlerImpl.h" + +AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control) + : _fs(fs), _uri(uri), _path(path), _default_file("index.htm"), _cache_control(cache_control), _last_modified(""), _callback(nullptr) +{ + // Ensure leading '/' + if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri; + if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path; + + // If path ends with '/' we assume a hint that this is a directory to improve performance. + // However - if it does not end with '/' we, can't assume a file, path can still be a directory. + _isDir = _path[_path.length()-1] == '/'; + + // Remove the trailing '/' so we can handle default file + // Notice that root will be "" not "/" + if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1); + if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.length()-1); + + // Reset stats + _gzipFirst = false; + _gzipStats = 0xF8; +} + +AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir){ + _isDir = isDir; + return *this; +} + +AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename){ + _default_file = String(filename); + return *this; +} + +AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control){ + _cache_control = String(cache_control); + return *this; +} + +AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified){ + _last_modified = String(last_modified); + return *this; +} + +AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified){ + char result[30]; + strftime (result,30,"%a, %d %b %Y %H:%M:%S %Z", last_modified); + return setLastModified((const char *)result); +} + +#ifdef ESP8266 +AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified){ + return setLastModified((struct tm *)gmtime(&last_modified)); +} + +AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(){ + time_t last_modified; + if(time(&last_modified) == 0) //time is not yet set + return *this; + return setLastModified(last_modified); +} +#endif +bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){ + if(request->method() != HTTP_GET + || !request->url().startsWith(_uri) + || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP) + ){ + return false; + } + if (_getFile(request)) { + // We interested in "If-Modified-Since" header to check if file was modified + if (_last_modified.length()) + request->addInterestingHeader("If-Modified-Since"); + + if(_cache_control.length()) + request->addInterestingHeader("If-None-Match"); + + DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n"); + return true; + } + + return false; +} + +bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request) +{ + // Remove the found uri + String path = request->url().substring(_uri.length()); + + // We can skip the file check and look for default if request is to the root of a directory or that request path ends with '/' + bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length()-1] == '/'); + + path = _path + path; + + // Do we have a file or .gz file + if (!canSkipFileCheck && _fileExists(request, path)) + return true; + + // Can't handle if not default file + if (_default_file.length() == 0) + return false; + + // Try to add default file, ensure there is a trailing '/' ot the path. + if (path.length() == 0 || path[path.length()-1] != '/') + path += "/"; + path += _default_file; + + return _fileExists(request, path); +} + +#ifdef ESP32 +#define FILE_IS_REAL(f) (f == true && !f.isDirectory()) +#else +#define FILE_IS_REAL(f) (f == true) +#endif + +bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String& path) +{ + bool fileFound = false; + bool gzipFound = false; + + String gzip = path + ".gz"; + + if (_gzipFirst) { + request->_tempFile = _fs.open(gzip, "r"); + gzipFound = FILE_IS_REAL(request->_tempFile); + if (!gzipFound){ + request->_tempFile = _fs.open(path, "r"); + fileFound = FILE_IS_REAL(request->_tempFile); + } + } else { + request->_tempFile = _fs.open(path, "r"); + fileFound = FILE_IS_REAL(request->_tempFile); + if (!fileFound){ + request->_tempFile = _fs.open(gzip, "r"); + gzipFound = FILE_IS_REAL(request->_tempFile); + } + } + + bool found = fileFound || gzipFound; + + if (found) { + // Extract the file name from the path and keep it in _tempObject + size_t pathLen = path.length(); + char * _tempPath = (char*)malloc(pathLen+1); + snprintf(_tempPath, pathLen+1, "%s", path.c_str()); + request->_tempObject = (void*)_tempPath; + + // Calculate gzip statistic + _gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0); + if (_gzipStats == 0x00) _gzipFirst = false; // All files are not gzip + else if (_gzipStats == 0xFF) _gzipFirst = true; // All files are gzip + else _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first + } + + return found; +} + +uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const +{ + uint8_t w = value; + uint8_t n; + for (n=0; w!=0; n++) w&=w-1; + return n; +} + +void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request) +{ + // Get the filename from request->_tempObject and free it + String filename = String((char*)request->_tempObject); + free(request->_tempObject); + request->_tempObject = NULL; + if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) + return request->requestAuthentication(); + + if (request->_tempFile == true) { + String etag = String(request->_tempFile.size()); + if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) { + request->_tempFile.close(); + request->send(304); // Not modified + } else if (_cache_control.length() && request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag)) { + request->_tempFile.close(); + AsyncWebServerResponse * response = new AsyncBasicResponse(304); // Not modified + response->addHeader("Cache-Control", _cache_control); + response->addHeader("ETag", etag); + request->send(response); + } else { + AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback); + if (_last_modified.length()) + response->addHeader("Last-Modified", _last_modified); + if (_cache_control.length()){ + response->addHeader("Cache-Control", _cache_control); + response->addHeader("ETag", etag); + } + request->send(response); + } + } else { + request->send(404); + } +} diff --git a/libraries/ESPAsyncWebServer/src/WebRequest.cpp b/libraries/ESPAsyncWebServer/src/WebRequest.cpp new file mode 100644 index 00000000..91b5caf6 --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/WebRequest.cpp @@ -0,0 +1,990 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +#include "ESPAsyncWebServer.h" +#include "WebResponseImpl.h" +#include "WebAuthentication.h" + +#ifndef ESP8266 +#define os_strlen strlen +#endif + +static const String SharedEmptyString = String(); + +#define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '=')) + +enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL }; + +AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) + : _client(c) + , _server(s) + , _handler(NULL) + , _response(NULL) + , _temp() + , _parseState(0) + , _version(0) + , _method(HTTP_ANY) + , _url() + , _host() + , _contentType() + , _boundary() + , _authorization() + , _reqconntype(RCT_HTTP) + , _isDigest(false) + , _isMultipart(false) + , _isPlainPost(false) + , _expectingContinue(false) + , _contentLength(0) + , _parsedLength(0) + , _headers(LinkedList([](AsyncWebHeader *h){ delete h; })) + , _params(LinkedList([](AsyncWebParameter *p){ delete p; })) + , _multiParseState(0) + , _boundaryPosition(0) + , _itemStartIndex(0) + , _itemSize(0) + , _itemName() + , _itemFilename() + , _itemType() + , _itemValue() + , _itemBuffer(0) + , _itemBufferIndex(0) + , _itemIsFile(false) + , _tempObject(NULL) +{ + c->onError([](void *r, AsyncClient* c, int8_t error){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this); + c->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this); + c->onDisconnect([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); delete c; }, this); + c->onTimeout([](void *r, AsyncClient* c, uint32_t time){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this); + c->onData([](void *r, AsyncClient* c, void *buf, size_t len){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this); + c->onPoll([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onPoll(); }, this); +} + +AsyncWebServerRequest::~AsyncWebServerRequest(){ + _headers.free(); + + _params.free(); + + _interestingHeaders.free(); + + if(_response != NULL){ + delete _response; + } + + if(_tempObject != NULL){ + free(_tempObject); + } + + if(_tempFile){ + _tempFile.close(); + } +} + +void AsyncWebServerRequest::_onData(void *buf, size_t len){ + size_t i = 0; + while (true) { + + if(_parseState < PARSE_REQ_BODY){ + // Find new line in buf + char *str = (char*)buf; + for (i = 0; i < len; i++) { + if (str[i] == '\n') { + break; + } + } + if (i == len) { // No new line, just add the buffer in _temp + char ch = str[len-1]; + str[len-1] = 0; + _temp.reserve(_temp.length()+len); + _temp.concat(str); + _temp.concat(ch); + } else { // Found new line - extract it and parse + str[i] = 0; // Terminate the string at the end of the line. + _temp.concat(str); + _temp.trim(); + _parseLine(); + if (++i < len) { + // Still have more buffer to process + buf = str+i; + len-= i; + continue; + } + } + } else if(_parseState == PARSE_REQ_BODY){ + if(_isMultipart){ + size_t i; + for(i=0; i end) equal = end; + String name = params.substring(start, equal); + String value = equal + 1 < end ? params.substring(equal + 1, end) : String(); + _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value))); + start = end + 1; + } +} + +bool AsyncWebServerRequest::_parseReqHead(){ + // Split the head into method, url and version + int index = _temp.indexOf(' '); + String m = _temp.substring(0, index); + index = _temp.indexOf(' ', index+1); + String u = _temp.substring(m.length()+1, index); + _temp = _temp.substring(index+1); + + if(m == "GET"){ + _method = HTTP_GET; + } else if(m == "POST"){ + _method = HTTP_POST; + } else if(m == "DELETE"){ + _method = HTTP_DELETE; + } else if(m == "PUT"){ + _method = HTTP_PUT; + } else if(m == "PATCH"){ + _method = HTTP_PATCH; + } else if(m == "HEAD"){ + _method = HTTP_HEAD; + } else if(m == "OPTIONS"){ + _method = HTTP_OPTIONS; + } + + String g = String(); + index = u.indexOf('?'); + if(index > 0){ + g = u.substring(index +1); + u = u.substring(0, index); + } + _url = urlDecode(u); + _addGetParams(g); + + if(!_temp.startsWith("HTTP/1.0")) + _version = 1; + + _temp = String(); + return true; +} + +bool strContains(String src, String find, bool mindcase = true) { + int pos=0, i=0; + const int slen = src.length(); + const int flen = find.length(); + + if (slen < flen) return false; + while (pos <= (slen - flen)) { + for (i=0; i < flen; i++) { + if (mindcase) { + if (src[pos+i] != find[i]) i = flen + 1; // no match + } else if (tolower(src[pos+i]) != tolower(find[i])) i = flen + 1; // no match + } + if (i == flen) return true; + pos++; + } + return false; +} + +bool AsyncWebServerRequest::_parseReqHeader(){ + int index = _temp.indexOf(':'); + if(index){ + String name = _temp.substring(0, index); + String value = _temp.substring(index + 2); + if(name.equalsIgnoreCase("Host")){ + _host = value; + } else if(name.equalsIgnoreCase("Content-Type")){ + if (value.startsWith("multipart/")){ + _boundary = value.substring(value.indexOf('=')+1); + _boundary.replace("\"",""); + _contentType = value.substring(0, value.indexOf(';')); + _isMultipart = true; + } else { + _contentType = value; + } + } else if(name.equalsIgnoreCase("Content-Length")){ + _contentLength = atoi(value.c_str()); + } else if(name.equalsIgnoreCase("Expect") && value == "100-continue"){ + _expectingContinue = true; + } else if(name.equalsIgnoreCase("Authorization")){ + if(value.length() > 5 && value.substring(0,5).equalsIgnoreCase("Basic")){ + _authorization = value.substring(6); + } else if(value.length() > 6 && value.substring(0,6).equalsIgnoreCase("Digest")){ + _isDigest = true; + _authorization = value.substring(7); + } + } else { + if(name.equalsIgnoreCase("Upgrade") && value.equalsIgnoreCase("websocket")){ + // WebSocket request can be uniquely identified by header: [Upgrade: websocket] + _reqconntype = RCT_WS; + } else { + if(name.equalsIgnoreCase("Accept") && strContains(value, "text/event-stream", false)){ + // WebEvent request can be uniquely identified by header: [Accept: text/event-stream] + _reqconntype = RCT_EVENT; + } + } + } + _headers.add(new AsyncWebHeader(name, value)); + } + _temp = String(); + return true; +} + +void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data){ + if(data && (char)data != '&') + _temp += (char)data; + if(!data || (char)data == '&' || _parsedLength == _contentLength){ + String name = "body"; + String value = _temp; + if(!_temp.startsWith("{") && !_temp.startsWith("[") && _temp.indexOf('=') > 0){ + name = _temp.substring(0, _temp.indexOf('=')); + value = _temp.substring(_temp.indexOf('=') + 1); + } + _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value), true)); + _temp = String(); + } +} + +void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last){ + _itemBuffer[_itemBufferIndex++] = data; + + if(last || _itemBufferIndex == 1460){ + //check if authenticated before calling the upload + if(_handler) + _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false); + _itemBufferIndex = 0; + } +} + +enum { + EXPECT_BOUNDARY, + PARSE_HEADERS, + WAIT_FOR_RETURN1, + EXPECT_FEED1, + EXPECT_DASH1, + EXPECT_DASH2, + BOUNDARY_OR_DATA, + DASH3_OR_RETURN2, + EXPECT_FEED2, + PARSING_FINISHED, + PARSE_ERROR +}; + +void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){ +#define itemWriteByte(b) do { _itemSize++; if(_itemIsFile) _handleUploadByte(b, last); else _itemValue+=(char)(b); } while(0) + + if(!_parsedLength){ + _multiParseState = EXPECT_BOUNDARY; + _temp = String(); + _itemName = String(); + _itemFilename = String(); + _itemType = String(); + } + + if(_multiParseState == WAIT_FOR_RETURN1){ + if(data != '\r'){ + itemWriteByte(data); + } else { + _multiParseState = EXPECT_FEED1; + } + } else if(_multiParseState == EXPECT_BOUNDARY){ + if(_parsedLength < 2 && data != '-'){ + _multiParseState = PARSE_ERROR; + return; + } else if(_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data){ + _multiParseState = PARSE_ERROR; + return; + } else if(_parsedLength - 2 == _boundary.length() && data != '\r'){ + _multiParseState = PARSE_ERROR; + return; + } else if(_parsedLength - 3 == _boundary.length()){ + if(data != '\n'){ + _multiParseState = PARSE_ERROR; + return; + } + _multiParseState = PARSE_HEADERS; + _itemIsFile = false; + } + } else if(_multiParseState == PARSE_HEADERS){ + if((char)data != '\r' && (char)data != '\n') + _temp += (char)data; + if((char)data == '\n'){ + if(_temp.length()){ + if(_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase("Content-Type")){ + _itemType = _temp.substring(14); + _itemIsFile = true; + } else if(_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase("Content-Disposition")){ + _temp = _temp.substring(_temp.indexOf(';') + 2); + while(_temp.indexOf(';') > 0){ + String name = _temp.substring(0, _temp.indexOf('=')); + String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1); + if(name == "name"){ + _itemName = nameVal; + } else if(name == "filename"){ + _itemFilename = nameVal; + _itemIsFile = true; + } + _temp = _temp.substring(_temp.indexOf(';') + 2); + } + String name = _temp.substring(0, _temp.indexOf('=')); + String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1); + if(name == "name"){ + _itemName = nameVal; + } else if(name == "filename"){ + _itemFilename = nameVal; + _itemIsFile = true; + } + } + _temp = String(); + } else { + _multiParseState = WAIT_FOR_RETURN1; + //value starts from here + _itemSize = 0; + _itemStartIndex = _parsedLength; + _itemValue = String(); + if(_itemIsFile){ + if(_itemBuffer) + free(_itemBuffer); + _itemBuffer = (uint8_t*)malloc(1460); + if(_itemBuffer == NULL){ + _multiParseState = PARSE_ERROR; + return; + } + _itemBufferIndex = 0; + } + } + } + } else if(_multiParseState == EXPECT_FEED1){ + if(data != '\n'){ + _multiParseState = WAIT_FOR_RETURN1; + itemWriteByte('\r'); _parseMultipartPostByte(data, last); + } else { + _multiParseState = EXPECT_DASH1; + } + } else if(_multiParseState == EXPECT_DASH1){ + if(data != '-'){ + _multiParseState = WAIT_FOR_RETURN1; + itemWriteByte('\r'); itemWriteByte('\n'); _parseMultipartPostByte(data, last); + } else { + _multiParseState = EXPECT_DASH2; + } + } else if(_multiParseState == EXPECT_DASH2){ + if(data != '-'){ + _multiParseState = WAIT_FOR_RETURN1; + itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); _parseMultipartPostByte(data, last); + } else { + _multiParseState = BOUNDARY_OR_DATA; + _boundaryPosition = 0; + } + } else if(_multiParseState == BOUNDARY_OR_DATA){ + if(_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data){ + _multiParseState = WAIT_FOR_RETURN1; + itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); + uint8_t i; + for(i=0; i<_boundaryPosition; i++) + itemWriteByte(_boundary.c_str()[i]); + _parseMultipartPostByte(data, last); + } else if(_boundaryPosition == _boundary.length() - 1){ + _multiParseState = DASH3_OR_RETURN2; + if(!_itemIsFile){ + _addParam(new AsyncWebParameter(_itemName, _itemValue, true)); + } else { + if(_itemSize){ + //check if authenticated before calling the upload + if(_handler) _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true); + _itemBufferIndex = 0; + _addParam(new AsyncWebParameter(_itemName, _itemFilename, true, true, _itemSize)); + } + free(_itemBuffer); + _itemBuffer = NULL; + } + + } else { + _boundaryPosition++; + } + } else if(_multiParseState == DASH3_OR_RETURN2){ + if(data == '-' && (_contentLength - _parsedLength - 4) != 0){ + //os_printf("ERROR: The parser got to the end of the POST but is expecting %u bytes more!\nDrop an issue so we can have more info on the matter!\n", _contentLength - _parsedLength - 4); + _contentLength = _parsedLength + 4;//lets close the request gracefully + } + if(data == '\r'){ + _multiParseState = EXPECT_FEED2; + } else if(data == '-' && _contentLength == (_parsedLength + 4)){ + _multiParseState = PARSING_FINISHED; + } else { + _multiParseState = WAIT_FOR_RETURN1; + itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); + uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]); + _parseMultipartPostByte(data, last); + } + } else if(_multiParseState == EXPECT_FEED2){ + if(data == '\n'){ + _multiParseState = PARSE_HEADERS; + _itemIsFile = false; + } else { + _multiParseState = WAIT_FOR_RETURN1; + itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); + uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]); + itemWriteByte('\r'); _parseMultipartPostByte(data, last); + } + } +} + +void AsyncWebServerRequest::_parseLine(){ + if(_parseState == PARSE_REQ_START){ + if(!_temp.length()){ + _parseState = PARSE_REQ_FAIL; + _client->close(); + } else { + _parseReqHead(); + _parseState = PARSE_REQ_HEADERS; + } + return; + } + + if(_parseState == PARSE_REQ_HEADERS){ + if(!_temp.length()){ + //end of headers + _server->_rewriteRequest(this); + _server->_attachHandler(this); + _removeNotInterestingHeaders(); + if(_expectingContinue){ + const char * response = "HTTP/1.1 100 Continue\r\n\r\n"; + _client->write(response, os_strlen(response)); + } + //check handler for authentication + if(_contentLength){ + _parseState = PARSE_REQ_BODY; + } else { + _parseState = PARSE_REQ_END; + if(_handler) _handler->handleRequest(this); + else send(501); + } + } else _parseReqHeader(); + } +} + +size_t AsyncWebServerRequest::headers() const{ + return _headers.length(); +} + +bool AsyncWebServerRequest::hasHeader(const String& name) const { + for(const auto& h: _headers){ + if(h->name().equalsIgnoreCase(name)){ + return true; + } + } + return false; +} + +bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper * data) const { + PGM_P p = reinterpret_cast(data); + size_t n = 0; + while (1) { + if (pgm_read_byte(p+n) == 0) break; + n += 1; + } + char * name = (char*) malloc(n+1); + name[n] = 0; + if (name) { + for(size_t b=0; bname().equalsIgnoreCase(name)){ + return h; + } + } + return nullptr; +} + +AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const { + PGM_P p = reinterpret_cast(data); + size_t n = strlen_P(p); + char * name = (char*) malloc(n+1); + if (name) { + strcpy_P(name, p); + AsyncWebHeader* result = getHeader( String(name)); + free(name); + return result; + } else { + return nullptr; + } +} + +AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const { + auto header = _headers.nth(num); + return header ? *header : nullptr; +} + +size_t AsyncWebServerRequest::params() const { + return _params.length(); +} + +bool AsyncWebServerRequest::hasParam(const String& name, bool post, bool file) const { + for(const auto& p: _params){ + if(p->name() == name && p->isPost() == post && p->isFile() == file){ + return true; + } + } + return false; +} + +bool AsyncWebServerRequest::hasParam(const __FlashStringHelper * data, bool post, bool file) const { + PGM_P p = reinterpret_cast(data); + size_t n = strlen_P(p); + + char * name = (char*) malloc(n+1); + name[n] = 0; + if (name) { + strcpy_P(name,p); + bool result = hasParam( name, post, file); + free(name); + return result; + } else { + return false; + } +} + +AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post, bool file) const { + for(const auto& p: _params){ + if(p->name() == name && p->isPost() == post && p->isFile() == file){ + return p; + } + } + return nullptr; +} + +AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper * data, bool post, bool file) const { + PGM_P p = reinterpret_cast(data); + size_t n = strlen_P(p); + char * name = (char*) malloc(n+1); + if (name) { + strcpy_P(name, p); + AsyncWebParameter* result = getParam(name, post, file); + free(name); + return result; + } else { + return nullptr; + } +} + +AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const { + auto param = _params.nth(num); + return param ? *param : nullptr; +} + +void AsyncWebServerRequest::addInterestingHeader(const String& name){ + if(!_interestingHeaders.containsIgnoreCase(name)) + _interestingHeaders.add(name); +} + +void AsyncWebServerRequest::send(AsyncWebServerResponse *response){ + _response = response; + if(_response == NULL){ + _client->close(true); + _onDisconnect(); + return; + } + if(!_response->_sourceValid()){ + delete response; + _response = NULL; + send(500); + } + else { + _client->setRxTimeout(0); + _response->_respond(this); + } +} + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const String& content){ + return new AsyncBasicResponse(code, contentType, content); +} + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ + if(fs.exists(path) || (!download && fs.exists(path+".gz"))) + return new AsyncFileResponse(fs, path, contentType, download, callback); + return NULL; +} + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ + if(content == true) + return new AsyncFileResponse(content, path, contentType, download, callback); + return NULL; +} + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback){ + return new AsyncStreamResponse(stream, contentType, len, callback); +} + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ + return new AsyncCallbackResponse(contentType, len, callback, templateCallback); +} + +AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ + if(_version) + return new AsyncChunkedResponse(contentType, callback, templateCallback); + return new AsyncCallbackResponse(contentType, 0, callback, templateCallback); +} + +AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& contentType, size_t bufferSize){ + return new AsyncResponseStream(contentType, bufferSize); +} + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){ + return new AsyncProgmemResponse(code, contentType, content, len, callback); +} + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){ + return beginResponse_P(code, contentType, (const uint8_t *)content, strlen_P(content), callback); +} + +void AsyncWebServerRequest::send(int code, const String& contentType, const String& content){ + send(beginResponse(code, contentType, content)); +} + +void AsyncWebServerRequest::send(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ + if(fs.exists(path) || (!download && fs.exists(path+".gz"))){ + send(beginResponse(fs, path, contentType, download, callback)); + } else send(404); +} + +void AsyncWebServerRequest::send(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ + if(content == true){ + send(beginResponse(content, path, contentType, download, callback)); + } else send(404); +} + +void AsyncWebServerRequest::send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback){ + send(beginResponse(stream, contentType, len, callback)); +} + +void AsyncWebServerRequest::send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ + send(beginResponse(contentType, len, callback, templateCallback)); +} + +void AsyncWebServerRequest::sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ + send(beginChunkedResponse(contentType, callback, templateCallback)); +} + +void AsyncWebServerRequest::send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){ + send(beginResponse_P(code, contentType, content, len, callback)); +} + +void AsyncWebServerRequest::send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){ + send(beginResponse_P(code, contentType, content, callback)); +} + +void AsyncWebServerRequest::redirect(const String& url){ + AsyncWebServerResponse * response = beginResponse(302); + response->addHeader("Location",url); + send(response); +} + +bool AsyncWebServerRequest::authenticate(const char * username, const char * password, const char * realm, bool passwordIsHash){ + if(_authorization.length()){ + if(_isDigest) + return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL); + else if(!passwordIsHash) + return checkBasicAuthentication(_authorization.c_str(), username, password); + else + return _authorization.equals(password); + } + return false; +} + +bool AsyncWebServerRequest::authenticate(const char * hash){ + if(!_authorization.length() || hash == NULL) + return false; + + if(_isDigest){ + String hStr = String(hash); + int separator = hStr.indexOf(":"); + if(separator <= 0) + return false; + String username = hStr.substring(0, separator); + hStr = hStr.substring(separator + 1); + separator = hStr.indexOf(":"); + if(separator <= 0) + return false; + String realm = hStr.substring(0, separator); + hStr = hStr.substring(separator + 1); + return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), realm.c_str(), true, NULL, NULL, NULL); + } + + return (_authorization.equals(hash)); +} + +void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest){ + AsyncWebServerResponse * r = beginResponse(401); + if(!isDigest && realm == NULL){ + r->addHeader("WWW-Authenticate", "Basic realm=\"Login Required\""); + } else if(!isDigest){ + String header = "Basic realm=\""; + header.concat(realm); + header.concat("\""); + r->addHeader("WWW-Authenticate", header); + } else { + String header = "Digest "; + header.concat(requestDigestAuthentication(realm)); + r->addHeader("WWW-Authenticate", header); + } + send(r); +} + +bool AsyncWebServerRequest::hasArg(const char* name) const { + for(const auto& arg: _params){ + if(arg->name() == name){ + return true; + } + } + return false; +} + +bool AsyncWebServerRequest::hasArg(const __FlashStringHelper * data) const { + PGM_P p = reinterpret_cast(data); + size_t n = strlen_P(p); + char * name = (char*) malloc(n+1); + if (name) { + strcpy_P(name, p); + bool result = hasArg( name ); + free(name); + return result; + } else { + return false; + } +} + + +const String& AsyncWebServerRequest::arg(const String& name) const { + for(const auto& arg: _params){ + if(arg->name() == name){ + return arg->value(); + } + } + return SharedEmptyString; +} + +const String& AsyncWebServerRequest::arg(const __FlashStringHelper * data) const { + PGM_P p = reinterpret_cast(data); + size_t n = strlen_P(p); + char * name = (char*) malloc(n+1); + if (name) { + strcpy(name, p); + const String & result = arg( String(name) ); + free(name); + return result; + } else { + return SharedEmptyString; + } + +} + +const String& AsyncWebServerRequest::arg(size_t i) const { + return getParam(i)->value(); +} + +const String& AsyncWebServerRequest::argName(size_t i) const { + return getParam(i)->name(); +} + +const String& AsyncWebServerRequest::header(const char* name) const { + AsyncWebHeader* h = getHeader(String(name)); + return h ? h->value() : SharedEmptyString; +} + +const String& AsyncWebServerRequest::header(const __FlashStringHelper * data) const { + PGM_P p = reinterpret_cast(data); + size_t n = strlen_P(p); + char * name = (char*) malloc(n+1); + if (name) { + strcpy_P(name, p); + const String & result = header( (const char *)name ); + free(name); + return result; + } else { + return SharedEmptyString; + } +}; + + +const String& AsyncWebServerRequest::header(size_t i) const { + AsyncWebHeader* h = getHeader(i); + return h ? h->value() : SharedEmptyString; +} + +const String& AsyncWebServerRequest::headerName(size_t i) const { + AsyncWebHeader* h = getHeader(i); + return h ? h->name() : SharedEmptyString; +} + +String AsyncWebServerRequest::urlDecode(const String& text) const { + char temp[] = "0x00"; + unsigned int len = text.length(); + unsigned int i = 0; + String decoded = String(); + decoded.reserve(len); // Allocate the string internal buffer - never longer from source text + 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.concat(decodedChar); + } + return decoded; +} + + +const char * AsyncWebServerRequest::methodToString() const { + if(_method == HTTP_ANY) return "ANY"; + else if(_method & HTTP_GET) return "GET"; + else if(_method & HTTP_POST) return "POST"; + else if(_method & HTTP_DELETE) return "DELETE"; + else if(_method & HTTP_PUT) return "PUT"; + else if(_method & HTTP_PATCH) return "PATCH"; + else if(_method & HTTP_HEAD) return "HEAD"; + else if(_method & HTTP_OPTIONS) return "OPTIONS"; + return "UNKNOWN"; +} + +const char *AsyncWebServerRequest::requestedConnTypeToString() const { + switch (_reqconntype) { + case RCT_NOT_USED: return "RCT_NOT_USED"; + case RCT_DEFAULT: return "RCT_DEFAULT"; + case RCT_HTTP: return "RCT_HTTP"; + case RCT_WS: return "RCT_WS"; + case RCT_EVENT: return "RCT_EVENT"; + default: return "ERROR"; + } +} + +bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) { + bool res = false; + if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) res = true; + if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) res = true; + if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)) res = true; + return res; +} diff --git a/libraries/ESPAsyncWebServer/src/WebResponseImpl.h b/libraries/ESPAsyncWebServer/src/WebResponseImpl.h new file mode 100644 index 00000000..d7e8c383 --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/WebResponseImpl.h @@ -0,0 +1,129 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +#ifndef ASYNCWEBSERVERRESPONSEIMPL_H_ +#define ASYNCWEBSERVERRESPONSEIMPL_H_ + +#ifdef Arduino_h +// arduino is not compatible with std::vector +#undef min +#undef max +#endif +#include +// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max. + +class AsyncBasicResponse: public AsyncWebServerResponse { + private: + String _content; + public: + AsyncBasicResponse(int code, const String& contentType=String(), const String& content=String()); + void _respond(AsyncWebServerRequest *request); + size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); + bool _sourceValid() const { return true; } +}; + +class AsyncAbstractResponse: public AsyncWebServerResponse { + private: + String _head; + std::vector _cache; + size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len); + size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen); + protected: + AwsTemplateProcessor _callback; + public: + AsyncAbstractResponse(AwsTemplateProcessor callback=nullptr); + void _respond(AsyncWebServerRequest *request); + size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); + bool _sourceValid() const { return false; } + virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } +}; + +#define TEMPLATE_PLACEHOLDER '%' +#define TEMPLATE_PARAM_NAME_LENGTH 32 +class AsyncFileResponse: public AsyncAbstractResponse { + using File = fs::File; + using FS = fs::FS; + private: + File _content; + String _path; + void _setContentType(const String& path); + public: + AsyncFileResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); + AsyncFileResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); + ~AsyncFileResponse(); + bool _sourceValid() const { return !!(_content); } + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; +}; + +class AsyncStreamResponse: public AsyncAbstractResponse { + private: + Stream *_content; + public: + AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); + bool _sourceValid() const { return !!(_content); } + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; +}; + +class AsyncCallbackResponse: public AsyncAbstractResponse { + private: + AwsResponseFiller _content; + size_t _filledLength; + public: + AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); + bool _sourceValid() const { return !!(_content); } + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; +}; + +class AsyncChunkedResponse: public AsyncAbstractResponse { + private: + AwsResponseFiller _content; + size_t _filledLength; + public: + AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); + bool _sourceValid() const { return !!(_content); } + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; +}; + +class AsyncProgmemResponse: public AsyncAbstractResponse { + private: + const uint8_t * _content; + size_t _readLength; + public: + AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); + bool _sourceValid() const { return true; } + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; +}; + +class cbuf; + +class AsyncResponseStream: public AsyncAbstractResponse, public Print { + private: + cbuf *_content; + public: + AsyncResponseStream(const String& contentType, size_t bufferSize); + ~AsyncResponseStream(); + bool _sourceValid() const { return (_state < RESPONSE_END); } + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; + size_t write(const uint8_t *data, size_t len); + size_t write(uint8_t data); + using Print::write; +}; + +#endif /* ASYNCWEBSERVERRESPONSEIMPL_H_ */ diff --git a/libraries/ESPAsyncWebServer/src/WebResponses.cpp b/libraries/ESPAsyncWebServer/src/WebResponses.cpp new file mode 100644 index 00000000..67b5bfdb --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/WebResponses.cpp @@ -0,0 +1,685 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +#include "ESPAsyncWebServer.h" +#include "WebResponseImpl.h" +#include "cbuf.h" + +// Since ESP8266 does not link memchr by default, here's its implementation. +void* memchr(void* ptr, int ch, size_t count) +{ + unsigned char* p = static_cast(ptr); + while(count--) + if(*p++ == static_cast(ch)) + return --p; + return nullptr; +} + + +/* + * Abstract Response + * */ +const char* AsyncWebServerResponse::_responseCodeToString(int code) { + switch (code) { + case 100: return "Continue"; + case 101: return "Switching Protocols"; + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 300: return "Multiple Choices"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + case 307: return "Temporary Redirect"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Time-out"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Request Entity Too Large"; + case 414: return "Request-URI Too Large"; + case 415: return "Unsupported Media Type"; + case 416: return "Requested range not satisfiable"; + case 417: return "Expectation Failed"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Time-out"; + case 505: return "HTTP Version not supported"; + default: return ""; + } +} + +AsyncWebServerResponse::AsyncWebServerResponse() + : _code(0) + , _headers(LinkedList([](AsyncWebHeader *h){ delete h; })) + , _contentType() + , _contentLength(0) + , _sendContentLength(true) + , _chunked(false) + , _headLength(0) + , _sentLength(0) + , _ackedLength(0) + , _writtenLength(0) + , _state(RESPONSE_SETUP) +{ + for(auto header: DefaultHeaders::Instance()) { + _headers.add(new AsyncWebHeader(header->name(), header->value())); + } +} + +AsyncWebServerResponse::~AsyncWebServerResponse(){ + _headers.free(); +} + +void AsyncWebServerResponse::setCode(int code){ + if(_state == RESPONSE_SETUP) + _code = code; +} + +void AsyncWebServerResponse::setContentLength(size_t len){ + if(_state == RESPONSE_SETUP) + _contentLength = len; +} + +void AsyncWebServerResponse::setContentType(const String& type){ + if(_state == RESPONSE_SETUP) + _contentType = type; +} + +void AsyncWebServerResponse::addHeader(const String& name, const String& value){ + _headers.add(new AsyncWebHeader(name, value)); +} + +String AsyncWebServerResponse::_assembleHead(uint8_t version){ + if(version){ + addHeader("Accept-Ranges","none"); + if(_chunked) + addHeader("Transfer-Encoding","chunked"); + } + String out = String(); + int bufSize = 300; + char buf[bufSize]; + + snprintf(buf, bufSize, "HTTP/1.%d %d %s\r\n", version, _code, _responseCodeToString(_code)); + out.concat(buf); + + if(_sendContentLength) { + snprintf(buf, bufSize, "Content-Length: %d\r\n", _contentLength); + out.concat(buf); + } + if(_contentType.length()) { + snprintf(buf, bufSize, "Content-Type: %s\r\n", _contentType.c_str()); + out.concat(buf); + } + + for(const auto& header: _headers){ + snprintf(buf, bufSize, "%s: %s\r\n", header->name().c_str(), header->value().c_str()); + out.concat(buf); + } + _headers.free(); + + out.concat("\r\n"); + _headLength = out.length(); + return out; +} + +bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; } +bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; } +bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; } +bool AsyncWebServerResponse::_sourceValid() const { return false; } +void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); } +size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ return 0; } + +/* + * String/Code Response + * */ +AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content){ + _code = code; + _content = content; + _contentType = contentType; + if(_content.length()){ + _contentLength = _content.length(); + if(!_contentType.length()) + _contentType = "text/plain"; + } + addHeader("Connection","close"); +} + +void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){ + _state = RESPONSE_HEADERS; + String out = _assembleHead(request->version()); + size_t outLen = out.length(); + size_t space = request->client()->space(); + if(!_contentLength && space >= outLen){ + _writtenLength += request->client()->write(out.c_str(), outLen); + _state = RESPONSE_WAIT_ACK; + } else if(_contentLength && space >= outLen + _contentLength){ + out += _content; + outLen += _contentLength; + _writtenLength += request->client()->write(out.c_str(), outLen); + _state = RESPONSE_WAIT_ACK; + } else if(space && space < outLen){ + String partial = out.substring(0, space); + _content = out.substring(space) + _content; + _contentLength += outLen - space; + _writtenLength += request->client()->write(partial.c_str(), partial.length()); + _state = RESPONSE_CONTENT; + } else if(space > outLen && space < (outLen + _contentLength)){ + size_t shift = space - outLen; + outLen += shift; + _sentLength += shift; + out += _content.substring(0, shift); + _content = _content.substring(shift); + _writtenLength += request->client()->write(out.c_str(), outLen); + _state = RESPONSE_CONTENT; + } else { + _content = out + _content; + _contentLength += outLen; + _state = RESPONSE_CONTENT; + } +} + +size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ + _ackedLength += len; + if(_state == RESPONSE_CONTENT){ + size_t available = _contentLength - _sentLength; + size_t space = request->client()->space(); + //we can fit in this packet + if(space > available){ + _writtenLength += request->client()->write(_content.c_str(), available); + _content = String(); + _state = RESPONSE_WAIT_ACK; + return available; + } + //send some data, the rest on ack + String out = _content.substring(0, space); + _content = _content.substring(space); + _sentLength += space; + _writtenLength += request->client()->write(out.c_str(), space); + return space; + } else if(_state == RESPONSE_WAIT_ACK){ + if(_ackedLength >= _writtenLength){ + _state = RESPONSE_END; + } + } + return 0; +} + + +/* + * Abstract Response + * */ + +AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _callback(callback) +{ + // In case of template processing, we're unable to determine real response size + if(callback) { + _contentLength = 0; + _sendContentLength = false; + _chunked = true; + } +} + +void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){ + addHeader("Connection","close"); + _head = _assembleHead(request->version()); + _state = RESPONSE_HEADERS; + _ack(request, 0, 0); +} + +size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ + if(!_sourceValid()){ + _state = RESPONSE_FAILED; + request->client()->close(); + return 0; + } + _ackedLength += len; + size_t space = request->client()->space(); + + size_t headLen = _head.length(); + if(_state == RESPONSE_HEADERS){ + if(space >= headLen){ + _state = RESPONSE_CONTENT; + space -= headLen; + } else { + String out = _head.substring(0, space); + _head = _head.substring(space); + _writtenLength += request->client()->write(out.c_str(), out.length()); + return out.length(); + } + } + + if(_state == RESPONSE_CONTENT){ + size_t outLen; + if(_chunked){ + if(space <= 8){ + return 0; + } + outLen = space; + } else if(!_sendContentLength){ + outLen = space; + } else { + outLen = ((_contentLength - _sentLength) > space)?space:(_contentLength - _sentLength); + } + + uint8_t *buf = (uint8_t *)malloc(outLen+headLen); + if (!buf) { + // os_printf("_ack malloc %d failed\n", outLen+headLen); + return 0; + } + + if(headLen){ + //TODO: memcpy should be faster? + sprintf((char*)buf, "%s", _head.c_str()); + _head = String(); + } + + size_t readLen = 0; + + if(_chunked){ + // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added. + // See RFC2616 sections 2, 3.6.1. + readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8); + outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen; + while(outLen < headLen + 4) buf[outLen++] = ' '; + buf[outLen++] = '\r'; + buf[outLen++] = '\n'; + outLen += readLen; + buf[outLen++] = '\r'; + buf[outLen++] = '\n'; + } else { + outLen = _fillBufferAndProcessTemplates(buf+headLen, outLen) + headLen; + } + + if(outLen) + _writtenLength += request->client()->write((const char*)buf, outLen); + + if(_chunked) + _sentLength += readLen; + else + _sentLength += outLen - headLen; + + free(buf); + + if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)){ + _state = RESPONSE_WAIT_ACK; + } + return outLen; + + } else if(_state == RESPONSE_WAIT_ACK){ + if(!_sendContentLength || _ackedLength >= _writtenLength){ + _state = RESPONSE_END; + if(!_chunked && !_sendContentLength) + request->client()->close(true); + } + } + return 0; +} + +size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len) +{ + // If we have something in cache, copy it to buffer + const size_t readFromCache = std::min(len, _cache.size()); + if(readFromCache) { + memcpy(data, _cache.data(), readFromCache); + _cache.erase(_cache.begin(), _cache.begin() + readFromCache); + } + // If we need to read more... + const size_t needFromFile = len - readFromCache; + const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile); + return readFromCache + readFromContent; +} + +size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len) +{ + if(!_callback) + return _fillBuffer(data, len); + + const size_t originalLen = len; + len = _readDataFromCacheOrContent(data, len); + // Now we've read 'len' bytes, either from cache or from file + // Search for template placeholders + uint8_t* pTemplateStart = data; + while((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1] + uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr; + // temporary buffer to hold parameter name + uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1]; + String paramName; + // cache position to insert remainder of template parameter value + std::vector::iterator i = _cache.end(); + // If closing placeholder is found: + if(pTemplateEnd) { + // prepare argument to callback + const size_t paramNameLength = std::min(sizeof(buf) - 1, (unsigned int)(pTemplateEnd - pTemplateStart - 1)); + if(paramNameLength) { + memcpy(buf, pTemplateStart + 1, paramNameLength); + buf[paramNameLength] = 0; + paramName = String(reinterpret_cast(buf)); + } else { // double percent sign encountered, this is single percent sign escaped. + // remove the 2nd percent sign + memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); + len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1; + ++pTemplateStart; + } + } else if(&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data + memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart); + const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1)); + if(readFromCacheOrContent) { + pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent); + if(pTemplateEnd) { + // prepare argument to callback + *pTemplateEnd = 0; + paramName = String(reinterpret_cast(buf)); + // Copy remaining read-ahead data into cache (when std::vector::insert returning iterator will be available, these 3 lines can be simplified into 1) + const size_t pos = _cache.size(); + _cache.insert(_cache.end(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); + i = _cache.begin() + pos; + pTemplateEnd = &data[len - 1]; + } + else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position + { + // but first, store read file data in cache + _cache.insert(_cache.end(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); + ++pTemplateStart; + } + } + else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position + ++pTemplateStart; + } + else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position + ++pTemplateStart; + if(paramName.length()) { + // call callback and replace with result. + // Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value. + // Data after pTemplateEnd may need to be moved. + // The first byte of data after placeholder is located at pTemplateEnd + 1. + // It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value). + const String paramValue(_callback(paramName)); + const char* pvstr = paramValue.c_str(); + const unsigned int pvlen = paramValue.length(); + const size_t numBytesCopied = std::min(pvlen, static_cast(&data[originalLen - 1] - pTemplateStart + 1)); + // make room for param value + // 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store + if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) { + size_t pos = i - _cache.begin(); + _cache.insert(i, &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]); + i = _cache.begin() + pos; + //2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end + memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied); + } else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied) + //2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit. + // Move the entire data after the placeholder + memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); + // 3. replace placeholder with actual value + memcpy(pTemplateStart, pvstr, numBytesCopied); + // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer) + if(numBytesCopied < pvlen) { + _cache.insert(i, pvstr + numBytesCopied, pvstr + pvlen); + } else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text... + // there is some free room, fill it from cache + const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied; + const size_t totalFreeRoom = originalLen - len + roomFreed; + len += _readDataFromCacheOrContent(&data[len - roomFreed], totalFreeRoom) - roomFreed; + } else { // result is copied fully; it is longer than placeholder text + const size_t roomTaken = pTemplateStart + numBytesCopied - pTemplateEnd - 1; + len = std::min(len + roomTaken, originalLen); + } + } + } // while(pTemplateStart) + return len; +} + + +/* + * File Response + * */ + +AsyncFileResponse::~AsyncFileResponse(){ + if(_content) + _content.close(); +} + +void AsyncFileResponse::_setContentType(const String& path){ + if (path.endsWith(".html")) _contentType = "text/html"; + else if (path.endsWith(".htm")) _contentType = "text/html"; + else if (path.endsWith(".css")) _contentType = "text/css"; + else if (path.endsWith(".json")) _contentType = "text/json"; + else if (path.endsWith(".js")) _contentType = "application/javascript"; + else if (path.endsWith(".png")) _contentType = "image/png"; + else if (path.endsWith(".gif")) _contentType = "image/gif"; + else if (path.endsWith(".jpg")) _contentType = "image/jpeg"; + else if (path.endsWith(".ico")) _contentType = "image/x-icon"; + else if (path.endsWith(".svg")) _contentType = "image/svg+xml"; + else if (path.endsWith(".eot")) _contentType = "font/eot"; + else if (path.endsWith(".woff")) _contentType = "font/woff"; + else if (path.endsWith(".woff2")) _contentType = "font/woff2"; + else if (path.endsWith(".ttf")) _contentType = "font/ttf"; + else if (path.endsWith(".xml")) _contentType = "text/xml"; + else if (path.endsWith(".pdf")) _contentType = "application/pdf"; + else if (path.endsWith(".zip")) _contentType = "application/zip"; + else if(path.endsWith(".gz")) _contentType = "application/x-gzip"; + else _contentType = "text/plain"; +} + +AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){ + _code = 200; + _path = path; + + if(!download && !fs.exists(_path) && fs.exists(_path+".gz")){ + _path = _path+".gz"; + addHeader("Content-Encoding", "gzip"); + _callback = nullptr; // Unable to process zipped templates + _sendContentLength = true; + _chunked = false; + } + + _content = fs.open(_path, "r"); + _contentLength = _content.size(); + + if(contentType == "") + _setContentType(path); + else + _contentType = contentType; + + int filenameStart = path.lastIndexOf('/') + 1; + char buf[26+path.length()-filenameStart]; + char* filename = (char*)path.c_str() + filenameStart; + + if(download) { + // set filename and force download + snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename); + } else { + // set filename and force rendering + snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename); + } + addHeader("Content-Disposition", buf); +} + +AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){ + _code = 200; + _path = path; + + if(!download && String(content.name()).endsWith(".gz") && !path.endsWith(".gz")){ + addHeader("Content-Encoding", "gzip"); + _callback = nullptr; // Unable to process gzipped templates + _sendContentLength = true; + _chunked = false; + } + + _content = content; + _contentLength = _content.size(); + + if(contentType == "") + _setContentType(path); + else + _contentType = contentType; + + int filenameStart = path.lastIndexOf('/') + 1; + char buf[26+path.length()-filenameStart]; + char* filename = (char*)path.c_str() + filenameStart; + + if(download) { + snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename); + } else { + snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename); + } + addHeader("Content-Disposition", buf); +} + +size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){ + return _content.read(data, len); +} + +/* + * Stream Response + * */ + +AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) { + _code = 200; + _content = &stream; + _contentLength = len; + _contentType = contentType; +} + +size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){ + size_t available = _content->available(); + size_t outLen = (available > len)?len:available; + size_t i; + for(i=0;iread(); + return outLen; +} + +/* + * Callback Response + * */ + +AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback): AsyncAbstractResponse(templateCallback) { + _code = 200; + _content = callback; + _contentLength = len; + if(!len) + _sendContentLength = false; + _contentType = contentType; + _filledLength = 0; +} + +size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){ + size_t ret = _content(data, len, _filledLength); + _filledLength += ret; + return ret; +} + +/* + * Chunked Response + * */ + +AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) { + _code = 200; + _content = callback; + _contentLength = 0; + _contentType = contentType; + _sendContentLength = false; + _chunked = true; + _filledLength = 0; +} + +size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){ + size_t ret = _content(data, len, _filledLength); + _filledLength += ret; + return ret; +} + +/* + * Progmem Response + * */ + +AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) { + _code = code; + _content = content; + _contentType = contentType; + _contentLength = len; + _readLength = 0; +} + +size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){ + size_t left = _contentLength - _readLength; + if (left > len) { + memcpy_P(data, _content + _readLength, len); + _readLength += len; + return len; + } + memcpy_P(data, _content + _readLength, left); + _readLength += left; + return left; +} + + +/* + * Response Stream (You can print/write/printf to it, up to the contentLen bytes) + * */ + +AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize){ + _code = 200; + _contentLength = 0; + _contentType = contentType; + _content = new cbuf(bufferSize); +} + +AsyncResponseStream::~AsyncResponseStream(){ + delete _content; +} + +size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){ + return _content->read((char*)buf, maxLen); +} + +size_t AsyncResponseStream::write(const uint8_t *data, size_t len){ + if(_started()) + return 0; + + if(len > _content->room()){ + size_t needed = len - _content->room(); + _content->resizeAdd(needed); + } + size_t written = _content->write((const char*)data, len); + _contentLength += written; + return written; +} + +size_t AsyncResponseStream::write(uint8_t data){ + return write(&data, 1); +} diff --git a/libraries/ESPAsyncWebServer/src/WebServer.cpp b/libraries/ESPAsyncWebServer/src/WebServer.cpp new file mode 100644 index 00000000..28d2055e --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/WebServer.cpp @@ -0,0 +1,187 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core 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 +*/ +#include "ESPAsyncWebServer.h" +#include "WebHandlerImpl.h" + +bool ON_STA_FILTER(AsyncWebServerRequest *request) { + return WiFi.localIP() == request->client()->localIP(); +} + +bool ON_AP_FILTER(AsyncWebServerRequest *request) { + return WiFi.localIP() != request->client()->localIP(); +} + + +AsyncWebServer::AsyncWebServer(uint16_t port) + : _server(port) + , _rewrites(LinkedList([](AsyncWebRewrite* r){ delete r; })) + , _handlers(LinkedList([](AsyncWebHandler* h){ delete h; })) +{ + _catchAllHandler = new AsyncCallbackWebHandler(); + if(_catchAllHandler == NULL) + return; + _server.onClient([](void *s, AsyncClient* c){ + if(c == NULL) + return; + c->setRxTimeout(3); + AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer*)s, c); + if(r == NULL){ + c->close(true); + c->free(); + delete c; + } + }, this); +} + +AsyncWebServer::~AsyncWebServer(){ + reset(); + delete _catchAllHandler; +} + +AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite){ + _rewrites.add(rewrite); + return *rewrite; +} + +bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite){ + return _rewrites.remove(rewrite); +} + +AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to){ + return addRewrite(new AsyncWebRewrite(from, to)); +} + +AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler){ + _handlers.add(handler); + return *handler; +} + +bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){ + return _handlers.remove(handler); +} + +void AsyncWebServer::begin(){ + _server.begin(); +} + +#if ASYNC_TCP_SSL_ENABLED +void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg){ + _server.onSslFileRequest(cb, arg); +} + +void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password){ + _server.beginSecure(cert, key, password); +} +#endif + +void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request){ + delete request; +} + +void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request){ + for(const auto& r: _rewrites){ + if (r->from() == request->_url && r->filter(request)){ + request->_url = r->toUrl(); + request->_addGetParams(r->params()); + } + } +} + +void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){ + for(const auto& h: _handlers){ + if (h->filter(request) && h->canHandle(request)){ + request->setHandler(h); + return; + } + } + + request->addInterestingHeader("ANY"); + request->setHandler(_catchAllHandler); +} + + +AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody){ + AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); + handler->setUri(uri); + handler->setMethod(method); + handler->onRequest(onRequest); + handler->onUpload(onUpload); + handler->onBody(onBody); + addHandler(handler); + return *handler; +} + +AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){ + AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); + handler->setUri(uri); + handler->setMethod(method); + handler->onRequest(onRequest); + handler->onUpload(onUpload); + addHandler(handler); + return *handler; +} + +AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){ + AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); + handler->setUri(uri); + handler->setMethod(method); + handler->onRequest(onRequest); + addHandler(handler); + return *handler; +} + +AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){ + AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); + handler->setUri(uri); + handler->onRequest(onRequest); + addHandler(handler); + return *handler; +} + +AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control){ + AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control); + addHandler(handler); + return *handler; +} + +void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){ + _catchAllHandler->onRequest(fn); +} + +void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn){ + _catchAllHandler->onUpload(fn); +} + +void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){ + _catchAllHandler->onBody(fn); +} + +void AsyncWebServer::reset(){ + _rewrites.free(); + _handlers.free(); + + if (_catchAllHandler != NULL){ + _catchAllHandler->onRequest(NULL); + _catchAllHandler->onUpload(NULL); + _catchAllHandler->onBody(NULL); + } +} + diff --git a/libraries/ESPAsyncWebServer/src/edit.htm b/libraries/ESPAsyncWebServer/src/edit.htm new file mode 100644 index 00000000..43d49845 --- /dev/null +++ b/libraries/ESPAsyncWebServer/src/edit.htm @@ -0,0 +1,627 @@ + + + + +ESP Editor + + + + + + +
+
+
+
+ + + + diff --git a/libraries/ESPAsyncWebServer/travis/common.sh b/libraries/ESPAsyncWebServer/travis/common.sh new file mode 100644 index 00000000..57bede34 --- /dev/null +++ b/libraries/ESPAsyncWebServer/travis/common.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +function build_sketches() +{ + local arduino=$1 + local srcpath=$2 + local platform=$3 + local sketches=$(find $srcpath -name *.ino) + for sketch in $sketches; do + local sketchdir=$(dirname $sketch) + if [[ -f "$sketchdir/.$platform.skip" ]]; then + echo -e "\n\n ------------ Skipping $sketch ------------ \n\n"; + continue + fi + echo -e "\n\n ------------ Building $sketch ------------ \n\n"; + $arduino --verify $sketch; + local result=$? + if [ $result -ne 0 ]; then + echo "Build failed ($1)" + return $result + fi + done +} diff --git a/libraries/WebServer/.travis.yml b/libraries/WebServer/.travis.yml deleted file mode 100644 index 3c0d1d70..00000000 --- a/libraries/WebServer/.travis.yml +++ /dev/null @@ -1,99 +0,0 @@ -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 - diff --git a/libraries/WebServer/README.md b/libraries/WebServer/README.md deleted file mode 100644 index 5ec07c51..00000000 --- a/libraries/WebServer/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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 diff --git a/libraries/WebServer/examples/AdvancedWebServer/AdvancedWebServer.ino b/libraries/WebServer/examples/AdvancedWebServer/AdvancedWebServer.ino deleted file mode 100644 index b46b101d..00000000 --- a/libraries/WebServer/examples/AdvancedWebServer/AdvancedWebServer.ino +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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 -#include -#include -#include -ESP8266WebServer server ( 80 ); -#else -#include -#include -#include -#include -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, - -"\ - \ - \ - ESP8266/ESP32 Demo\ - \ - \ - \ -

Hello from ESP8266/ESP32!

\ -

Uptime: %02d:%02d:%02d

\ - \ - \ -", - - 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 += "\n"; - out += "\n"; - out += "\n"; - int y = rand() % 130; - for (int x = 10; x < 390; x+= 10) { - int y2 = rand() % 130; - sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); - out += temp; - y = y2; - } - out += "\n\n"; - - server.send ( 200, "image/svg+xml", out); -} diff --git a/libraries/WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/WebServer/examples/FSBrowser/FSBrowser.ino deleted file mode 100644 index e4cea8a1..00000000 --- a/libraries/WebServer/examples/FSBrowser/FSBrowser.ino +++ /dev/null @@ -1,345 +0,0 @@ -/* - 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:///edit >/dev/null - * curl -X POST -F "data=@graphs.js.gz" http:///edit >/dev/null - * curl -X POST -F "data=@favicon.ico" http:///edit >/dev/null - * curl -X POST -F "data=@edit.htm.gz" http:///edit >/dev/null - */ -#ifdef ESP8266 -#include -#include -#include -#include -#include -ESP8266WebServer server(80); -#else -#include -#include -#include -#include -#include -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(); -} diff --git a/libraries/WebServer/examples/FSBrowser/data/edit.htm.gz b/libraries/WebServer/examples/FSBrowser/data/edit.htm.gz deleted file mode 100644 index 69ce414f..00000000 Binary files a/libraries/WebServer/examples/FSBrowser/data/edit.htm.gz and /dev/null differ diff --git a/libraries/WebServer/examples/FSBrowser/data/graphs.js.gz b/libraries/WebServer/examples/FSBrowser/data/graphs.js.gz deleted file mode 100644 index 72435445..00000000 Binary files a/libraries/WebServer/examples/FSBrowser/data/graphs.js.gz and /dev/null differ diff --git a/libraries/WebServer/examples/FSBrowser/data/index.htm b/libraries/WebServer/examples/FSBrowser/data/index.htm deleted file mode 100644 index 4e1dc7d9..00000000 --- a/libraries/WebServer/examples/FSBrowser/data/index.htm +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - ESP Monitor - - - - -
- - - - -
-
-
-
- - \ No newline at end of file diff --git a/libraries/WebServer/examples/HelloServer/HelloServer.ino b/libraries/WebServer/examples/HelloServer/HelloServer.ino deleted file mode 100644 index f5390364..00000000 --- a/libraries/WebServer/examples/HelloServer/HelloServer.ino +++ /dev/null @@ -1,91 +0,0 @@ -#ifdef ESP8266 -#include -#include -#include -#include -ESP8266WebServer server(80); -#else -#include -#include -#include -#include -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 -#include -#include -#include -ESP8266WebServer server(80); -#else -#include -#include -#include -#include -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(); -} diff --git a/libraries/WebServer/examples/SDWebServer/SDWebServer.ino b/libraries/WebServer/examples/SDWebServer/SDWebServer.ino deleted file mode 100644 index 4234a880..00000000 --- a/libraries/WebServer/examples/SDWebServer/SDWebServer.ino +++ /dev/null @@ -1,290 +0,0 @@ -/* - 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 -#include -#include -#include -#include -#include -ESP8266WebServer server(80); -#else -#include -#include -#include -#include -#include -#include -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 - - - SD Editor - - - - - -
-
-
- - - - diff --git a/libraries/WebServer/examples/SDWebServer/SdRoot/index.htm b/libraries/WebServer/examples/SDWebServer/SdRoot/index.htm deleted file mode 100644 index 55fe5a66..00000000 --- a/libraries/WebServer/examples/SDWebServer/SdRoot/index.htm +++ /dev/null @@ -1,22 +0,0 @@ - - - - - ESP Index - - - - -

ESP8266 Pin Functions

- - - diff --git a/libraries/WebServer/examples/SDWebServer/SdRoot/pins.png b/libraries/WebServer/examples/SDWebServer/SdRoot/pins.png deleted file mode 100644 index ac7fc0f9..00000000 Binary files a/libraries/WebServer/examples/SDWebServer/SdRoot/pins.png and /dev/null differ diff --git a/libraries/WebServer/examples/SimpleAuthentification/SimpleAuthentification.ino b/libraries/WebServer/examples/SimpleAuthentification/SimpleAuthentification.ino deleted file mode 100644 index bf5310e6..00000000 --- a/libraries/WebServer/examples/SimpleAuthentification/SimpleAuthentification.ino +++ /dev/null @@ -1,137 +0,0 @@ -#ifdef ESP8266 -#include -#include -#include -ESP8266WebServer server(80); -#else -#include -#include -#include -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 = "
To log in, please use : admin/admin
"; - content += "User:
"; - content += "Password:
"; - content += "
" + msg + "
"; - content += "You also can go here"; - 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 = "

hello, you successfully connected to esp8266/esp32!


"; - if (server.hasHeader("User-Agent")){ - content += "the user agent used is : " + server.header("User-Agent") + "

"; - } - content += "You can access this page until you disconnect"; - 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 -#include -#include -#include -ESP8266WebServer server(80); -const char* host = "esp8266-webupdate"; -#else -#include -#include -#include -#include -WebServer server(80); -const char* host = "esp32-webupdate"; -#endif - -const char* ssid = "........"; -const char* password = "........"; - -const char* serverIndex = "
"; - -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); -} diff --git a/libraries/WebServer/keywords.txt b/libraries/WebServer/keywords.txt deleted file mode 100644 index 22e3d74e..00000000 --- a/libraries/WebServer/keywords.txt +++ /dev/null @@ -1,36 +0,0 @@ -####################################### -# 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 diff --git a/libraries/WebServer/library.properties b/libraries/WebServer/library.properties deleted file mode 100644 index 1e9c0707..00000000 --- a/libraries/WebServer/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=WebServer -version=1.0 -author=Ivan Grokhotkov -maintainer=Ivan Grokhtkov -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 diff --git a/libraries/WebServer/src/Parsing.cpp b/libraries/WebServer/src/Parsing.cpp deleted file mode 100644 index 542e41be..00000000 --- a/libraries/WebServer/src/Parsing.cpp +++ /dev/null @@ -1,610 +0,0 @@ -/* - 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 -#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; -} diff --git a/libraries/WebServer/src/WebServer.cpp b/libraries/WebServer/src/WebServer.cpp deleted file mode 100644 index 951c6dce..00000000 --- a/libraries/WebServer/src/WebServer.cpp +++ /dev/null @@ -1,530 +0,0 @@ -/* - 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 -#include -#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 ""; - } -} diff --git a/libraries/WebServer/src/WebServer.h b/libraries/WebServer/src/WebServer.h deleted file mode 100644 index 40807867..00000000 --- a/libraries/WebServer/src/WebServer.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - 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 -#ifdef ESP8266 -#define WebServer ESP8266WebServer -#include -#else -#include -#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 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 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 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 diff --git a/libraries/WebServer/src/detail/RequestHandler.h b/libraries/WebServer/src/detail/RequestHandler.h deleted file mode 100644 index c1cc909d..00000000 --- a/libraries/WebServer/src/detail/RequestHandler.h +++ /dev/null @@ -1,19 +0,0 @@ -#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 diff --git a/libraries/WebServer/src/detail/RequestHandlersImpl.h b/libraries/WebServer/src/detail/RequestHandlersImpl.h deleted file mode 100644 index e337bc11..00000000 --- a/libraries/WebServer/src/detail/RequestHandlersImpl.h +++ /dev/null @@ -1,191 +0,0 @@ -#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 diff --git a/libraries/WebServer/.gitignore b/libraries/arduinoWebSockets/.gitignore similarity index 87% rename from libraries/WebServer/.gitignore rename to libraries/arduinoWebSockets/.gitignore index 259148fa..44b2c85f 100644 --- a/libraries/WebServer/.gitignore +++ b/libraries/arduinoWebSockets/.gitignore @@ -1,6 +1,3 @@ -# Prerequisites -*.d - # Compiled Object files *.slo *.lo @@ -18,7 +15,6 @@ # Fortran module files *.mod -*.smod # Compiled Static libraries *.lai @@ -30,3 +26,4 @@ *.exe *.out *.app +/tests/webSocketServer/node_modules diff --git a/libraries/arduinoWebSockets/.travis.yml b/libraries/arduinoWebSockets/.travis.yml new file mode 100644 index 00000000..14693dd8 --- /dev/null +++ b/libraries/arduinoWebSockets/.travis.yml @@ -0,0 +1,40 @@ +sudo: false +language: bash +os: + - linux +env: + matrix: + - CPU="esp8266" BOARD="esp8266com:esp8266:generic:CpuFrequency=80" IDE_VERSION=1.6.5 + - CPU="esp8266" BOARD="esp8266com:esp8266:generic:CpuFrequency=80,FlashSize=1M0,FlashMode=qio,FlashFreq=80" IDE_VERSION=1.8.5 + - CPU="esp8266" BOARD="esp8266com:esp8266:generic:CpuFrequency=80,Debug=Serial1" IDE_VERSION=1.6.5 + - CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.6.5 + - CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.5 + +script: + - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16 + - sleep 3 + - export DISPLAY=:1.0 + - wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz + - tar xf arduino-$IDE_VERSION-linux64.tar.xz + - mv arduino-$IDE_VERSION $HOME/arduino_ide + - export PATH="$HOME/arduino_ide:$PATH" + - which arduino + - mkdir -p $HOME/Arduino/libraries + - cp -r $TRAVIS_BUILD_DIR $HOME/Arduino/libraries/arduinoWebSockets + - source $TRAVIS_BUILD_DIR/travis/common.sh + - get_core $CPU + - cd $TRAVIS_BUILD_DIR + - arduino --board $BOARD --save-prefs + - arduino --get-pref sketchbook.path + - build_sketches arduino $HOME/Arduino/libraries/arduinoWebSockets/examples/$CPU $CPU + +notifications: + email: + on_success: change + on_failure: change + webhooks: + urls: + - https://webhooks.gitter.im/e/1aa78fbe15080b0c2e37 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: false # default: false diff --git a/libraries/WebServer/LICENSE b/libraries/arduinoWebSockets/LICENSE similarity index 98% rename from libraries/WebServer/LICENSE rename to libraries/arduinoWebSockets/LICENSE index 19e30718..f166cc57 100644 --- a/libraries/WebServer/LICENSE +++ b/libraries/arduinoWebSockets/LICENSE @@ -6,9 +6,9 @@ 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 +[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.) + the version number 2.1.] Preamble @@ -55,7 +55,7 @@ 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 @@ -111,7 +111,7 @@ 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 @@ -158,7 +158,7 @@ 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 @@ -216,7 +216,7 @@ 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. @@ -267,7 +267,7 @@ Library will still fall under Section 6.) 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 @@ -329,7 +329,7 @@ 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 @@ -370,7 +370,7 @@ 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 @@ -422,7 +422,7 @@ 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 @@ -456,7 +456,7 @@ 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 @@ -470,8 +470,8 @@ 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} + + Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -485,8 +485,7 @@ convey the exclusion of warranty; and each file should have at least the 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 + 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. @@ -495,10 +494,9 @@ 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. + library `Frob' (a library for tweaking knobs) written by James Random Hacker. - {signature of Ty Coon}, 1 April 1990 + , 1 April 1990 Ty Coon, President of Vice -That's all there is to it! +That's all there is to it! \ No newline at end of file diff --git a/libraries/arduinoWebSockets/README.md b/libraries/arduinoWebSockets/README.md new file mode 100644 index 00000000..63eef3e2 --- /dev/null +++ b/libraries/arduinoWebSockets/README.md @@ -0,0 +1,98 @@ +WebSocket Server and Client for Arduino [![Build Status](https://travis-ci.org/Links2004/arduinoWebSockets.svg?branch=master)](https://travis-ci.org/Links2004/arduinoWebSockets) +=========================================== + +a WebSocket Server and Client for Arduino based on RFC6455. + + +##### Supported features of RFC6455 ##### + - text frame + - binary frame + - connection close + - ping + - pong + - continuation frame + +##### Limitations ##### + - max input length is limited to the ram size and the ```WEBSOCKETS_MAX_DATA_SIZE``` define + - max output length has no limit (the hardware is the limit) + - Client send big frames with mask 0x00000000 (on AVR all frames) + - continuation frame reassembly need to be handled in the application code + + ##### Limitations for Async ##### + - Functions called from within the context of the websocket event might not honor `yield()` and/or `delay()`. See [this issue](https://github.com/Links2004/arduinoWebSockets/issues/58#issuecomment-192376395) for more info and a potential workaround. + - wss / SSL is not possible. + +##### Supported Hardware ##### + - ESP8266 [Arduino for ESP8266](https://github.com/esp8266/Arduino/) + - ESP32 [Arduino for ESP32](https://github.com/espressif/arduino-esp32) + - ESP31B + - Particle with STM32 ARM Cortex M3 + - ATmega328 with Ethernet Shield (ATmega branch) + - ATmega328 with enc28j60 (ATmega branch) + - ATmega2560 with Ethernet Shield (ATmega branch) + - ATmega2560 with enc28j60 (ATmega branch) + +###### Note: ###### + + version 2.0 and up is not compatible with AVR/ATmega, check ATmega branch. + + Arduino for AVR not supports std namespace of c++. + +### wss / SSL ### + supported for: + - wss client on the ESP8266 + - wss / SSL is not natively supported in WebSocketsServer however it is possible to achieve secure websockets + by running the device behind an SSL proxy. See [Nginx](examples/Nginx/esp8266.ssl.reverse.proxy.conf) for a + sample Nginx server configuration file to enable this. + +### ESP Async TCP ### + +This libary can run in Async TCP mode on the ESP. + +The mode can be activated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE define). + +[ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) libary is required. + + +### High Level Client API ### + + - `begin` : Initiate connection sequence to the websocket host. +``` +void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino"); +void begin(String host, uint16_t port, String url = "/", String protocol = "arduino"); + ``` + - `onEvent`: Callback to handle for websocket events + + ``` + void onEvent(WebSocketClientEvent cbEvent); + ``` + + - `WebSocketClientEvent`: Handler for websocket events + ``` + void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length) + ``` +Where `WStype_t type` is defined as: + ``` + typedef enum { + WStype_ERROR, + WStype_DISCONNECTED, + WStype_CONNECTED, + WStype_TEXT, + WStype_BIN, + WStype_FRAGMENT_TEXT_START, + WStype_FRAGMENT_BIN_START, + WStype_FRAGMENT, + WStype_FRAGMENT_FIN, + } WStype_t; + ``` + +### Issues ### +Submit issues to: https://github.com/Links2004/arduinoWebSockets/issues + +[![Join the chat at https://gitter.im/Links2004/arduinoWebSockets](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Links2004/arduinoWebSockets?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +### License and credits ### + +The library is licensed under [LGPLv2.1](https://github.com/Links2004/arduinoWebSockets/blob/master/LICENSE) + +[libb64](http://libb64.sourceforge.net/) written by Chris Venter. It is distributed under Public Domain see [LICENSE](https://github.com/Links2004/arduinoWebSockets/blob/master/src/libb64/LICENSE). diff --git a/libraries/arduinoWebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf b/libraries/arduinoWebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf new file mode 100644 index 00000000..ec5aa89f --- /dev/null +++ b/libraries/arduinoWebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf @@ -0,0 +1,83 @@ +# ESP8266 nginx SSL reverse proxy configuration file (tested and working on nginx v1.10.0) + +# proxy cache location +proxy_cache_path /opt/etc/nginx/cache levels=1:2 keys_zone=ESP8266_cache:10m max_size=10g inactive=5m use_temp_path=off; + +# webserver proxy +server { + + # general server parameters + listen 50080; + server_name myDomain.net; + access_log /opt/var/log/nginx/myDomain.net.access.log; + + # SSL configuration + ssl on; + ssl_certificate /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/fullchain.pem; + ssl_certificate_key /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/privkey.pem; + ssl_session_cache builtin:1000 shared:SSL:10m; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4; + ssl_prefer_server_ciphers on; + + location / { + + # proxy caching configuration + proxy_cache ESP8266_cache; + proxy_cache_revalidate on; + proxy_cache_min_uses 1; + proxy_cache_use_stale off; + proxy_cache_lock on; + # proxy_cache_bypass $http_cache_control; + # include the sessionId cookie value as part of the cache key - keeps the cache per user + # proxy_cache_key $proxy_host$request_uri$cookie_sessionId; + + # header pass through configuration + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # ESP8266 custom headers which identify to the device that it's running through an SSL proxy + proxy_set_header X-SSL On; + proxy_set_header X-SSL-WebserverPort 50080; + proxy_set_header X-SSL-WebsocketPort 50081; + + # extra debug headers + add_header X-Proxy-Cache $upstream_cache_status; + add_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # actual proxying configuration + proxy_ssl_session_reuse on; + # target the IP address of the device with proxy_pass + proxy_pass http://192.168.0.20; + proxy_read_timeout 90; + } + } + +# websocket proxy +server { + + # general server parameters + listen 50081; + server_name myDomain.net; + access_log /opt/var/log/nginx/myDomain.net.wss.access.log; + + # SSL configuration + ssl on; + ssl_certificate /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/fullchain.pem; + ssl_certificate_key /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/privkey.pem; + ssl_session_cache builtin:1000 shared:SSL:10m; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4; + ssl_prefer_server_ciphers on; + + location / { + + # websocket upgrade tunnel configuration + proxy_pass http://192.168.0.20:81; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_read_timeout 86400; + } + } diff --git a/libraries/arduinoWebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino b/libraries/arduinoWebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino new file mode 100644 index 00000000..9d49d149 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino @@ -0,0 +1,84 @@ +/* + * WebSocketClientAVR.ino + * + * Created on: 10.12.2015 + * + */ + +#include + +#include +#include + +#include + + + +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + +// Set the static IP address to use if the DHCP fails to assign +IPAddress ip(192, 168, 0, 177); + +WebSocketsClient webSocket; + + + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + + switch(type) { + case WStype_DISCONNECTED: + Serial.println("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + Serial.print("[WSc] Connected to url: "); + Serial.println((char *)payload); + // send message to server when Connected + webSocket.sendTXT("Connected"); + } + break; + case WStype_TEXT: + Serial.print("[WSc] get text: "); + Serial.println((char *)payload); + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + Serial.print("[WSc] get binary length: "); + Serial.println(length); + // hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() +{ + // Open serial communications and wait for port to open: + Serial.begin(115200); + while (!Serial) {} + + // start the Ethernet connection: + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // no point in carrying on, so do nothing forevermore: + // try to congifure using IP address instead of DHCP: + Ethernet.begin(mac, ip); + } + + webSocket.begin("192.168.0.123", 8011); + webSocket.onEvent(webSocketEvent); + +} + + +void loop() +{ + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino b/libraries/arduinoWebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino new file mode 100644 index 00000000..41a2b703 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino @@ -0,0 +1,106 @@ +/* + * WebSocketClient.ino + * + * Created on: 24.05.2015 + * + */ + +#include + +#include +#include +#include + +#include + + +WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + +HardwareSerial Serial1(2); + +#define USE_SERIAL Serial1 + +void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) { + const uint8_t* src = (const uint8_t*) mem; + USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); + for(uint32_t i = 0; i < len; i++) { + if(i % cols == 0) { + USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); + } + USE_SERIAL.printf("%02X ", *src); + src++; + } + USE_SERIAL.printf("\n"); +} + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + // send message to server when Connected + webSocket.sendTXT("Connected"); + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + // server address, port and URL + webSocket.begin("192.168.0.123", 81, "/"); + + // event handler + webSocket.onEvent(webSocketEvent); + + // use HTTP Basic Authorization this is optional remove if not needed + webSocket.setAuthorization("user", "Password"); + + // try ever 5000 again if connection has failed + webSocket.setReconnectInterval(5000); + +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino b/libraries/arduinoWebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino new file mode 100644 index 00000000..dc3427b1 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino @@ -0,0 +1,102 @@ +/* + * WebSocketClientSSL.ino + * + * Created on: 10.12.2015 + * + * note SSL is only possible with the ESP8266 + * + */ + +#include + +#include +#include +#include + +#include + + +WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + +HardwareSerial Serial1(2); + +#define USE_SERIAL Serial1 + +void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) { + const uint8_t* src = (const uint8_t*) mem; + USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); + for(uint32_t i = 0; i < len; i++) { + if(i % cols == 0) { + USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); + } + USE_SERIAL.printf("%02X ", *src); + src++; + } + USE_SERIAL.printf("\n"); +} + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + // send message to server when Connected + webSocket.sendTXT("Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.beginSSL("192.168.0.123", 81); + webSocket.onEvent(webSocketEvent); + +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino b/libraries/arduinoWebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino new file mode 100644 index 00000000..7ebf8cab --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino @@ -0,0 +1,100 @@ +/* + * WebSocketServer.ino + * + * Created on: 22.05.2015 + * + */ + +#include + +#include +#include +#include + +#include + +WiFiMulti WiFiMulti; +WebSocketsServer webSocket = WebSocketsServer(81); + +HardwareSerial Serial1(2); + +#define USE_SERIAL Serial1 + +void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) { + const uint8_t* src = (const uint8_t*) mem; + USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); + for(uint32_t i = 0; i < len; i++) { + if(i % cols == 0) { + USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); + } + USE_SERIAL.printf("%02X ", *src); + src++; + } + USE_SERIAL.printf("\n"); +} + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: + { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + // send message to client + // webSocket.sendTXT(num, "message here"); + + // send data to all connected clients + // webSocket.broadcastTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[%u] get binary length: %u\n", num, length); + hexdump(payload, length); + + // send message to client + // webSocket.sendBIN(num, payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.begin(); + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino new file mode 100644 index 00000000..b990c13a --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino @@ -0,0 +1,92 @@ +/* + * WebSocketClient.ino + * + * Created on: 24.05.2015 + * + */ + +#include + +#include +#include + +#include + +#include + +ESP8266WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + +#define USE_SERIAL Serial1 + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + // send message to server when Connected + webSocket.sendTXT("Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + // server address, port and URL + webSocket.begin("192.168.0.123", 81, "/"); + + // event handler + webSocket.onEvent(webSocketEvent); + + // use HTTP Basic Authorization this is optional remove if not needed + webSocket.setAuthorization("user", "Password"); + + // try ever 5000 again if connection has failed + webSocket.setReconnectInterval(5000); + +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino new file mode 100644 index 00000000..d45060e9 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino @@ -0,0 +1,88 @@ +/* + * WebSocketClientSSL.ino + * + * Created on: 10.12.2015 + * + * note SSL is only possible with the ESP8266 + * + */ + +#include + +#include +#include + +#include + +#include + +ESP8266WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + + +#define USE_SERIAL Serial1 + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + // send message to server when Connected + webSocket.sendTXT("Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.beginSSL("192.168.0.123", 81); + webSocket.onEvent(webSocketEvent); + +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino new file mode 100644 index 00000000..40e343e2 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino @@ -0,0 +1,113 @@ +/* + * WebSocketClientSocketIO.ino + * + * Created on: 06.06.2016 + * + */ + +#include + +#include +#include + +#include + +#include + +ESP8266WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + + +#define USE_SERIAL Serial1 + +#define MESSAGE_INTERVAL 30000 +#define HEARTBEAT_INTERVAL 25000 + +uint64_t messageTimestamp = 0; +uint64_t heartbeatTimestamp = 0; +bool isConnected = false; + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + isConnected = false; + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + isConnected = true; + + // send message to server when Connected + // socket.io upgrade confirmation message (required) + webSocket.sendTXT("5"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.beginSocketIO("192.168.0.123", 81); + //webSocket.setAuthorization("user", "Password"); // HTTP Basic Authorization + webSocket.onEvent(webSocketEvent); + +} + +void loop() { + webSocket.loop(); + + if(isConnected) { + + uint64_t now = millis(); + + if(now - messageTimestamp > MESSAGE_INTERVAL) { + messageTimestamp = now; + // example socket.io message with type "messageType" and JSON payload + webSocket.sendTXT("42[\"messageType\",{\"greeting\":\"hello\"}]"); + } + if((now - heartbeatTimestamp) > HEARTBEAT_INTERVAL) { + heartbeatTimestamp = now; + // socket.io heartbeat message + webSocket.sendTXT("2"); + } + } +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino new file mode 100644 index 00000000..a0eb011f --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino @@ -0,0 +1,149 @@ +/* + WebSocketClientStomp.ino + + Example for connecting and maintining a connection with a STOMP websocket connection. + In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html). + + Created on: 25.09.2017 + Author: Martin Becker , Contact: becker@informatik.uni-wuerzburg.de +*/ + +// PRE + +#define USE_SERIAL Serial + + +// LIBRARIES + +#include +#include + +#include +#include + + +// SETTINGS + +const char* wlan_ssid = "yourssid"; +const char* wlan_password = "somepassword"; + +const char* ws_host = "the.host.net"; +const int ws_port = 80; + +// URL for STOMP endpoint. +// For the default config of Spring's STOMP support, the default URL is "/socketentry/websocket". +const char* stompUrl = "/socketentry/websocket"; // don't forget the leading "/" !!! + + +// VARIABLES + +WebSocketsClient webSocket; + + +// FUNCTIONS + +/** + * STOMP messages need to be NULL-terminated (i.e., \0 or \u0000). + * However, when we send a String or a char[] array without specifying + * a length, the size of the message payload is derived by strlen() internally, + * thus dropping any NULL values appended to the "msg"-String. + * + * To solve this, we first convert the String to a NULL terminated char[] array + * via "c_str" and set the length of the payload to include the NULL value. + */ +void sendMessage(String & msg) { + webSocket.sendTXT(msg.c_str(), msg.length() + 1); +} + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + switch (type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + String msg = "CONNECT\r\naccept-version:1.1,1.0\r\nheart-beat:10000,10000\r\n\r\n"; + sendMessage(msg); + } + break; + case WStype_TEXT: + { + // ##################### + // handle STOMP protocol + // ##################### + + String text = (char*) payload; + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + if (text.startsWith("CONNECTED")) { + + // subscribe to some channels + + String msg = "SUBSCRIBE\nid:sub-0\ndestination:/user/queue/messages\n\n"; + sendMessage(msg); + delay(1000); + + // and send a message + + msg = "SEND\ndestination:/app/message\n\n{\"user\":\"esp\",\"message\":\"Hello!\"}"; + sendMessage(msg); + delay(1000); + + } else { + + // do something with messages + + } + + break; + } + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + + // setup serial + + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + // USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + + + // connect to WiFi + + USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ..."); + WiFi.mode(WIFI_STA); + WiFi.begin(wlan_ssid, wlan_password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + USE_SERIAL.print("."); + } + USE_SERIAL.println(" success."); + USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP()); + + + // connect to websocket + webSocket.begin(ws_host, ws_port, stompUrl); + webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config + // webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino new file mode 100644 index 00000000..cb0c45be --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino @@ -0,0 +1,150 @@ +/* + WebSocketClientStompOverSockJs.ino + + Example for connecting and maintining a connection with a SockJS+STOMP websocket connection. + In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html). + + Created on: 18.07.2017 + Author: Martin Becker , Contact: becker@informatik.uni-wuerzburg.de +*/ + +// PRE + +#define USE_SERIAL Serial + + +// LIBRARIES + +#include +#include + +#include +#include + + +// SETTINGS + +const char* wlan_ssid = "yourssid"; +const char* wlan_password = "somepassword"; + +const char* ws_host = "the.host.net"; +const int ws_port = 80; + +// base URL for SockJS (websocket) connection +// The complete URL will look something like this(cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36): +// ws://://<3digits>//websocket +// For the default config of Spring's SockJS/STOMP support, the default base URL is "/socketentry/". +const char* ws_baseurl = "/socketentry/"; // don't forget leading and trailing "/" !!! + + +// VARIABLES + +WebSocketsClient webSocket; + + +// FUNCTIONS + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + switch (type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + } + break; + case WStype_TEXT: + { + // ##################### + // handle SockJs+STOMP protocol + // ##################### + + String text = (char*) payload; + + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + if (payload[0] == 'h') { + + USE_SERIAL.println("Heartbeat!"); + + } else if (payload[0] == 'o') { + + // on open connection + char *msg = "[\"CONNECT\\naccept-version:1.1,1.0\\nheart-beat:10000,10000\\n\\n\\u0000\"]"; + webSocket.sendTXT(msg); + + } else if (text.startsWith("a[\"CONNECTED")) { + + // subscribe to some channels + + char *msg = "[\"SUBSCRIBE\\nid:sub-0\\ndestination:/user/queue/messages\\n\\n\\u0000\"]"; + webSocket.sendTXT(msg); + delay(1000); + + // and send a message + + msg = "[\"SEND\\ndestination:/app/message\\n\\n{\\\"user\\\":\\\"esp\\\",\\\"message\\\":\\\"Hello!\\\"}\\u0000\"]"; + webSocket.sendTXT(msg); + delay(1000); + } + + break; + } + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + + // setup serial + + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + // USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + + + // connect to WiFi + + USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ..."); + WiFi.mode(WIFI_STA); + WiFi.begin(wlan_ssid, wlan_password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + USE_SERIAL.print("."); + } + USE_SERIAL.println(" success."); + USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP()); + + + // ##################### + // create socket url according to SockJS protocol (cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36) + // ##################### + String socketUrl = ws_baseurl; + socketUrl += random(0, 999); + socketUrl += "/"; + socketUrl += random(0, 999999); // should be a random string, but this works (see ) + socketUrl += "/websocket"; + + // connect to websocket + webSocket.begin(ws_host, ws_port, socketUrl); + webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config + // webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino new file mode 100644 index 00000000..1ac3002d --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino @@ -0,0 +1,86 @@ +/* + * WebSocketServer.ino + * + * Created on: 22.05.2015 + * + */ + +#include + +#include +#include +#include +#include + +ESP8266WiFiMulti WiFiMulti; + +WebSocketsServer webSocket = WebSocketsServer(81); + +#define USE_SERIAL Serial1 + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: + { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + // send message to client + // webSocket.sendTXT(num, "message here"); + + // send data to all connected clients + // webSocket.broadcastTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[%u] get binary length: %u\n", num, length); + hexdump(payload, length); + + // send message to client + // webSocket.sendBIN(num, payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.begin(); + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} + diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino new file mode 100644 index 00000000..5fed1a95 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino @@ -0,0 +1,132 @@ +/* + * WebSocketServerAllFunctionsDemo.ino + * + * Created on: 10.05.2018 + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define LED_RED 15 +#define LED_GREEN 12 +#define LED_BLUE 13 + +#define USE_SERIAL Serial + +ESP8266WiFiMulti WiFiMulti; + +ESP8266WebServer server(80); +WebSocketsServer webSocket = WebSocketsServer(81); + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + if(payload[0] == '#') { + // we get RGB data + + // decode rgb data + uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16); + + analogWrite(LED_RED, ((rgb >> 16) & 0xFF)); + analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF)); + analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF)); + } + + break; + } + +} + +void setup() { + //USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + pinMode(LED_RED, OUTPUT); + pinMode(LED_GREEN, OUTPUT); + pinMode(LED_BLUE, OUTPUT); + + digitalWrite(LED_RED, 1); + digitalWrite(LED_GREEN, 1); + digitalWrite(LED_BLUE, 1); + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + // start webSocket server + webSocket.begin(); + webSocket.onEvent(webSocketEvent); + + if(MDNS.begin("esp8266")) { + USE_SERIAL.println("MDNS responder started"); + } + + // handle index + server.on("/", []() { + // send index.html + server.send(200, "text/html", "LED Control:

R:
G:
B:
"); + }); + + server.begin(); + + // Add service to MDNS + MDNS.addService("http", "tcp", 80); + MDNS.addService("ws", "tcp", 81); + + digitalWrite(LED_RED, 0); + digitalWrite(LED_GREEN, 0); + digitalWrite(LED_BLUE, 0); + +} + +unsigned long last_10sec = 0; +unsigned int counter = 0; + +void loop() { + unsigned long t = millis(); + webSocket.loop(); + server.handleClient(); + + if((t - last_10sec) > 10 * 1000) { + counter++; + bool ping = (counter % 2); + int i = webSocket.connectedClients(ping); + USE_SERIAL.printf("%d Connected websocket clients ping: %d\n", i, ping); + last_10sec = millis(); + } +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino new file mode 100644 index 00000000..84c9775d --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino @@ -0,0 +1,94 @@ +/* + * WebSocketServer.ino + * + * Created on: 22.05.2015 + * + */ + +#include + +#include +#include +#include +#include + +ESP8266WiFiMulti WiFiMulti; + +WebSocketsServer webSocket = WebSocketsServer(81); + +#define USE_SERIAL Serial + +String fragmentBuffer = ""; + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + break; + case WStype_BIN: + USE_SERIAL.printf("[%u] get binary length: %u\n", num, length); + hexdump(payload, length); + break; + + // Fragmentation / continuation opcode handling + // case WStype_FRAGMENT_BIN_START: + case WStype_FRAGMENT_TEXT_START: + fragmentBuffer = (char*)payload; + USE_SERIAL.printf("[%u] get start start of Textfragment: %s\n", num, payload); + break; + case WStype_FRAGMENT: + fragmentBuffer += (char*)payload; + USE_SERIAL.printf("[%u] get Textfragment : %s\n", num, payload); + break; + case WStype_FRAGMENT_FIN: + fragmentBuffer += (char*)payload; + USE_SERIAL.printf("[%u] get end of Textfragment: %s\n", num, payload); + USE_SERIAL.printf("[%u] full frame: %s\n", num, fragmentBuffer.c_str()); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.begin(); + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} + diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino new file mode 100644 index 00000000..8bc646c4 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino @@ -0,0 +1,86 @@ +/* + * WebSocketServerHttpHeaderValidation.ino + * + * Created on: 08.06.2016 + * + */ + +#include + +#include +#include +#include +#include + +ESP8266WiFiMulti WiFiMulti; + +WebSocketsServer webSocket = WebSocketsServer(81); + +#define USE_SERIAL Serial1 + +const unsigned long int validSessionId = 12345; //some arbitrary value to act as a valid sessionId + +/* + * Returns a bool value as an indicator to describe whether a user is allowed to initiate a websocket upgrade + * based on the value of a cookie. This function expects the rawCookieHeaderValue to look like this "sessionId=|" + */ +bool isCookieValid(String rawCookieHeaderValue) { + + if (rawCookieHeaderValue.indexOf("sessionId") != -1) { + String sessionIdStr = rawCookieHeaderValue.substring(rawCookieHeaderValue.indexOf("sessionId=") + 10, rawCookieHeaderValue.indexOf("|")); + unsigned long int sessionId = strtoul(sessionIdStr.c_str(), NULL, 10); + return sessionId == validSessionId; + } + return false; +} + +/* + * The WebSocketServerHttpHeaderValFunc delegate passed to webSocket.onValidateHttpHeader + */ +bool validateHttpHeader(String headerName, String headerValue) { + + //assume a true response for any headers not handled by this validator + bool valid = true; + + if(headerName.equalsIgnoreCase("Cookie")) { + //if the header passed is the Cookie header, validate it according to the rules in 'isCookieValid' function + valid = isCookieValid(headerValue); + } + + return valid; +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + //connecting clients must supply a valid session cookie at websocket upgrade handshake negotiation time + const char * headerkeys[] = { "Cookie" }; + size_t headerKeyCount = sizeof(headerkeys) / sizeof(char*); + webSocket.onValidateHttpHeader(validateHttpHeader, headerkeys, headerKeyCount); + webSocket.begin(); +} + +void loop() { + webSocket.loop(); +} + diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino new file mode 100644 index 00000000..8f32e753 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino @@ -0,0 +1,121 @@ +/* + * WebSocketServer_LEDcontrol.ino + * + * Created on: 26.11.2015 + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define LED_RED 15 +#define LED_GREEN 12 +#define LED_BLUE 13 + +#define USE_SERIAL Serial + + +ESP8266WiFiMulti WiFiMulti; + +ESP8266WebServer server(80); +WebSocketsServer webSocket = WebSocketsServer(81); + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + if(payload[0] == '#') { + // we get RGB data + + // decode rgb data + uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16); + + analogWrite(LED_RED, ((rgb >> 16) & 0xFF)); + analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF)); + analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF)); + } + + break; + } + +} + +void setup() { + //USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + pinMode(LED_RED, OUTPUT); + pinMode(LED_GREEN, OUTPUT); + pinMode(LED_BLUE, OUTPUT); + + digitalWrite(LED_RED, 1); + digitalWrite(LED_GREEN, 1); + digitalWrite(LED_BLUE, 1); + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + // start webSocket server + webSocket.begin(); + webSocket.onEvent(webSocketEvent); + + if(MDNS.begin("esp8266")) { + USE_SERIAL.println("MDNS responder started"); + } + + // handle index + server.on("/", []() { + // send index.html + server.send(200, "text/html", "LED Control:

R:
G:
B:
"); + }); + + server.begin(); + + // Add service to MDNS + MDNS.addService("http", "tcp", 80); + MDNS.addService("ws", "tcp", 81); + + digitalWrite(LED_RED, 0); + digitalWrite(LED_GREEN, 0); + digitalWrite(LED_BLUE, 0); + +} + +void loop() { + webSocket.loop(); + server.handleClient(); +} diff --git a/libraries/arduinoWebSockets/examples/particle/ParticleWebSocketClient/application.cpp b/libraries/arduinoWebSockets/examples/particle/ParticleWebSocketClient/application.cpp new file mode 100644 index 00000000..461228f3 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/particle/ParticleWebSocketClient/application.cpp @@ -0,0 +1,46 @@ +/* To compile using make CLI, create a folder under \firmware\user\applications and copy application.cpp there. +* Then, copy src files under particleWebSocket folder. +*/ + +#include "application.h" +#include "particleWebSocket/WebSocketsClient.h" + +WebSocketsClient webSocket; + +void webSocketEvent(WStype_t type, uint8_t* payload, size_t length) +{ + switch (type) + { + case WStype_DISCONNECTED: + Serial.printlnf("[WSc] Disconnected!"); + break; + case WStype_CONNECTED: + Serial.printlnf("[WSc] Connected to URL: %s", payload); + webSocket.sendTXT("Connected\r\n"); + break; + case WStype_TEXT: + Serial.printlnf("[WSc] get text: %s", payload); + break; + case WStype_BIN: + Serial.printlnf("[WSc] get binary length: %u", length); + break; + } +} + +void setup() +{ + Serial.begin(9600); + + WiFi.setCredentials("[SSID]", "[PASSWORD]", WPA2, WLAN_CIPHER_AES_TKIP); + WiFi.connect(); + + webSocket.begin("192.168.1.153", 85, "/ClientService/?variable=Test1212"); + webSocket.onEvent(webSocketEvent); +} + +void loop() +{ + webSocket.sendTXT("Hello world!"); + delay(500); + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/library.json b/libraries/arduinoWebSockets/library.json new file mode 100644 index 00000000..0dbd62d2 --- /dev/null +++ b/libraries/arduinoWebSockets/library.json @@ -0,0 +1,25 @@ +{ + "name": "WebSockets", + "description": "WebSocket Server and Client for Arduino based on RFC6455", + "keywords": "wifi, http, web, server, client, websocket", + "authors": [ + { + "name": "Markus Sattler", + "url": "https://github.com/Links2004", + "maintainer": true + } + ], + "repository": { + "type": "git", + "url": "https://github.com/Links2004/arduinoWebSockets.git" + }, + "version": "2.1.0", + "license": "LGPL-2.1", + "export": { + "exclude": [ + "tests" + ] + }, + "frameworks": "arduino", + "platforms": "atmelavr, espressif8266, espressif32" +} diff --git a/libraries/arduinoWebSockets/library.properties b/libraries/arduinoWebSockets/library.properties new file mode 100644 index 00000000..14e1fe95 --- /dev/null +++ b/libraries/arduinoWebSockets/library.properties @@ -0,0 +1,9 @@ +name=WebSockets +version=2.1.0 +author=Markus Sattler +maintainer=Markus Sattler +sentence=WebSockets for Arduino (Server + Client) +paragraph=use 2.x.x for ESP and 1.3 for AVR +category=Communication +url=https://github.com/Links2004/arduinoWebSockets +architectures=* diff --git a/libraries/arduinoWebSockets/src/WebSockets.cpp b/libraries/arduinoWebSockets/src/WebSockets.cpp new file mode 100644 index 00000000..f41cacbe --- /dev/null +++ b/libraries/arduinoWebSockets/src/WebSockets.cpp @@ -0,0 +1,654 @@ +/** + * @file WebSockets.cpp + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * 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 + * + */ + +#include "WebSockets.h" + +#ifdef ESP8266 +#include +#endif + +extern "C" { +#ifdef CORE_HAS_LIBB64 +#include +#else +#include "libb64/cencode_inc.h" +#endif +} + +#ifdef ESP8266 +#include +#elif defined(ESP32) +#include +#else + +extern "C" { +#include "libsha1/libsha1.h" +} + +#endif + + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param code uint16_t see RFC + * @param reason ptr to the disconnect reason message + * @param reasonLen length of the disconnect reason message + */ +void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] clientDisconnect code: %u\n", client->num, code); + if(client->status == WSC_CONNECTED && code) { + if(reason) { + sendFrame(client, WSop_close, (uint8_t *) reason, reasonLen); + } else { + uint8_t buffer[2]; + buffer[0] = ((code >> 8) & 0xFF); + buffer[1] = (code & 0xFF); + sendFrame(client, WSop_close, &buffer[0], 2); + } + } + clientDisconnect(client); +} + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param opcode WSopcode_t + * @param payload uint8_t * ptr to the payload + * @param length size_t length of the payload + * @param mask bool add dummy mask to the frame (needed for web browser) + * @param fin bool can be used to send data in more then one frame (set fin on the last frame) + * @param headerToPayload bool set true if the payload has reserved 14 Byte at the beginning to dynamically add the Header (payload neet to be in RAM!) + * @return true if ok + */ +bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool mask, bool fin, bool headerToPayload) { + + if(client->tcp && !client->tcp->connected()) { + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not Connected!?\n", client->num); + return false; + } + + if(client->status != WSC_CONNECTED) { + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not in WSC_CONNECTED state!?\n", client->num); + return false; + } + + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] ------- send message frame -------\n", client->num); + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] fin: %u opCode: %u mask: %u length: %u headerToPayload: %u\n", client->num, fin, opcode, mask, length, headerToPayload); + + if(opcode == WSop_text) { + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] text: %s\n", client->num, (payload + (headerToPayload ? 14 : 0))); + } + + uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 }; + uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 }; + + uint8_t headerSize; + uint8_t * headerPtr; + uint8_t * payloadPtr = payload; + bool useInternBuffer = false; + bool ret = true; + + // calculate header Size + if(length < 126) { + headerSize = 2; + } else if(length < 0xFFFF) { + headerSize = 4; + } else { + headerSize = 10; + } + + if(mask) { + headerSize += 4; + } + +#ifdef WEBSOCKETS_USE_BIG_MEM + // only for ESP since AVR has less HEAP + // try to send data in one TCP package (only if some free Heap is there) + if(!headerToPayload && ((length > 0) && (length < 1400)) && (GET_FREE_HEAP > 6000)) { + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num); + uint8_t * dataPtr = (uint8_t *) malloc(length + WEBSOCKETS_MAX_HEADER_SIZE); + if(dataPtr) { + memcpy((dataPtr + WEBSOCKETS_MAX_HEADER_SIZE), payload, length); + headerToPayload = true; + useInternBuffer = true; + payloadPtr = dataPtr; + } + } +#endif + + // set Header Pointer + if(headerToPayload) { + // calculate offset in payload + headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize)); + } else { + headerPtr = &buffer[0]; + } + + // create header + + // byte 0 + *headerPtr = 0x00; + if(fin) { + *headerPtr |= bit(7); ///< set Fin + } + *headerPtr |= opcode; ///< set opcode + headerPtr++; + + // byte 1 + *headerPtr = 0x00; + if(mask) { + *headerPtr |= bit(7); ///< set mask + } + + if(length < 126) { + *headerPtr |= length; + headerPtr++; + } else if(length < 0xFFFF) { + *headerPtr |= 126; + headerPtr++; + *headerPtr = ((length >> 8) & 0xFF); + headerPtr++; + *headerPtr = (length & 0xFF); + headerPtr++; + } else { + // Normally we never get here (to less memory) + *headerPtr |= 127; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = ((length >> 24) & 0xFF); + headerPtr++; + *headerPtr = ((length >> 16) & 0xFF); + headerPtr++; + *headerPtr = ((length >> 8) & 0xFF); + headerPtr++; + *headerPtr = (length & 0xFF); + headerPtr++; + } + + if(mask) { + if(useInternBuffer) { + // if we use a Intern Buffer we can modify the data + // by this fact its possible the do the masking + for(uint8_t x = 0; x < sizeof(maskKey); x++) { + maskKey[x] = random(0xFF); + *headerPtr = maskKey[x]; + headerPtr++; + } + + uint8_t * dataMaskPtr; + + if(headerToPayload) { + dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE); + } else { + dataMaskPtr = payloadPtr; + } + + for(size_t x = 0; x < length; x++) { + dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]); + } + + } else { + *headerPtr = maskKey[0]; + headerPtr++; + *headerPtr = maskKey[1]; + headerPtr++; + *headerPtr = maskKey[2]; + headerPtr++; + *headerPtr = maskKey[3]; + headerPtr++; + } + } + +#ifndef NODEBUG_WEBSOCKETS + unsigned long start = micros(); +#endif + + if(headerToPayload) { + // header has be added to payload + // payload is forced to reserved 14 Byte but we may not need all based on the length and mask settings + // offset in payload is calculatetd 14 - headerSize + if(write(client, &payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) { + ret = false; + } + } else { + // send header + if(write(client, &buffer[0], headerSize) != headerSize) { + ret = false; + } + + if(payloadPtr && length > 0) { + // send payload + if(write(client, &payloadPtr[0], length) != length) { + ret = false; + } + } + } + + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] sending Frame Done (%luus).\n", client->num, (micros() - start)); + +#ifdef WEBSOCKETS_USE_BIG_MEM + if(useInternBuffer && payloadPtr) { + free(payloadPtr); + } +#endif + + return ret; +} + +/** + * callen when HTTP header is done + * @param client WSclient_t * ptr to the client struct + */ +void WebSockets::headerDone(WSclient_t * client) { + client->status = WSC_CONNECTED; + client->cWsRXsize = 0; + DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done.\n", client->num); +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->cHttpLine = ""; + handleWebsocket(client); +#endif +} + +/** + * handle the WebSocket stream + * @param client WSclient_t * ptr to the client struct + */ +void WebSockets::handleWebsocket(WSclient_t * client) { + if(client->cWsRXsize == 0) { + handleWebsocketCb(client); + } +} + +/** + * wait for + * @param client + * @param size + */ +bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) { + if(!client->tcp || !client->tcp->connected()) { + return false; + } + + if(size > WEBSOCKETS_MAX_HEADER_SIZE) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d too big!\n", client->num, size); + return false; + } + + if(client->cWsRXsize >= size) { + return true; + } + + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d cWsRXsize: %d\n", client->num, size, client->cWsRXsize); + readCb(client, &client->cWsHeader[client->cWsRXsize], (size - client->cWsRXsize), std::bind([](WebSockets * server, size_t size, WSclient_t * client, bool ok) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor][readCb] size: %d ok: %d\n", client->num, size, ok); + if(ok) { + client->cWsRXsize = size; + server->handleWebsocketCb(client); + } else { + DEBUG_WEBSOCKETS("[WS][%d][readCb] failed.\n", client->num); + client->cWsRXsize = 0; + // timeout or error + server->clientDisconnect(client, 1002); + } + }, this, size, std::placeholders::_1, std::placeholders::_2)); + return false; +} + +void WebSockets::handleWebsocketCb(WSclient_t * client) { + + if(!client->tcp || !client->tcp->connected()) { + return; + } + + uint8_t * buffer = client->cWsHeader; + + WSMessageHeader_t * header = &client->cWsHeaderDecode; + uint8_t * payload = NULL; + + uint8_t headerLen = 2; + + if(!handleWebsocketWaitFor(client, headerLen)) { + return; + } + + // split first 2 bytes in the data + header->fin = ((*buffer >> 7) & 0x01); + header->rsv1 = ((*buffer >> 6) & 0x01); + header->rsv2 = ((*buffer >> 5) & 0x01); + header->rsv3 = ((*buffer >> 4) & 0x01); + header->opCode = (WSopcode_t) (*buffer & 0x0F); + buffer++; + + header->mask = ((*buffer >> 7) & 0x01); + header->payloadLen = (WSopcode_t) (*buffer & 0x7F); + buffer++; + + if(header->payloadLen == 126) { + headerLen += 2; + if(!handleWebsocketWaitFor(client, headerLen)) { + return; + } + header->payloadLen = buffer[0] << 8 | buffer[1]; + buffer += 2; + } else if(header->payloadLen == 127) { + headerLen += 8; + // read 64bit integer as length + if(!handleWebsocketWaitFor(client, headerLen)) { + return; + } + + if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) { + // really too big! + header->payloadLen = 0xFFFFFFFF; + } else { + header->payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7]; + } + buffer += 8; + } + + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ------- read massage frame -------\n", client->num); + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] fin: %u rsv1: %u rsv2: %u rsv3 %u opCode: %u\n", client->num, header->fin, header->rsv1, header->rsv2, header->rsv3, header->opCode); + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, header->mask, header->payloadLen); + + if(header->payloadLen > WEBSOCKETS_MAX_DATA_SIZE) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload too big! (%u)\n", client->num, header->payloadLen); + clientDisconnect(client, 1009); + return; + } + + if(header->mask) { + headerLen += 4; + if(!handleWebsocketWaitFor(client, headerLen)) { + return; + } + header->maskKey = buffer; + buffer += 4; + } + + if(header->payloadLen > 0) { + // if text data we need one more + payload = (uint8_t *) malloc(header->payloadLen + 1); + + if(!payload) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, header->payloadLen); + clientDisconnect(client, 1011); + return; + } + readCb(client, payload, header->payloadLen, std::bind(&WebSockets::handleWebsocketPayloadCb, this, std::placeholders::_1, std::placeholders::_2, payload)); + } else { + handleWebsocketPayloadCb(client, true, NULL); + } +} + +void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload) { + + WSMessageHeader_t * header = &client->cWsHeaderDecode; + if(ok) { + if(header->payloadLen > 0) { + payload[header->payloadLen] = 0x00; + + if(header->mask) { + //decode XOR + for(size_t i = 0; i < header->payloadLen; i++) { + payload[i] = (payload[i] ^ header->maskKey[i % 4]); + } + } + } + + switch(header->opCode) { + case WSop_text: + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload); + // no break here! + case WSop_binary: + case WSop_continuation: + messageReceived(client, header->opCode, payload, header->payloadLen, header->fin); + break; + case WSop_ping: + // send pong back + sendFrame(client, WSop_pong, payload, header->payloadLen, true); + break; + case WSop_pong: + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char*)payload : ""); + break; + case WSop_close: { + uint16_t reasonCode = 1000; + if(header->payloadLen >= 2) { + reasonCode = payload[0] << 8 | payload[1]; + } + + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d", client->num, reasonCode); + if(header->payloadLen > 2) { + DEBUG_WEBSOCKETS(" (%s)\n", (payload + 2)); + } else { + DEBUG_WEBSOCKETS("\n"); + } + clientDisconnect(client, 1000); + } + break; + default: + clientDisconnect(client, 1002); + break; + } + + if(payload) { + free(payload); + } + + // reset input + client->cWsRXsize = 0; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + //register callback for next message + handleWebsocketWaitFor(client, 2); +#endif + + } else { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] missing data!\n", client->num); + free(payload); + clientDisconnect(client, 1002); + } +} + +/** + * generate the key for Sec-WebSocket-Accept + * @param clientKey String + * @return String Accept Key + */ +String WebSockets::acceptKey(String & clientKey) { + uint8_t sha1HashBin[20] = { 0 }; +#ifdef ESP8266 + sha1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]); +#elif defined(ESP32) + String data = clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + esp_sha(SHA1, (unsigned char*)data.c_str(), data.length(), &sha1HashBin[0]); +#else + clientKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + SHA1_CTX ctx; + SHA1Init(&ctx); + SHA1Update(&ctx, (const unsigned char*)clientKey.c_str(), clientKey.length()); + SHA1Final(&sha1HashBin[0], &ctx); +#endif + + String key = base64_encode(sha1HashBin, 20); + key.trim(); + + return key; +} + +/** + * base64_encode + * @param data uint8_t * + * @param length size_t + * @return base64 encoded String + */ +String WebSockets::base64_encode(uint8_t * data, size_t length) { + size_t size = ((length * 1.6f) + 1); + char * buffer = (char *) malloc(size); + if(buffer) { + base64_encodestate _state; + base64_init_encodestate(&_state); + int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state); + len = base64_encode_blockend((buffer + len), &_state); + + String base64 = String(buffer); + free(buffer); + return base64; + } + return String("-FAIL-"); +} + +/** + * read x byte from tcp or get timeout + * @param client WSclient_t * + * @param out uint8_t * data buffer + * @param n size_t byte count + * @return true if ok + */ +bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb) { +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + if(!client->tcp || !client->tcp->connected()) { + return false; + } + + client->tcp->readBytes(out, n, std::bind([](WSclient_t * client, bool ok, WSreadWaitCb cb) { + if(cb) { + cb(client, ok); + } + }, client, std::placeholders::_1, cb)); + +#else + unsigned long t = millis(); + size_t len; + DEBUG_WEBSOCKETS("[readCb] n: %zu t: %lu\n", n, t); + while(n > 0) { + if(client->tcp == NULL) { + DEBUG_WEBSOCKETS("[readCb] tcp is null!\n"); + if(cb) { + cb(client, false); + } + return false; + } + + if(!client->tcp->connected()) { + DEBUG_WEBSOCKETS("[readCb] not connected!\n"); + if(cb) { + cb(client, false); + } + return false; + } + + if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) { + DEBUG_WEBSOCKETS("[readCb] receive TIMEOUT! %lu\n", (millis() - t)); + if(cb) { + cb(client, false); + } + return false; + } + + if(!client->tcp->available()) { +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + continue; + } + + len = client->tcp->read((uint8_t*) out, n); + if(len) { + t = millis(); + out += len; + n -= len; + //DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n); + } else { + //DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n); + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + if(cb) { + cb(client, true); + } +#endif + return true; +} + +/** + * write x byte to tcp or get timeout + * @param client WSclient_t * + * @param out uint8_t * data buffer + * @param n size_t byte count + * @return bytes send + */ +size_t WebSockets::write(WSclient_t * client, uint8_t *out, size_t n) { + if(out == NULL) return 0; + if(client == NULL) return 0; + unsigned long t = millis(); + size_t len = 0; + size_t total = 0; + DEBUG_WEBSOCKETS("[write] n: %zu t: %lu\n", n, t); + while(n > 0) { + if(client->tcp == NULL) { + DEBUG_WEBSOCKETS("[write] tcp is null!\n"); + break; + } + + if(!client->tcp->connected()) { + DEBUG_WEBSOCKETS("[write] not connected!\n"); + break; + } + + if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) { + DEBUG_WEBSOCKETS("[write] write TIMEOUT! %lu\n", (millis() - t)); + break; + } + + len = client->tcp->write((const uint8_t*)out, n); + if(len) { + t = millis(); + out += len; + n -= len; + total += len; + //DEBUG_WEBSOCKETS("write %d left %d!\n", len, n); + } else { + //DEBUG_WEBSOCKETS("write %d failed left %d!\n", len, n); + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + return total; +} + +size_t WebSockets::write(WSclient_t * client, const char *out) { + if(client == NULL) return 0; + if(out == NULL) return 0; + return write(client, (uint8_t*)out, strlen(out)); +} diff --git a/libraries/arduinoWebSockets/src/WebSockets.h b/libraries/arduinoWebSockets/src/WebSockets.h new file mode 100644 index 00000000..dee63977 --- /dev/null +++ b/libraries/arduinoWebSockets/src/WebSockets.h @@ -0,0 +1,311 @@ +/** + * @file WebSockets.h + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * 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 + * + */ + +#ifndef WEBSOCKETS_H_ +#define WEBSOCKETS_H_ + +#ifdef STM32_DEVICE +#include +#define bit(b) (1UL << (b)) // Taken directly from Arduino.h +#else +#include +#include +#endif + +#ifdef ARDUINO_ARCH_AVR +#error Version 2.x.x currently does not support Arduino with AVR since there is no support for std namespace of c++. +#error Use Version 1.x.x. (ATmega branch) +#else +#include +#endif + + +#ifndef NODEBUG_WEBSOCKETS +#ifdef DEBUG_ESP_PORT +#define DEBUG_WEBSOCKETS(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ ) +#else +//#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ ) +#endif +#endif + + +#ifndef DEBUG_WEBSOCKETS +#define DEBUG_WEBSOCKETS(...) +#define NODEBUG_WEBSOCKETS +#endif + +#if defined(ESP8266) || defined(ESP32) + +#define WEBSOCKETS_MAX_DATA_SIZE (15*1024) +#define WEBSOCKETS_USE_BIG_MEM +#define GET_FREE_HEAP ESP.getFreeHeap() +// moves all Header strings to Flash (~300 Byte) +//#define WEBSOCKETS_SAVE_RAM + +#elif defined(STM32_DEVICE) + +#define WEBSOCKETS_MAX_DATA_SIZE (15*1024) +#define WEBSOCKETS_USE_BIG_MEM +#define GET_FREE_HEAP System.freeMemory() + +#else + +//atmega328p has only 2KB ram! +#define WEBSOCKETS_MAX_DATA_SIZE (1024) +// moves all Header strings to Flash +#define WEBSOCKETS_SAVE_RAM + +#endif + + +#define WEBSOCKETS_TCP_TIMEOUT (2000) + +#define NETWORK_ESP8266_ASYNC (0) +#define NETWORK_ESP8266 (1) +#define NETWORK_W5100 (2) +#define NETWORK_ENC28J60 (3) +#define NETWORK_ESP32 (4) + +// max size of the WS Message Header +#define WEBSOCKETS_MAX_HEADER_SIZE (14) + +#if !defined(WEBSOCKETS_NETWORK_TYPE) +// select Network type based +#if defined(ESP8266) || defined(ESP31B) +#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266 +//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266_ASYNC +//#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100 + +#elif defined(ESP32) +#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32 + +#else +#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100 + +#endif +#endif + +// Includes and defined based on Network Type +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + +// Note: +// No SSL/WSS support for client in Async mode +// TLS lib need a sync interface! + + +#if defined(ESP8266) +#include +#elif defined(ESP32) +#include +#include +#elif defined(ESP31B) +#include +#else +#error "network type ESP8266 ASYNC only possible on the ESP mcu!" +#endif + +#include +#include +#define WEBSOCKETS_NETWORK_CLASS AsyncTCPbuffer +#define WEBSOCKETS_NETWORK_SERVER_CLASS AsyncServer + +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + +#if !defined(ESP8266) && !defined(ESP31B) +#error "network type ESP8266 only possible on the ESP mcu!" +#endif + +#ifdef ESP8266 +#include +#else +#include +#endif +#define WEBSOCKETS_NETWORK_CLASS WiFiClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer + +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100) + +#ifdef STM32_DEVICE +#define WEBSOCKETS_NETWORK_CLASS TCPClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS TCPServer +#else +#include +#include +#define WEBSOCKETS_NETWORK_CLASS EthernetClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer +#endif + +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60) + +#include +#define WEBSOCKETS_NETWORK_CLASS UIPClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS UIPServer + +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + +#include +#include +#define WEBSOCKETS_NETWORK_CLASS WiFiClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer + +#else +#error "no network type selected!" +#endif + +// moves all Header strings to Flash (~300 Byte) +#ifdef WEBSOCKETS_SAVE_RAM +#define WEBSOCKETS_STRING(var) F(var) +#else +#define WEBSOCKETS_STRING(var) var +#endif + +typedef enum { + WSC_NOT_CONNECTED, + WSC_HEADER, + WSC_CONNECTED +} WSclientsStatus_t; + +typedef enum { + WStype_ERROR, + WStype_DISCONNECTED, + WStype_CONNECTED, + WStype_TEXT, + WStype_BIN, + WStype_FRAGMENT_TEXT_START, + WStype_FRAGMENT_BIN_START, + WStype_FRAGMENT, + WStype_FRAGMENT_FIN, +} WStype_t; + +typedef enum { + WSop_continuation = 0x00, ///< %x0 denotes a continuation frame + WSop_text = 0x01, ///< %x1 denotes a text frame + WSop_binary = 0x02, ///< %x2 denotes a binary frame + ///< %x3-7 are reserved for further non-control frames + WSop_close = 0x08, ///< %x8 denotes a connection close + WSop_ping = 0x09, ///< %x9 denotes a ping + WSop_pong = 0x0A ///< %xA denotes a pong + ///< %xB-F are reserved for further control frames +} WSopcode_t; + +typedef struct { + + bool fin; + bool rsv1; + bool rsv2; + bool rsv3; + + WSopcode_t opCode; + bool mask; + + size_t payloadLen; + + uint8_t * maskKey; +} WSMessageHeader_t; + +typedef struct { + uint8_t num; ///< connection number + + WSclientsStatus_t status; + + WEBSOCKETS_NETWORK_CLASS * tcp; + + bool isSocketIO; ///< client for socket.io server + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + bool isSSL; ///< run in ssl mode + WiFiClientSecure * ssl; +#endif + + String cUrl; ///< http url + uint16_t cCode; ///< http code + + bool cIsUpgrade; ///< Connection == Upgrade + bool cIsWebsocket; ///< Upgrade == websocket + + String cSessionId; ///< client Set-Cookie (session id) + String cKey; ///< client Sec-WebSocket-Key + String cAccept; ///< client Sec-WebSocket-Accept + String cProtocol; ///< client Sec-WebSocket-Protocol + String cExtensions; ///< client Sec-WebSocket-Extensions + uint16_t cVersion; ///< client Sec-WebSocket-Version + + uint8_t cWsRXsize; ///< State of the RX + uint8_t cWsHeader[WEBSOCKETS_MAX_HEADER_SIZE]; ///< RX WS Message buffer + WSMessageHeader_t cWsHeaderDecode; + + String base64Authorization; ///< Base64 encoded Auth request + String plainAuthorization; ///< Base64 encoded Auth request + + String extraHeaders; + + bool cHttpHeadersValid; ///< non-websocket http header validity indicator + size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present count + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + String cHttpLine; ///< HTTP header lines +#endif + +} WSclient_t; + + + +class WebSockets { + protected: +#ifdef __AVR__ + typedef void (*WSreadWaitCb)(WSclient_t * client, bool ok); +#else + typedef std::function WSreadWaitCb; +#endif + + virtual void clientDisconnect(WSclient_t * client) = 0; + virtual bool clientIsConnected(WSclient_t * client) = 0; + + virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) = 0; + + void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0); + bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool mask = false, bool fin = true, bool headerToPayload = false); + + void headerDone(WSclient_t * client); + + void handleWebsocket(WSclient_t * client); + + bool handleWebsocketWaitFor(WSclient_t * client, size_t size); + void handleWebsocketCb(WSclient_t * client); + void handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload); + + String acceptKey(String & clientKey); + String base64_encode(uint8_t * data, size_t length); + + bool readCb(WSclient_t * client, uint8_t *out, size_t n, WSreadWaitCb cb); + virtual size_t write(WSclient_t * client, uint8_t *out, size_t n); + size_t write(WSclient_t * client, const char *out); + + +}; + +#ifndef UNUSED +#define UNUSED(var) (void)(var) +#endif +#endif /* WEBSOCKETS_H_ */ diff --git a/libraries/arduinoWebSockets/src/WebSocketsClient.cpp b/libraries/arduinoWebSockets/src/WebSocketsClient.cpp new file mode 100644 index 00000000..f98822a8 --- /dev/null +++ b/libraries/arduinoWebSockets/src/WebSocketsClient.cpp @@ -0,0 +1,762 @@ +/** + * @file WebSocketsClient.cpp + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * 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 + * + */ + +#include "WebSockets.h" +#include "WebSocketsClient.h" + +WebSocketsClient::WebSocketsClient() { + _cbEvent = NULL; + _client.num = 0; + _client.extraHeaders = WEBSOCKETS_STRING("Origin: file://"); +} + +WebSocketsClient::~WebSocketsClient() { + disconnect(); +} + +/** + * calles to init the Websockets server + */ +void WebSocketsClient::begin(const char *host, uint16_t port, const char * url, const char * protocol) { + _host = host; + _port = port; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + _fingerprint = ""; +#endif + + _client.num = 0; + _client.status = WSC_NOT_CONNECTED; + _client.tcp = NULL; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + _client.isSSL = false; + _client.ssl = NULL; +#endif + _client.cUrl = url; + _client.cCode = 0; + _client.cIsUpgrade = false; + _client.cIsWebsocket = true; + _client.cKey = ""; + _client.cAccept = ""; + _client.cProtocol = protocol; + _client.cExtensions = ""; + _client.cVersion = 0; + _client.base64Authorization = ""; + _client.plainAuthorization = ""; + _client.isSocketIO = false; + +#ifdef ESP8266 + randomSeed(RANDOM_REG32); +#else + // todo find better seed + randomSeed(millis()); +#endif +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + asyncConnect(); +#endif + + _lastConnectionFail = 0; + _reconnectInterval = 500; +} + +void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) { + begin(host.c_str(), port, url.c_str(), protocol.c_str()); +} + +void WebSocketsClient::begin(IPAddress host, uint16_t port, const char * url, const char * protocol) { + return begin(host.toString().c_str(), port, url, protocol); +} + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) +void WebSocketsClient::beginSSL(const char *host, uint16_t port, const char * url, const char * fingerprint, const char * protocol) { + begin(host, port, url, protocol); + _client.isSSL = true; + _fingerprint = fingerprint; +} + +void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String fingerprint, String protocol) { + beginSSL(host.c_str(), port, url.c_str(), fingerprint.c_str(), protocol.c_str()); +} +#endif + +void WebSocketsClient::beginSocketIO(const char *host, uint16_t port, const char * url, const char * protocol) { + begin(host, port, url, protocol); + _client.isSocketIO = true; +} + +void WebSocketsClient::beginSocketIO(String host, uint16_t port, String url, String protocol) { + beginSocketIO(host.c_str(), port, url.c_str(), protocol.c_str()); +} + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) +void WebSocketsClient::beginSocketIOSSL(const char *host, uint16_t port, const char * url, const char * protocol) { + begin(host, port, url, protocol); + _client.isSocketIO = true; + _client.isSSL = true; + _fingerprint = ""; +} + +void WebSocketsClient::beginSocketIOSSL(String host, uint16_t port, String url, String protocol) { + beginSocketIOSSL(host.c_str(), port, url.c_str(), protocol.c_str()); +} +#endif + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) +/** + * called in arduino loop + */ +void WebSocketsClient::loop(void) { + if(!clientIsConnected(&_client)) { + // do not flood the server + if((millis() - _lastConnectionFail) < _reconnectInterval) { + return; + } + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + if(_client.isSSL) { + DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n"); + if(_client.ssl) { + delete _client.ssl; + _client.ssl = NULL; + _client.tcp = NULL; + } + _client.ssl = new WiFiClientSecure(); + _client.tcp = _client.ssl; + } else { + DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n"); + if(_client.tcp) { + delete _client.tcp; + _client.tcp = NULL; + } + _client.tcp = new WiFiClient(); + } +#else + _client.tcp = new WEBSOCKETS_NETWORK_CLASS(); +#endif + + if(!_client.tcp) { + DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); + return; + } + + if(_client.tcp->connect(_host.c_str(), _port)) { + connectedCb(); + _lastConnectionFail = 0; + } else { + connectFailedCb(); + _lastConnectionFail = millis(); + + } + } else { + handleClientData(); + } +} +#endif + +/** + * set callback function + * @param cbEvent WebSocketServerEvent + */ +void WebSocketsClient::onEvent(WebSocketClientEvent cbEvent) { + _cbEvent = cbEvent; +} + +/** + * send text data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsClient::sendTXT(uint8_t * payload, size_t length, bool headerToPayload) { + if(length == 0) { + length = strlen((const char *) payload); + } + if(clientIsConnected(&_client)) { + return sendFrame(&_client, WSop_text, payload, length, true, true, headerToPayload); + } + return false; +} + +bool WebSocketsClient::sendTXT(const uint8_t * payload, size_t length) { + return sendTXT((uint8_t *) payload, length); +} + +bool WebSocketsClient::sendTXT(char * payload, size_t length, bool headerToPayload) { + return sendTXT((uint8_t *) payload, length, headerToPayload); +} + +bool WebSocketsClient::sendTXT(const char * payload, size_t length) { + return sendTXT((uint8_t *) payload, length); +} + +bool WebSocketsClient::sendTXT(String & payload) { + return sendTXT((uint8_t *) payload.c_str(), payload.length()); +} + +/** + * send binary data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsClient::sendBIN(uint8_t * payload, size_t length, bool headerToPayload) { + if(clientIsConnected(&_client)) { + return sendFrame(&_client, WSop_binary, payload, length, true, true, headerToPayload); + } + return false; +} + +bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) { + return sendBIN((uint8_t *) payload, length); +} + +/** + * sends a WS ping to Server + * @param payload uint8_t * + * @param length size_t + * @return true if ping is send out + */ +bool WebSocketsClient::sendPing(uint8_t * payload, size_t length) { + if(clientIsConnected(&_client)) { + return sendFrame(&_client, WSop_ping, payload, length, true); + } + return false; +} + +bool WebSocketsClient::sendPing(String & payload) { + return sendPing((uint8_t *) payload.c_str(), payload.length()); +} + +/** + * disconnect one client + * @param num uint8_t client id + */ +void WebSocketsClient::disconnect(void) { + if(clientIsConnected(&_client)) { + WebSockets::clientDisconnect(&_client, 1000); + } +} + +/** + * set the Authorizatio for the http request + * @param user const char * + * @param password const char * + */ +void WebSocketsClient::setAuthorization(const char * user, const char * password) { + if(user && password) { + String auth = user; + auth += ":"; + auth += password; + _client.base64Authorization = base64_encode((uint8_t *) auth.c_str(), auth.length()); + } +} + +/** + * set the Authorizatio for the http request + * @param auth const char * base64 + */ +void WebSocketsClient::setAuthorization(const char * auth) { + if(auth) { + //_client.base64Authorization = auth; + _client.plainAuthorization = auth; + } +} + +/** + * set extra headers for the http request; + * separate headers by "\r\n" + * @param extraHeaders const char * extraHeaders + */ +void WebSocketsClient::setExtraHeaders(const char * extraHeaders) { + _client.extraHeaders = extraHeaders; +} + +/** + * set the reconnect Interval + * how long to wait after a connection initiate failed + * @param time in ms + */ +void WebSocketsClient::setReconnectInterval(unsigned long time) { + _reconnectInterval = time; +} + +//################################################################################# +//################################################################################# +//################################################################################# + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param opcode WSopcode_t + * @param payload uint8_t * + * @param length size_t + */ +void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) { + WStype_t type = WStype_ERROR; + + UNUSED(client); + + switch(opcode) { + case WSop_text: + type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START; + break; + case WSop_binary: + type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START; + break; + case WSop_continuation: + type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT; + break; + case WSop_close: + case WSop_ping: + case WSop_pong: + default: + break; + } + + runCbEvent(type, payload, length); + +} + +/** + * Disconnect an client + * @param client WSclient_t * ptr to the client struct + */ +void WebSocketsClient::clientDisconnect(WSclient_t * client) { + + bool event = false; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + if(client->isSSL && client->ssl) { + if(client->ssl->connected()) { + client->ssl->flush(); + client->ssl->stop(); + } + event = true; + delete client->ssl; + client->ssl = NULL; + client->tcp = NULL; + } +#endif + + if(client->tcp) { + if(client->tcp->connected()) { +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + client->tcp->flush(); +#endif + client->tcp->stop(); + } + event = true; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->status = WSC_NOT_CONNECTED; +#else + delete client->tcp; +#endif + client->tcp = NULL; + } + + client->cCode = 0; + client->cKey = ""; + client->cAccept = ""; + client->cVersion = 0; + client->cIsUpgrade = false; + client->cIsWebsocket = false; + client->cSessionId = ""; + + client->status = WSC_NOT_CONNECTED; + + DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n"); + if(event) { + runCbEvent(WStype_DISCONNECTED, NULL, 0); + } +} + +/** + * get client state + * @param client WSclient_t * ptr to the client struct + * @return true = conneted + */ +bool WebSocketsClient::clientIsConnected(WSclient_t * client) { + + if(!client->tcp) { + return false; + } + + if(client->tcp->connected()) { + if(client->status != WSC_NOT_CONNECTED) { + return true; + } + } else { + // client lost + if(client->status != WSC_NOT_CONNECTED) { + DEBUG_WEBSOCKETS("[WS-Client] connection lost.\n"); + // do cleanup + clientDisconnect(client); + } + } + + if(client->tcp) { + // do cleanup + clientDisconnect(client); + } + + return false; +} +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) +/** + * Handel incomming data from Client + */ +void WebSocketsClient::handleClientData(void) { + int len = _client.tcp->available(); + if(len > 0) { + switch(_client.status) { + case WSC_HEADER: { + String headerLine = _client.tcp->readStringUntil('\n'); + handleHeader(&_client, &headerLine); + } + break; + case WSC_CONNECTED: + WebSockets::handleWebsocket(&_client); + break; + default: + WebSockets::clientDisconnect(&_client, 1002); + break; + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + delay(0); +#endif +} +#endif + +/** + * send the WebSocket header to Server + * @param client WSclient_t * ptr to the client struct + */ +void WebSocketsClient::sendHeader(WSclient_t * client) { + + static const char * NEW_LINE = "\r\n"; + + DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n"); + + uint8_t randomKey[16] = { 0 }; + + for(uint8_t i = 0; i < sizeof(randomKey); i++) { + randomKey[i] = random(0xFF); + } + + client->cKey = base64_encode(&randomKey[0], 16); + +#ifndef NODEBUG_WEBSOCKETS + unsigned long start = micros(); +#endif + + String handshake; + bool ws_header = true; + String url = client->cUrl; + + if(client->isSocketIO) { + if(client->cSessionId.length() == 0) { + url += WEBSOCKETS_STRING("&transport=polling"); + ws_header = false; + } else { + url += WEBSOCKETS_STRING("&transport=websocket&sid="); + url += client->cSessionId; + } + } + + handshake = WEBSOCKETS_STRING("GET "); + handshake += url + WEBSOCKETS_STRING(" HTTP/1.1\r\n" + "Host: "); + handshake += _host + ":" + _port + NEW_LINE; + + if(ws_header) { + handshake += WEBSOCKETS_STRING("Connection: Upgrade\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Key: "); + handshake += client->cKey + NEW_LINE; + + if(client->cProtocol.length() > 0) { + handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: "); + handshake += client->cProtocol + NEW_LINE; + } + + if(client->cExtensions.length() > 0) { + handshake += WEBSOCKETS_STRING("Sec-WebSocket-Extensions: "); + handshake += client->cExtensions + NEW_LINE; + } + } else { + handshake += WEBSOCKETS_STRING("Connection: keep-alive\r\n"); + } + + // add extra headers; by default this includes "Origin: file://" + if(client->extraHeaders) { + handshake += client->extraHeaders + NEW_LINE; + } + + handshake += WEBSOCKETS_STRING("User-Agent: arduino-WebSocket-Client\r\n"); + + if(client->base64Authorization.length() > 0) { + handshake += WEBSOCKETS_STRING("Authorization: Basic "); + handshake += client->base64Authorization + NEW_LINE; + } + + if(client->plainAuthorization.length() > 0) { + handshake += WEBSOCKETS_STRING("Authorization: "); + handshake += client->plainAuthorization + NEW_LINE; + } + + handshake += NEW_LINE; + + DEBUG_WEBSOCKETS("[WS-Client][sendHeader] handshake %s", (uint8_t* )handshake.c_str()); + write(client, (uint8_t*) handshake.c_str(), handshake.length()); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine))); +#endif + + DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%luus).\n", (micros() - start)); + +} + +/** + * handle the WebSocket header reading + * @param client WSclient_t * ptr to the client struct + */ +void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) { + + headerLine->trim(); // remove \r + + if(headerLine->length() > 0) { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n", headerLine->c_str()); + + if(headerLine->startsWith(WEBSOCKETS_STRING("HTTP/1."))) { + // "HTTP/1.1 101 Switching Protocols" + client->cCode = headerLine->substring(9, headerLine->indexOf(' ', 9)).toInt(); + } else if(headerLine->indexOf(':')) { + String headerName = headerLine->substring(0, headerLine->indexOf(':')); + String headerValue = headerLine->substring(headerLine->indexOf(':') + 1); + + // remove space in the beginning (RFC2616) + if(headerValue[0] == ' ') { + headerValue.remove(0, 1); + } + + if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) { + if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("upgrade"))) { + client->cIsUpgrade = true; + } + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) { + if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) { + client->cIsWebsocket = true; + } + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Accept"))) { + client->cAccept = headerValue; + client->cAccept.trim(); // see rfc6455 + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) { + client->cProtocol = headerValue; + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) { + client->cExtensions = headerValue; + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) { + client->cVersion = headerValue.toInt(); + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Set-Cookie"))) { + if(headerValue.indexOf(WEBSOCKETS_STRING("HttpOnly")) > -1) { + client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1, headerValue.indexOf(";")); + } else { + client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1); + } + } + } else { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str()); + } + + (*headerLine) = ""; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine))); +#endif + + } else { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n"); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Client settings:\n"); + + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cURL: %s\n", client->cUrl.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cKey: %s\n", client->cKey.c_str()); + + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Server header:\n"); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cCode: %d\n", client->cCode); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsUpgrade: %d\n", client->cIsUpgrade); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsWebsocket: %d\n", client->cIsWebsocket); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cAccept: %s\n", client->cAccept.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cProtocol: %s\n", client->cProtocol.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cExtensions: %s\n", client->cExtensions.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n", client->cVersion); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cSessionId: %s\n", client->cSessionId.c_str()); + + bool ok = (client->cIsUpgrade && client->cIsWebsocket); + + if(ok) { + switch(client->cCode) { + case 101: ///< Switching Protocols + + break; + case 200: + if(client->isSocketIO) { + break; + } + case 403: ///< Forbidden + // todo handle login + default: ///< Server dont unterstand requrst + ok = false; + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n", client->cCode); + clientDisconnect(client); + _lastConnectionFail = millis(); + break; + } + } + + if(ok) { + + if(client->cAccept.length() == 0) { + ok = false; + } else { + // generate Sec-WebSocket-Accept key for check + String sKey = acceptKey(client->cKey); + if(sKey != client->cAccept) { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Sec-WebSocket-Accept is wrong\n"); + ok = false; + } + } + } + + if(ok) { + + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection init done.\n"); + headerDone(client); + + runCbEvent(WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length()); + + } else if(clientIsConnected(client) && client->isSocketIO && client->cSessionId.length() > 0) { + sendHeader(client); + } else { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n"); + _lastConnectionFail = millis(); + if(clientIsConnected(client)) { + write(client, "This is a webSocket client!"); + } + clientDisconnect(client); + } + } +} + +void WebSocketsClient::connectedCb() { + + DEBUG_WEBSOCKETS("[WS-Client] connected to %s:%u.\n", _host.c_str(), _port); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + _client.tcp->onDisconnect(std::bind([](WebSocketsClient * c, AsyncTCPbuffer * obj, WSclient_t * client) -> bool { + DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num); + client->status = WSC_NOT_CONNECTED; + client->tcp = NULL; + + // reconnect + c->asyncConnect(); + + return true; + }, this, std::placeholders::_1, &_client)); +#endif + + _client.status = WSC_HEADER; + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + // set Timeout for readBytesUntil and readStringUntil + _client.tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT); +#endif + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + _client.tcp->setNoDelay(true); + + if(_client.isSSL && _fingerprint.length()) { + if(!_client.ssl->verify(_fingerprint.c_str(), _host.c_str())) { + DEBUG_WEBSOCKETS("[WS-Client] certificate mismatch\n"); + WebSockets::clientDisconnect(&_client, 1000); + return; + } + } +#endif + + // send Header to Server + sendHeader(&_client); + +} + +void WebSocketsClient::connectFailedCb() { + DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Faild\n", _host.c_str(), _port); +} + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + +void WebSocketsClient::asyncConnect() { + + DEBUG_WEBSOCKETS("[WS-Client] asyncConnect...\n"); + + AsyncClient * tcpclient = new AsyncClient(); + + if(!tcpclient) { + DEBUG_WEBSOCKETS("[WS-Client] creating AsyncClient class failed!\n"); + return; + } + + tcpclient->onDisconnect([](void *obj, AsyncClient* c) { + c->free(); + delete c; + }); + + tcpclient->onConnect(std::bind([](WebSocketsClient * ws , AsyncClient * tcp) { + ws->_client.tcp = new AsyncTCPbuffer(tcp); + if(!ws->_client.tcp) { + DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!\n"); + ws->connectFailedCb(); + return; + } + ws->connectedCb(); + }, this, std::placeholders::_2)); + + tcpclient->onError(std::bind([](WebSocketsClient * ws , AsyncClient * tcp) { + ws->connectFailedCb(); + + // reconnect + ws->asyncConnect(); + }, this, std::placeholders::_2)); + + if(!tcpclient->connect(_host.c_str(), _port)) { + connectFailedCb(); + delete tcpclient; + } + +} + +#endif diff --git a/libraries/arduinoWebSockets/src/WebSocketsClient.h b/libraries/arduinoWebSockets/src/WebSocketsClient.h new file mode 100644 index 00000000..61b8ea2a --- /dev/null +++ b/libraries/arduinoWebSockets/src/WebSocketsClient.h @@ -0,0 +1,136 @@ +/** + * @file WebSocketsClient.h + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * 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 + * + */ + +#ifndef WEBSOCKETSCLIENT_H_ +#define WEBSOCKETSCLIENT_H_ + +#include "WebSockets.h" + +class WebSocketsClient: private WebSockets { + public: +#ifdef __AVR__ + typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length); +#else + typedef std::function WebSocketClientEvent; +#endif + + + WebSocketsClient(void); + virtual ~WebSocketsClient(void); + + void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino"); + void begin(String host, uint16_t port, String url = "/", String protocol = "arduino"); + void begin(IPAddress host, uint16_t port, const char * url = "/", const char * protocol = "arduino"); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + void beginSSL(const char *host, uint16_t port, const char * url = "/", const char * = "", const char * protocol = "arduino"); + void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "", String protocol = "arduino"); +#endif + + void beginSocketIO(const char *host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino"); + void beginSocketIO(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino"); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + void beginSocketIOSSL(const char *host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino"); + void beginSocketIOSSL(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino"); +#endif + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + void loop(void); +#else + // Async interface not need a loop call + void loop(void) __attribute__ ((deprecated)) {} +#endif + + void onEvent(WebSocketClientEvent cbEvent); + + bool sendTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false); + bool sendTXT(const uint8_t * payload, size_t length = 0); + bool sendTXT(char * payload, size_t length = 0, bool headerToPayload = false); + bool sendTXT(const char * payload, size_t length = 0); + bool sendTXT(String & payload); + + bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false); + bool sendBIN(const uint8_t * payload, size_t length); + + bool sendPing(uint8_t * payload = NULL, size_t length = 0); + bool sendPing(String & payload); + + void disconnect(void); + + void setAuthorization(const char * user, const char * password); + void setAuthorization(const char * auth); + + void setExtraHeaders(const char * extraHeaders = NULL); + + void setReconnectInterval(unsigned long time); + + protected: + String _host; + uint16_t _port; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + String _fingerprint; +#endif + WSclient_t _client; + + WebSocketClientEvent _cbEvent; + + unsigned long _lastConnectionFail; + unsigned long _reconnectInterval; + + void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin); + + void clientDisconnect(WSclient_t * client); + bool clientIsConnected(WSclient_t * client); + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + void handleClientData(void); +#endif + + void sendHeader(WSclient_t * client); + void handleHeader(WSclient_t * client, String * headerLine); + + void connectedCb(); + void connectFailedCb(); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + void asyncConnect(); +#endif + + /** + * called for sending a Event to the app + * @param type WStype_t + * @param payload uint8_t * + * @param length size_t + */ + virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) { + if(_cbEvent) { + _cbEvent(type, payload, length); + } + } + +}; + +#endif /* WEBSOCKETSCLIENT_H_ */ diff --git a/libraries/arduinoWebSockets/src/WebSocketsServer.cpp b/libraries/arduinoWebSockets/src/WebSocketsServer.cpp new file mode 100644 index 00000000..b6f950f4 --- /dev/null +++ b/libraries/arduinoWebSockets/src/WebSocketsServer.cpp @@ -0,0 +1,873 @@ +/** + * @file WebSocketsServer.cpp + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * 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 + * + */ + +#include "WebSockets.h" +#include "WebSocketsServer.h" + +WebSocketsServer::WebSocketsServer(uint16_t port, String origin, String protocol) { + _port = port; + _origin = origin; + _protocol = protocol; + _runnning = false; + + _server = new WEBSOCKETS_NETWORK_SERVER_CLASS(port); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + _server->onClient([](void *s, AsyncClient* c){ + ((WebSocketsServer*)s)->newClient(new AsyncTCPbuffer(c)); + }, this); +#endif + + _cbEvent = NULL; + + _httpHeaderValidationFunc = NULL; + _mandatoryHttpHeaders = NULL; + _mandatoryHttpHeaderCount = 0; + + memset(&_clients[0], 0x00, (sizeof(WSclient_t) * WEBSOCKETS_SERVER_CLIENT_MAX)); +} + + +WebSocketsServer::~WebSocketsServer() { + // disconnect all clients + close(); + + if (_mandatoryHttpHeaders) + delete[] _mandatoryHttpHeaders; + + _mandatoryHttpHeaderCount = 0; +} + +/** + * called to initialize the Websocket server + */ +void WebSocketsServer::begin(void) { + WSclient_t * client; + + // init client storage + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + + client->num = i; + client->status = WSC_NOT_CONNECTED; + client->tcp = NULL; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + client->isSSL = false; + client->ssl = NULL; +#endif + client->cUrl = ""; + client->cCode = 0; + client->cKey = ""; + client->cProtocol = ""; + client->cVersion = 0; + client->cIsUpgrade = false; + client->cIsWebsocket = false; + + client->base64Authorization = ""; + + client->cWsRXsize = 0; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->cHttpLine = ""; +#endif + } + +#ifdef ESP8266 + randomSeed(RANDOM_REG32); +#elif defined(ESP32) + #define DR_REG_RNG_BASE 0x3ff75144 + randomSeed(READ_PERI_REG(DR_REG_RNG_BASE)); +#else + // TODO find better seed + randomSeed(millis()); +#endif + + _runnning = true; + _server->begin(); + + DEBUG_WEBSOCKETS("[WS-Server] Server Started.\n"); +} + +void WebSocketsServer::close(void) { + _runnning = false; + disconnect(); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + _server->close(); +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + _server->end(); +#else + // TODO how to close server? +#endif + +} + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) +/** + * called in arduino loop + */ +void WebSocketsServer::loop(void) { + if(_runnning) { + handleNewClients(); + handleClientData(); + } +} +#endif + +/** + * set callback function + * @param cbEvent WebSocketServerEvent + */ +void WebSocketsServer::onEvent(WebSocketServerEvent cbEvent) { + _cbEvent = cbEvent; +} + +/* + * Sets the custom http header validator function + * @param httpHeaderValidationFunc WebSocketServerHttpHeaderValFunc ///< pointer to the custom http header validation function + * @param mandatoryHttpHeaders[] const char* ///< the array of named http headers considered to be mandatory / must be present in order for websocket upgrade to succeed + * @param mandatoryHttpHeaderCount size_t ///< the number of items in the mandatoryHttpHeaders array + */ +void WebSocketsServer::onValidateHttpHeader( + WebSocketServerHttpHeaderValFunc validationFunc, + const char* mandatoryHttpHeaders[], + size_t mandatoryHttpHeaderCount) +{ + _httpHeaderValidationFunc = validationFunc; + + if (_mandatoryHttpHeaders) + delete[] _mandatoryHttpHeaders; + + _mandatoryHttpHeaderCount = mandatoryHttpHeaderCount; + _mandatoryHttpHeaders = new String[_mandatoryHttpHeaderCount]; + + for (size_t i = 0; i < _mandatoryHttpHeaderCount; i++) { + _mandatoryHttpHeaders[i] = mandatoryHttpHeaders[i]; + } +} + +/* + * send text data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsServer::sendTXT(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return false; + } + if(length == 0) { + length = strlen((const char *) payload); + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + return sendFrame(client, WSop_text, payload, length, false, true, headerToPayload); + } + return false; +} + +bool WebSocketsServer::sendTXT(uint8_t num, const uint8_t * payload, size_t length) { + return sendTXT(num, (uint8_t *) payload, length); +} + +bool WebSocketsServer::sendTXT(uint8_t num, char * payload, size_t length, bool headerToPayload) { + return sendTXT(num, (uint8_t *) payload, length, headerToPayload); +} + +bool WebSocketsServer::sendTXT(uint8_t num, const char * payload, size_t length) { + return sendTXT(num, (uint8_t *) payload, length); +} + +bool WebSocketsServer::sendTXT(uint8_t num, String & payload) { + return sendTXT(num, (uint8_t *) payload.c_str(), payload.length()); +} + +/** + * send text data to client all + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsServer::broadcastTXT(uint8_t * payload, size_t length, bool headerToPayload) { + WSclient_t * client; + bool ret = true; + if(length == 0) { + length = strlen((const char *) payload); + } + + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + if(!sendFrame(client, WSop_text, payload, length, false, true, headerToPayload)) { + ret = false; + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + return ret; +} + +bool WebSocketsServer::broadcastTXT(const uint8_t * payload, size_t length) { + return broadcastTXT((uint8_t *) payload, length); +} + +bool WebSocketsServer::broadcastTXT(char * payload, size_t length, bool headerToPayload) { + return broadcastTXT((uint8_t *) payload, length, headerToPayload); +} + +bool WebSocketsServer::broadcastTXT(const char * payload, size_t length) { + return broadcastTXT((uint8_t *) payload, length); +} + +bool WebSocketsServer::broadcastTXT(String & payload) { + return broadcastTXT((uint8_t *) payload.c_str(), payload.length()); +} + +/** + * send binary data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsServer::sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return false; + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + return sendFrame(client, WSop_binary, payload, length, false, true, headerToPayload); + } + return false; +} + +bool WebSocketsServer::sendBIN(uint8_t num, const uint8_t * payload, size_t length) { + return sendBIN(num, (uint8_t *) payload, length); +} + +/** + * send binary data to client all + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsServer::broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload) { + WSclient_t * client; + bool ret = true; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + if(!sendFrame(client, WSop_binary, payload, length, false, true, headerToPayload)) { + ret = false; + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + return ret; +} + +bool WebSocketsServer::broadcastBIN(const uint8_t * payload, size_t length) { + return broadcastBIN((uint8_t *) payload, length); +} + + +/** + * sends a WS ping to Client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @return true if ping is send out + */ +bool WebSocketsServer::sendPing(uint8_t num, uint8_t * payload, size_t length) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return false; + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + return sendFrame(client, WSop_ping, payload, length); + } + return false; +} + +bool WebSocketsServer::sendPing(uint8_t num, String & payload) { + return sendPing(num, (uint8_t *) payload.c_str(), payload.length()); +} + +/** + * sends a WS ping to all Client + * @param payload uint8_t * + * @param length size_t + * @return true if ping is send out + */ +bool WebSocketsServer::broadcastPing(uint8_t * payload, size_t length) { + WSclient_t * client; + bool ret = true; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + if(!sendFrame(client, WSop_ping, payload, length)) { + ret = false; + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + return ret; +} + +bool WebSocketsServer::broadcastPing(String & payload) { + return broadcastPing((uint8_t *) payload.c_str(), payload.length()); +} + + +/** + * disconnect all clients + */ +void WebSocketsServer::disconnect(void) { + WSclient_t * client; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + WebSockets::clientDisconnect(client, 1000); + } + } +} + +/** + * disconnect one client + * @param num uint8_t client id + */ +void WebSocketsServer::disconnect(uint8_t num) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return; + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + WebSockets::clientDisconnect(client, 1000); + } +} + + +/* + * set the Authorization for the http request + * @param user const char * + * @param password const char * + */ +void WebSocketsServer::setAuthorization(const char * user, const char * password) { + if(user && password) { + String auth = user; + auth += ":"; + auth += password; + _base64Authorization = base64_encode((uint8_t *)auth.c_str(), auth.length()); + } +} + +/** + * set the Authorizatio for the http request + * @param auth const char * base64 + */ +void WebSocketsServer::setAuthorization(const char * auth) { + if(auth) { + _base64Authorization = auth; + } +} + +/** + * count the connected clients (optional ping them) + * @param ping bool ping the connected clients + */ +int WebSocketsServer::connectedClients(bool ping) { + WSclient_t * client; + int count = 0; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(client->status == WSC_CONNECTED) { + if(ping != true || sendPing(i)) { + count++; + } + } + } + return count; +} + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) +/** + * get an IP for a client + * @param num uint8_t client id + * @return IPAddress + */ +IPAddress WebSocketsServer::remoteIP(uint8_t num) { + if(num < WEBSOCKETS_SERVER_CLIENT_MAX) { + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + return client->tcp->remoteIP(); + } + } + + return IPAddress(); +} +#endif + +//################################################################################# +//################################################################################# +//################################################################################# + +/** + * handle new client connection + * @param client + */ +bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) { + WSclient_t * client; + // search free list entry for client + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + + // state is not connected or tcp connection is lost + if(!clientIsConnected(client)) { + + client->tcp = TCPclient; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + client->isSSL = false; + client->tcp->setNoDelay(true); +#endif +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + // set Timeout for readBytesUntil and readStringUntil + client->tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT); +#endif + client->status = WSC_HEADER; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + IPAddress ip = client->tcp->remoteIP(); + DEBUG_WEBSOCKETS("[WS-Server][%d] new client from %d.%d.%d.%d\n", client->num, ip[0], ip[1], ip[2], ip[3]); +#else + DEBUG_WEBSOCKETS("[WS-Server][%d] new client\n", client->num); +#endif + + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->onDisconnect(std::bind([](WebSocketsServer * server, AsyncTCPbuffer * obj, WSclient_t * client) -> bool { + DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num); + + AsyncTCPbuffer ** sl = &server->_clients[client->num].tcp; + if(*sl == obj) { + client->status = WSC_NOT_CONNECTED; + *sl = NULL; + } + return true; + }, this, std::placeholders::_1, client)); + + + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine))); +#endif + + return true; + break; + } + } + return false; +} + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param opcode WSopcode_t + * @param payload uint8_t * + * @param length size_t + */ +void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) { + WStype_t type = WStype_ERROR; + + switch(opcode) { + case WSop_text: + type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START; + break; + case WSop_binary: + type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START; + break; + case WSop_continuation: + type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT; + break; + case WSop_close: + case WSop_ping: + case WSop_pong: + default: + break; + } + + runCbEvent(client->num, type, payload, length); + +} + +/** + * Disconnect an client + * @param client WSclient_t * ptr to the client struct + */ +void WebSocketsServer::clientDisconnect(WSclient_t * client) { + + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + if(client->isSSL && client->ssl) { + if(client->ssl->connected()) { + client->ssl->flush(); + client->ssl->stop(); + } + delete client->ssl; + client->ssl = NULL; + client->tcp = NULL; + } +#endif + + if(client->tcp) { + if(client->tcp->connected()) { +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + client->tcp->flush(); +#endif + client->tcp->stop(); + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->status = WSC_NOT_CONNECTED; +#else + delete client->tcp; +#endif + client->tcp = NULL; + } + + client->cUrl = ""; + client->cKey = ""; + client->cProtocol = ""; + client->cVersion = 0; + client->cIsUpgrade = false; + client->cIsWebsocket = false; + + client->cWsRXsize = 0; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->cHttpLine = ""; +#endif + + client->status = WSC_NOT_CONNECTED; + + DEBUG_WEBSOCKETS("[WS-Server][%d] client disconnected.\n", client->num); + + runCbEvent(client->num, WStype_DISCONNECTED, NULL, 0); + +} + +/** + * get client state + * @param client WSclient_t * ptr to the client struct + * @return true = connected + */ +bool WebSocketsServer::clientIsConnected(WSclient_t * client) { + + if(!client->tcp) { + return false; + } + + if(client->tcp->connected()) { + if(client->status != WSC_NOT_CONNECTED) { + return true; + } + } else { + // client lost + if(client->status != WSC_NOT_CONNECTED) { + DEBUG_WEBSOCKETS("[WS-Server][%d] client connection lost.\n", client->num); + // do cleanup + clientDisconnect(client); + } + } + + if(client->tcp) { + // do cleanup + DEBUG_WEBSOCKETS("[WS-Server][%d] client list cleanup.\n", client->num); + clientDisconnect(client); + } + + return false; +} +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) +/** + * Handle incoming Connection Request + */ +void WebSocketsServer::handleNewClients(void) { + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + while(_server->hasClient()) { +#endif + bool ok = false; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + // store new connection + WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available()); +#else + WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available()); +#endif + + if(!tcpClient) { + DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); + return; + } + + ok = newClient(tcpClient); + + if(!ok) { + // no free space to handle client +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + IPAddress ip = tcpClient->remoteIP(); + DEBUG_WEBSOCKETS("[WS-Server] no free space new client from %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); +#else + DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n"); +#endif + tcpClient->stop(); + } + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + delay(0); + } +#endif + +} + + +/** + * Handel incomming data from Client + */ +void WebSocketsServer::handleClientData(void) { + + WSclient_t * client; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + int len = client->tcp->available(); + if(len > 0) { + //DEBUG_WEBSOCKETS("[WS-Server][%d][handleClientData] len: %d\n", client->num, len); + switch(client->status) { + case WSC_HEADER: + { + String headerLine = client->tcp->readStringUntil('\n'); + handleHeader(client, &headerLine); + } + break; + case WSC_CONNECTED: + WebSockets::handleWebsocket(client); + break; + default: + WebSockets::clientDisconnect(client, 1002); + break; + } + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } +} +#endif + +/* + * returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection + * @param headerName String ///< the name of the header being checked + */ +bool WebSocketsServer::hasMandatoryHeader(String headerName) { + for (size_t i = 0; i < _mandatoryHttpHeaderCount; i++) { + if (_mandatoryHttpHeaders[i].equalsIgnoreCase(headerName)) + return true; + } + return false; +} + + +/** + * handles http header reading for WebSocket upgrade + * @param client WSclient_t * ///< pointer to the client struct + * @param headerLine String ///< the header being read / processed + */ +void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) { + + static const char * NEW_LINE = "\r\n"; + + headerLine->trim(); // remove \r + + if(headerLine->length() > 0) { + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] RX: %s\n", client->num, headerLine->c_str()); + + // websocket requests always start with GET see rfc6455 + if(headerLine->startsWith("GET ")) { + + // cut URL out + client->cUrl = headerLine->substring(4, headerLine->indexOf(' ', 4)); + + //reset non-websocket http header validation state for this client + client->cHttpHeadersValid = true; + client->cMandatoryHeadersCount = 0; + + } else if(headerLine->indexOf(':')) { + String headerName = headerLine->substring(0, headerLine->indexOf(':')); + String headerValue = headerLine->substring(headerLine->indexOf(':') + 1); + + // remove space in the beginning (RFC2616) + if(headerValue[0] == ' ') { + headerValue.remove(0, 1); + } + + if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) { + headerValue.toLowerCase(); + if(headerValue.indexOf(WEBSOCKETS_STRING("upgrade")) >= 0) { + client->cIsUpgrade = true; + } + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) { + if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) { + client->cIsWebsocket = true; + } + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) { + client->cVersion = headerValue.toInt(); + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Key"))) { + client->cKey = headerValue; + client->cKey.trim(); // see rfc6455 + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) { + client->cProtocol = headerValue; + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) { + client->cExtensions = headerValue; + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Authorization"))) { + client->base64Authorization = headerValue; + } else { + client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue); + if(_mandatoryHttpHeaderCount > 0 && hasMandatoryHeader(headerName)) { + client->cMandatoryHeadersCount++; + } + } + + } else { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str()); + } + + (*headerLine) = ""; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine))); +#endif + } else { + + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Header read fin.\n", client->num); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cURL: %s\n", client->num, client->cUrl.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsUpgrade: %d\n", client->num, client->cIsUpgrade); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsWebsocket: %d\n", client->num, client->cIsWebsocket); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cKey: %s\n", client->num, client->cKey.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cProtocol: %s\n", client->num, client->cProtocol.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cExtensions: %s\n", client->num, client->cExtensions.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cVersion: %d\n", client->num, client->cVersion); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - base64Authorization: %s\n", client->num, client->base64Authorization.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cHttpHeadersValid: %d\n", client->num, client->cHttpHeadersValid); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cMandatoryHeadersCount: %d\n", client->num, client->cMandatoryHeadersCount); + + bool ok = (client->cIsUpgrade && client->cIsWebsocket); + + if(ok) { + if(client->cUrl.length() == 0) { + ok = false; + } + if(client->cKey.length() == 0) { + ok = false; + } + if(client->cVersion != 13) { + ok = false; + } + if(!client->cHttpHeadersValid) { + ok = false; + } + if (client->cMandatoryHeadersCount != _mandatoryHttpHeaderCount) { + ok = false; + } + } + + if(_base64Authorization.length() > 0) { + String auth = WEBSOCKETS_STRING("Basic "); + auth += _base64Authorization; + if(auth != client->base64Authorization) { + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] HTTP Authorization failed!\n", client->num); + handleAuthorizationFailed(client); + return; + } + } + + if(ok) { + + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incoming.\n", client->num); + + // generate Sec-WebSocket-Accept key + String sKey = acceptKey(client->cKey); + + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - sKey: %s\n", client->num, sKey.c_str()); + + client->status = WSC_CONNECTED; + + String handshake = WEBSOCKETS_STRING("HTTP/1.1 101 Switching Protocols\r\n" + "Server: arduino-WebSocketsServer\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Accept: "); + handshake += sKey + NEW_LINE; + + if(_origin.length() > 0) { + handshake += WEBSOCKETS_STRING("Access-Control-Allow-Origin: "); + handshake +=_origin + NEW_LINE; + } + + if(client->cProtocol.length() > 0) { + handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: "); + handshake +=_protocol + NEW_LINE; + } + + // header end + handshake += NEW_LINE; + + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] handshake %s", client->num, (uint8_t*)handshake.c_str()); + + write(client, (uint8_t*)handshake.c_str(), handshake.length()); + + headerDone(client); + + // send ping + WebSockets::sendFrame(client, WSop_ping); + + runCbEvent(client->num, WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length()); + + } else { + handleNonWebsocketConnection(client); + } + } +} + + + diff --git a/libraries/arduinoWebSockets/src/WebSocketsServer.h b/libraries/arduinoWebSockets/src/WebSocketsServer.h new file mode 100644 index 00000000..db945a6f --- /dev/null +++ b/libraries/arduinoWebSockets/src/WebSocketsServer.h @@ -0,0 +1,212 @@ +/** + * @file WebSocketsServer.h + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * 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 + * + */ + +#ifndef WEBSOCKETSSERVER_H_ +#define WEBSOCKETSSERVER_H_ + +#include "WebSockets.h" + +#ifndef WEBSOCKETS_SERVER_CLIENT_MAX +#define WEBSOCKETS_SERVER_CLIENT_MAX (5) +#endif + + + + +class WebSocketsServer: protected WebSockets { +public: + +#ifdef __AVR__ + typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length); + typedef bool (*WebSocketServerHttpHeaderValFunc)(String headerName, String headerValue); +#else + typedef std::function WebSocketServerEvent; + typedef std::function WebSocketServerHttpHeaderValFunc; +#endif + + WebSocketsServer(uint16_t port, String origin = "", String protocol = "arduino"); + virtual ~WebSocketsServer(void); + + void begin(void); + void close(void); + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + void loop(void); +#else + // Async interface not need a loop call + void loop(void) __attribute__ ((deprecated)) {} +#endif + + void onEvent(WebSocketServerEvent cbEvent); + void onValidateHttpHeader( + WebSocketServerHttpHeaderValFunc validationFunc, + const char* mandatoryHttpHeaders[], + size_t mandatoryHttpHeaderCount); + + + bool sendTXT(uint8_t num, uint8_t * payload, size_t length = 0, bool headerToPayload = false); + bool sendTXT(uint8_t num, const uint8_t * payload, size_t length = 0); + bool sendTXT(uint8_t num, char * payload, size_t length = 0, bool headerToPayload = false); + bool sendTXT(uint8_t num, const char * payload, size_t length = 0); + bool sendTXT(uint8_t num, String & payload); + + bool broadcastTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false); + bool broadcastTXT(const uint8_t * payload, size_t length = 0); + bool broadcastTXT(char * payload, size_t length = 0, bool headerToPayload = false); + bool broadcastTXT(const char * payload, size_t length = 0); + bool broadcastTXT(String & payload); + + bool sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload = false); + bool sendBIN(uint8_t num, const uint8_t * payload, size_t length); + + bool broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload = false); + bool broadcastBIN(const uint8_t * payload, size_t length); + + bool sendPing(uint8_t num, uint8_t * payload = NULL, size_t length = 0); + bool sendPing(uint8_t num, String & payload); + + bool broadcastPing(uint8_t * payload = NULL, size_t length = 0); + bool broadcastPing(String & payload); + + void disconnect(void); + void disconnect(uint8_t num); + + void setAuthorization(const char * user, const char * password); + void setAuthorization(const char * auth); + + int connectedClients(bool ping = false); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + IPAddress remoteIP(uint8_t num); +#endif + +protected: + uint16_t _port; + String _origin; + String _protocol; + String _base64Authorization; ///< Base64 encoded Auth request + String * _mandatoryHttpHeaders; + size_t _mandatoryHttpHeaderCount; + + WEBSOCKETS_NETWORK_SERVER_CLASS * _server; + + WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX]; + + WebSocketServerEvent _cbEvent; + WebSocketServerHttpHeaderValFunc _httpHeaderValidationFunc; + + bool _runnning; + + bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient); + + void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin); + + void clientDisconnect(WSclient_t * client); + bool clientIsConnected(WSclient_t * client); + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + void handleNewClients(void); + void handleClientData(void); +#endif + + void handleHeader(WSclient_t * client, String * headerLine); + + /** + * called if a non Websocket connection is coming in. + * Note: can be override + * @param client WSclient_t * ptr to the client struct + */ + virtual void handleNonWebsocketConnection(WSclient_t * client) { + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num); + client->tcp->write("HTTP/1.1 400 Bad Request\r\n" + "Server: arduino-WebSocket-Server\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 32\r\n" + "Connection: close\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n" + "This is a Websocket server only!"); + clientDisconnect(client); + } + + /** + * called if a non Authorization connection is coming in. + * Note: can be override + * @param client WSclient_t * ptr to the client struct + */ + virtual void handleAuthorizationFailed(WSclient_t *client) { + client->tcp->write("HTTP/1.1 401 Unauthorized\r\n" + "Server: arduino-WebSocket-Server\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 45\r\n" + "Connection: close\r\n" + "Sec-WebSocket-Version: 13\r\n" + "WWW-Authenticate: Basic realm=\"WebSocket Server\"" + "\r\n" + "This Websocket server requires Authorization!"); + clientDisconnect(client); + } + + /** + * called for sending a Event to the app + * @param num uint8_t + * @param type WStype_t + * @param payload uint8_t * + * @param length size_t + */ + virtual void runCbEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + if(_cbEvent) { + _cbEvent(num, type, payload, length); + } + } + + /* + * Called at client socket connect handshake negotiation time for each http header that is not + * a websocket specific http header (not Connection, Upgrade, Sec-WebSocket-*) + * If the custom httpHeaderValidationFunc returns false for any headerName / headerValue passed, the + * socket negotiation is considered invalid and the upgrade to websockets request is denied / rejected + * This mechanism can be used to enable custom authentication schemes e.g. test the value + * of a session cookie to determine if a user is logged on / authenticated + */ + virtual bool execHttpHeaderValidation(String headerName, String headerValue) { + if(_httpHeaderValidationFunc) { + //return the value of the custom http header validation function + return _httpHeaderValidationFunc(headerName, headerValue); + } + //no custom http header validation so just assume all is good + return true; + } + +private: + /* + * returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection + * @param headerName String ///< the name of the header being checked + */ + bool hasMandatoryHeader(String headerName); + +}; + + + +#endif /* WEBSOCKETSSERVER_H_ */ diff --git a/libraries/arduinoWebSockets/src/libb64/AUTHORS b/libraries/arduinoWebSockets/src/libb64/AUTHORS new file mode 100644 index 00000000..af687375 --- /dev/null +++ b/libraries/arduinoWebSockets/src/libb64/AUTHORS @@ -0,0 +1,7 @@ +libb64: Base64 Encoding/Decoding Routines +====================================== + +Authors: +------- + +Chris Venter chris.venter@gmail.com http://rocketpod.blogspot.com diff --git a/libraries/arduinoWebSockets/src/libb64/LICENSE b/libraries/arduinoWebSockets/src/libb64/LICENSE new file mode 100644 index 00000000..a6b56069 --- /dev/null +++ b/libraries/arduinoWebSockets/src/libb64/LICENSE @@ -0,0 +1,29 @@ +Copyright-Only Dedication (based on United States law) +or Public Domain Certification + +The person or persons who have associated work with this document (the +"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of +his knowledge, the work of authorship identified is in the public domain of the +country from which the work is published, or (b) hereby dedicates whatever +copyright the dedicators holds in the work of authorship identified below (the +"Work") to the public domain. A certifier, moreover, dedicates any copyright +interest he may have in the associated work, and for these purposes, is +described as a "dedicator" below. + +A certifier has taken reasonable steps to verify the copyright status of this +work. Certifier recognizes that his good faith efforts may not shield him from +liability if in fact the work certified is not in the public domain. + +Dedicator makes this dedication for the benefit of the public at large and to +the detriment of the Dedicator's heirs and successors. Dedicator intends this +dedication to be an overt act of relinquishment in perpetuity of all present +and future rights under copyright law, whether vested or contingent, in the +Work. Dedicator understands that such relinquishment of all rights includes +the relinquishment of all rights to enforce (by lawsuit or otherwise) those +copyrights in the Work. + +Dedicator recognizes that, once placed in the public domain, the Work may be +freely reproduced, distributed, transmitted, used, modified, built upon, or +otherwise exploited by anyone for any purpose, commercial or non-commercial, +and in any way, including by methods that have not yet been invented or +conceived. \ No newline at end of file diff --git a/libraries/arduinoWebSockets/src/libb64/cdecode.c b/libraries/arduinoWebSockets/src/libb64/cdecode.c new file mode 100644 index 00000000..e135da24 --- /dev/null +++ b/libraries/arduinoWebSockets/src/libb64/cdecode.c @@ -0,0 +1,98 @@ +/* +cdecoder.c - c source to a base64 decoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifdef ESP8266 +#include +#endif + +#if defined(ESP32) +#define CORE_HAS_LIBB64 +#endif + +#ifndef CORE_HAS_LIBB64 +#include "cdecode_inc.h" + +int base64_decode_value(char value_in) +{ + static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; + static const char decoding_size = sizeof(decoding); + value_in -= 43; + if (value_in < 0 || value_in > decoding_size) return -1; + return decoding[(int)value_in]; +} + +void base64_init_decodestate(base64_decodestate* state_in) +{ + state_in->step = step_a; + state_in->plainchar = 0; +} + +int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in) +{ + const char* codechar = code_in; + char* plainchar = plaintext_out; + char fragment; + + *plainchar = state_in->plainchar; + + switch (state_in->step) + { + while (1) + { + case step_a: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_a; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar = (fragment & 0x03f) << 2; + case step_b: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_b; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x030) >> 4; + *plainchar = (fragment & 0x00f) << 4; + case step_c: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_c; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03c) >> 2; + *plainchar = (fragment & 0x003) << 6; + case step_d: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_d; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03f); + } + } + /* control should not reach here */ + return plainchar - plaintext_out; +} + +#endif diff --git a/libraries/arduinoWebSockets/src/libb64/cdecode_inc.h b/libraries/arduinoWebSockets/src/libb64/cdecode_inc.h new file mode 100644 index 00000000..d0d7f489 --- /dev/null +++ b/libraries/arduinoWebSockets/src/libb64/cdecode_inc.h @@ -0,0 +1,28 @@ +/* +cdecode.h - c header for a base64 decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CDECODE_H +#define BASE64_CDECODE_H + +typedef enum +{ + step_a, step_b, step_c, step_d +} base64_decodestep; + +typedef struct +{ + base64_decodestep step; + char plainchar; +} base64_decodestate; + +void base64_init_decodestate(base64_decodestate* state_in); + +int base64_decode_value(char value_in); + +int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in); + +#endif /* BASE64_CDECODE_H */ diff --git a/libraries/arduinoWebSockets/src/libb64/cencode.c b/libraries/arduinoWebSockets/src/libb64/cencode.c new file mode 100644 index 00000000..afe1463c --- /dev/null +++ b/libraries/arduinoWebSockets/src/libb64/cencode.c @@ -0,0 +1,119 @@ +/* +cencoder.c - c source to a base64 encoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifdef ESP8266 +#include +#endif + +#if defined(ESP32) +#define CORE_HAS_LIBB64 +#endif + +#ifndef CORE_HAS_LIBB64 +#include "cencode_inc.h" + +const int CHARS_PER_LINE = 72; + +void base64_init_encodestate(base64_encodestate* state_in) +{ + state_in->step = step_A; + state_in->result = 0; + state_in->stepcount = 0; +} + +char base64_encode_value(char value_in) +{ + static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (value_in > 63) return '='; + return encoding[(int)value_in]; +} + +int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) +{ + const char* plainchar = plaintext_in; + const char* const plaintextend = plaintext_in + length_in; + char* codechar = code_out; + char result; + char fragment; + + result = state_in->result; + + switch (state_in->step) + { + while (1) + { + case step_A: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_A; + return codechar - code_out; + } + fragment = *plainchar++; + result = (fragment & 0x0fc) >> 2; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x003) << 4; + case step_B: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_B; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0f0) >> 4; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x00f) << 2; + case step_C: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_C; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0c0) >> 6; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x03f) >> 0; + *codechar++ = base64_encode_value(result); + + ++(state_in->stepcount); + if (state_in->stepcount == CHARS_PER_LINE/4) + { + *codechar++ = '\n'; + state_in->stepcount = 0; + } + } + } + /* control should not reach here */ + return codechar - code_out; +} + +int base64_encode_blockend(char* code_out, base64_encodestate* state_in) +{ + char* codechar = code_out; + + switch (state_in->step) + { + case step_B: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + *codechar++ = '='; + break; + case step_C: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + break; + case step_A: + break; + } + *codechar++ = 0x00; + + return codechar - code_out; +} + +#endif diff --git a/libraries/arduinoWebSockets/src/libb64/cencode_inc.h b/libraries/arduinoWebSockets/src/libb64/cencode_inc.h new file mode 100644 index 00000000..c1e3464a --- /dev/null +++ b/libraries/arduinoWebSockets/src/libb64/cencode_inc.h @@ -0,0 +1,31 @@ +/* +cencode.h - c header for a base64 encoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CENCODE_H +#define BASE64_CENCODE_H + +typedef enum +{ + step_A, step_B, step_C +} base64_encodestep; + +typedef struct +{ + base64_encodestep step; + char result; + int stepcount; +} base64_encodestate; + +void base64_init_encodestate(base64_encodestate* state_in); + +char base64_encode_value(char value_in); + +int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in); + +int base64_encode_blockend(char* code_out, base64_encodestate* state_in); + +#endif /* BASE64_CENCODE_H */ diff --git a/libraries/arduinoWebSockets/src/libsha1/libsha1.c b/libraries/arduinoWebSockets/src/libsha1/libsha1.c new file mode 100644 index 00000000..48f4df5a --- /dev/null +++ b/libraries/arduinoWebSockets/src/libsha1/libsha1.c @@ -0,0 +1,202 @@ +/* from valgrind tests */ + +/* ================ sha1.c ================ */ +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#if !defined(ESP8266) && !defined(ESP32) + +#define SHA1HANDSOFF + +#include +#include +#include + +#include "libsha1.h" + + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#elif BYTE_ORDER == BIG_ENDIAN +#define blk0(i) block->l[i] +#else +#error "Endianness not defined!" +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) +{ + uint32_t a, b, c, d, e; + typedef union { + unsigned char c[64]; + uint32_t l[16]; + } CHAR64LONG16; +#ifdef SHA1HANDSOFF + CHAR64LONG16 block[1]; /* use array to appear as a pointer */ + memcpy(block, buffer, 64); +#else + /* The following had better never be used because it causes the + * pointer-to-const buffer to be cast into a pointer to non-const. + * And the result is written through. I threw a "const" in, hoping + * this will cause a diagnostic. + */ + CHAR64LONG16* block = (const CHAR64LONG16*)buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +#ifdef SHA1HANDSOFF + memset(block, '\0', sizeof(block)); +#endif +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len) +{ + uint32_t i, j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1]++; + context->count[1] += (len>>29); + j = (j >> 3) & 63; + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +{ + unsigned i; + unsigned char finalcount[8]; + unsigned char c; + +#if 0 /* untested "improvement" by DHR */ + /* Convert context->count to a sequence of bytes + * in finalcount. Second element first, but + * big-endian order within element. + * But we do it all backwards. + */ + unsigned char *fcp = &finalcount[8]; + + for (i = 0; i < 2; i++) + { + uint32_t t = context->count[i]; + int j; + + for (j = 0; j < 4; t >>= 8, j++) + *--fcp = (unsigned char) t; + } +#else + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } +#endif + c = 0200; + SHA1Update(context, &c, 1); + while ((context->count[0] & 504) != 448) { + c = 0000; + SHA1Update(context, &c, 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + memset(context, '\0', sizeof(*context)); + memset(&finalcount, '\0', sizeof(finalcount)); +} +/* ================ end of sha1.c ================ */ + + +#endif diff --git a/libraries/arduinoWebSockets/src/libsha1/libsha1.h b/libraries/arduinoWebSockets/src/libsha1/libsha1.h new file mode 100644 index 00000000..ee3718e1 --- /dev/null +++ b/libraries/arduinoWebSockets/src/libsha1/libsha1.h @@ -0,0 +1,21 @@ +/* ================ sha1.h ================ */ +/* +SHA-1 in C +By Steve Reid +100% Public Domain +*/ + +#if !defined(ESP8266) && !defined(ESP32) + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]); +void SHA1Init(SHA1_CTX* context); +void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len); +void SHA1Final(unsigned char digest[20], SHA1_CTX* context); + +#endif diff --git a/libraries/arduinoWebSockets/tests/webSocket.html b/libraries/arduinoWebSockets/tests/webSocket.html new file mode 100644 index 00000000..66a27089 --- /dev/null +++ b/libraries/arduinoWebSockets/tests/webSocket.html @@ -0,0 +1,49 @@ + + + + + + + +LED Control:
+
+R:
+G:
+B:
+ + \ No newline at end of file diff --git a/libraries/arduinoWebSockets/tests/webSocketServer/index.js b/libraries/arduinoWebSockets/tests/webSocketServer/index.js new file mode 100644 index 00000000..389e1930 --- /dev/null +++ b/libraries/arduinoWebSockets/tests/webSocketServer/index.js @@ -0,0 +1,57 @@ +#!/usr/bin/env node +var WebSocketServer = require('websocket').server; +var http = require('http'); + +var server = http.createServer(function(request, response) { + console.log((new Date()) + ' Received request for ' + request.url); + response.writeHead(404); + response.end(); +}); +server.listen(8011, function() { + console.log((new Date()) + ' Server is listening on port 8011'); +}); + +wsServer = new WebSocketServer({ + httpServer: server, + // You should not use autoAcceptConnections for production + // applications, as it defeats all standard cross-origin protection + // facilities built into the protocol and the browser. You should + // *always* verify the connection's origin and decide whether or not + // to accept it. + autoAcceptConnections: false +}); + +function originIsAllowed(origin) { + // put logic here to detect whether the specified origin is allowed. + return true; +} + +wsServer.on('request', function(request) { + + if (!originIsAllowed(request.origin)) { + // Make sure we only accept requests from an allowed origin + request.reject(); + console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.'); + return; + } + + var connection = request.accept('arduino', request.origin); + console.log((new Date()) + ' Connection accepted.'); + + connection.on('message', function(message) { + if (message.type === 'utf8') { + console.log('Received Message: ' + message.utf8Data); + // connection.sendUTF(message.utf8Data); + } + else if (message.type === 'binary') { + console.log('Received Binary Message of ' + message.binaryData.length + ' bytes'); + //connection.sendBytes(message.binaryData); + } + }); + + connection.on('close', function(reasonCode, description) { + console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.'); + }); + + connection.sendUTF("Hallo Client!"); +}); diff --git a/libraries/arduinoWebSockets/tests/webSocketServer/package.json b/libraries/arduinoWebSockets/tests/webSocketServer/package.json new file mode 100644 index 00000000..9538323e --- /dev/null +++ b/libraries/arduinoWebSockets/tests/webSocketServer/package.json @@ -0,0 +1,27 @@ +{ + "name": "webSocketServer", + "version": "1.0.0", + "description": "WebSocketServer for testing", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/Links2004/arduinoWebSockets" + }, + "keywords": [ + "esp8266", + "websocket", + "arduino" + ], + "author": "Markus Sattler", + "license": "LGPLv2", + "bugs": { + "url": "https://github.com/Links2004/arduinoWebSockets/issues" + }, + "homepage": "https://github.com/Links2004/arduinoWebSockets", + "dependencies": { + "websocket": "^1.0.18" + } +} diff --git a/libraries/arduinoWebSockets/travis/common.sh b/libraries/arduinoWebSockets/travis/common.sh new file mode 100644 index 00000000..be959faf --- /dev/null +++ b/libraries/arduinoWebSockets/travis/common.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +function build_sketches() +{ + local arduino=$1 + local srcpath=$2 + local platform=$3 + local sketches=$(find $srcpath -name *.ino) + for sketch in $sketches; do + local sketchdir=$(dirname $sketch) + if [[ -f "$sketchdir/.$platform.skip" ]]; then + echo -e "\n\n ------------ Skipping $sketch ------------ \n\n"; + continue + fi + echo -e "\n\n ------------ Building $sketch ------------ \n\n"; + $arduino --verify $sketch; + local result=$? + if [ $result -ne 0 ]; then + echo "Build failed ($sketch) build verbose..." + $arduino --verify --verbose --preserve-temp-files $sketch + result=$? + fi + if [ $result -ne 0 ]; then + echo "Build failed ($1) $sketch" + return $result + fi + done +} + + +function get_core() +{ + echo Setup core for $1 + + cd $HOME/arduino_ide/hardware + + if [ "$1" = "esp8266" ] ; then + mkdir esp8266com + cd esp8266com + git clone https://github.com/esp8266/Arduino.git esp8266 + cd esp8266/tools + python get.py + fi + + if [ "$1" = "esp32" ] ; then + mkdir espressif + cd espressif + git clone https://github.com/espressif/arduino-esp32.git esp32 + cd esp32/tools + python get.py + fi + +} diff --git a/libraries/oled-ssd1306/.travis.yml b/libraries/oled-ssd1306/.travis.yml new file mode 100644 index 00000000..88a1e72b --- /dev/null +++ b/libraries/oled-ssd1306/.travis.yml @@ -0,0 +1,24 @@ +language: python +python: + - "2.7" + +# Cache PlatformIO packages using Travis CI container-based infrastructure +sudo: false +cache: + directories: + - "~/.platformio" + +env: + - PLATFORMIO_CI_SRC=examples/SSD1306UiDemo + - PLATFORMIO_CI_SRC=examples/SSD1306SimpleDemo + - PLATFORMIO_CI_SRC=examples/SSD1306DrawingDemo + - PLATFORMIO_CI_SRC=examples/SSD1306OTADemo + - PLATFORMIO_CI_SRC=examples/SSD1306ClockDemo + + +install: + - pip install -U platformio + - platformio lib install 44 + +script: + - platformio ci --lib="." --board=nodemcuv2 diff --git a/libraries/oled-ssd1306/OLEDDisplay.cpp b/libraries/oled-ssd1306/OLEDDisplay.cpp new file mode 100644 index 00000000..a7ca6472 --- /dev/null +++ b/libraries/oled-ssd1306/OLEDDisplay.cpp @@ -0,0 +1,809 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 by Daniel Eichhorn + * Copyright (c) 2016 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing! + */ + +#include "OLEDDisplay.h" + +bool OLEDDisplay::init() { + if (!this->connect()) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Can't establish connection to display\n"); + return false; + } + this->buffer = (uint8_t*) malloc(sizeof(uint8_t) * DISPLAY_BUFFER_SIZE); + if(!this->buffer) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create display\n"); + return false; + } + + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + this->buffer_back = (uint8_t*) malloc(sizeof(uint8_t) * DISPLAY_BUFFER_SIZE); + if(!this->buffer_back) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create back buffer\n"); + free(this->buffer); + return false; + } + #endif + + sendInitCommands(); + resetDisplay(); + + return true; +} + +void OLEDDisplay::end() { + if (this->buffer) free(this->buffer); + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + if (this->buffer_back) free(this->buffer_back); + #endif +} + +void OLEDDisplay::resetDisplay(void) { + clear(); + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + memset(buffer_back, 1, DISPLAY_BUFFER_SIZE); + #endif + display(); +} + +void OLEDDisplay::setColor(OLEDDISPLAY_COLOR color) { + this->color = color; +} + +void OLEDDisplay::setPixel(int16_t x, int16_t y) { + if (x >= 0 && x < 128 && y >= 0 && y < 64) { + switch (color) { + case WHITE: buffer[x + (y / 8) * DISPLAY_WIDTH] |= (1 << (y & 7)); break; + case BLACK: buffer[x + (y / 8) * DISPLAY_WIDTH] &= ~(1 << (y & 7)); break; + case INVERSE: buffer[x + (y / 8) * DISPLAY_WIDTH] ^= (1 << (y & 7)); break; + } + } +} + +// Bresenham's algorithm - thx wikipedia and Adafruit_GFX +void OLEDDisplay::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1) { + int16_t steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + _swap_int16_t(x0, y0); + _swap_int16_t(x1, y1); + } + + if (x0 > x1) { + _swap_int16_t(x0, x1); + _swap_int16_t(y0, y1); + } + + int16_t dx, dy; + dx = x1 - x0; + dy = abs(y1 - y0); + + int16_t err = dx / 2; + int16_t ystep; + + if (y0 < y1) { + ystep = 1; + } else { + ystep = -1; + } + + for (; x0<=x1; x0++) { + if (steep) { + setPixel(y0, x0); + } else { + setPixel(x0, y0); + } + err -= dy; + if (err < 0) { + y0 += ystep; + err += dx; + } + } +} + +void OLEDDisplay::drawRect(int16_t x, int16_t y, int16_t width, int16_t height) { + drawHorizontalLine(x, y, width); + drawVerticalLine(x, y, height); + drawVerticalLine(x + width - 1, y, height); + drawHorizontalLine(x, y + height - 1, width); +} + +void OLEDDisplay::fillRect(int16_t xMove, int16_t yMove, int16_t width, int16_t height) { + for (int16_t x = xMove; x < xMove + width; x++) { + drawVerticalLine(x, yMove, height); + } +} + +void OLEDDisplay::drawCircle(int16_t x0, int16_t y0, int16_t radius) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + do { + if (dp < 0) + dp = dp + 2 * (++x) + 3; + else + dp = dp + 2 * (++x) - 2 * (--y) + 5; + + setPixel(x0 + x, y0 + y); //For the 8 octants + setPixel(x0 - x, y0 + y); + setPixel(x0 + x, y0 - y); + setPixel(x0 - x, y0 - y); + setPixel(x0 + y, y0 + x); + setPixel(x0 - y, y0 + x); + setPixel(x0 + y, y0 - x); + setPixel(x0 - y, y0 - x); + + } while (x < y); + + setPixel(x0 + radius, y0); + setPixel(x0, y0 + radius); + setPixel(x0 - radius, y0); + setPixel(x0, y0 - radius); +} + +void OLEDDisplay::drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + while (x < y) { + if (dp < 0) + dp = dp + 2 * (++x) + 3; + else + dp = dp + 2 * (++x) - 2 * (--y) + 5; + if (quads & 0x1) { + setPixel(x0 + x, y0 - y); + setPixel(x0 + y, y0 - x); + } + if (quads & 0x2) { + setPixel(x0 - y, y0 - x); + setPixel(x0 - x, y0 - y); + } + if (quads & 0x4) { + setPixel(x0 - y, y0 + x); + setPixel(x0 - x, y0 + y); + } + if (quads & 0x8) { + setPixel(x0 + x, y0 + y); + setPixel(x0 + y, y0 + x); + } + } + if (quads & 0x1 && quads & 0x8) { + setPixel(x0 + radius, y0); + } + if (quads & 0x4 && quads & 0x8) { + setPixel(x0, y0 + radius); + } + if (quads & 0x2 && quads & 0x4) { + setPixel(x0 - radius, y0); + } + if (quads & 0x1 && quads & 0x2) { + setPixel(x0, y0 - radius); + } +} + + +void OLEDDisplay::fillCircle(int16_t x0, int16_t y0, int16_t radius) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + do { + if (dp < 0) + dp = dp + 2 * (++x) + 3; + else + dp = dp + 2 * (++x) - 2 * (--y) + 5; + + drawHorizontalLine(x0 - x, y0 - y, 2*x); + drawHorizontalLine(x0 - x, y0 + y, 2*x); + drawHorizontalLine(x0 - y, y0 - x, 2*y); + drawHorizontalLine(x0 - y, y0 + x, 2*y); + + + } while (x < y); + drawHorizontalLine(x0 - radius, y0, 2 * radius); + +} + +void OLEDDisplay::drawHorizontalLine(int16_t x, int16_t y, int16_t length) { + if (y < 0 || y >= DISPLAY_HEIGHT) { return; } + + if (x < 0) { + length += x; + x = 0; + } + + if ( (x + length) > DISPLAY_WIDTH) { + length = (DISPLAY_WIDTH - x); + } + + if (length <= 0) { return; } + + uint8_t * bufferPtr = buffer; + bufferPtr += (y >> 3) * DISPLAY_WIDTH; + bufferPtr += x; + + uint8_t drawBit = 1 << (y & 7); + + switch (color) { + case WHITE: while (length--) { + *bufferPtr++ |= drawBit; + }; break; + case BLACK: drawBit = ~drawBit; while (length--) { + *bufferPtr++ &= drawBit; + }; break; + case INVERSE: while (length--) { + *bufferPtr++ ^= drawBit; + }; break; + } +} + +void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) { + if (x < 0 || x >= DISPLAY_WIDTH) return; + + if (y < 0) { + length += y; + y = 0; + } + + if ( (y + length) > DISPLAY_HEIGHT) { + length = (DISPLAY_HEIGHT - y); + } + + if (length <= 0) return; + + + uint8_t yOffset = y & 7; + uint8_t drawBit; + uint8_t *bufferPtr = buffer; + + bufferPtr += (y >> 3) * DISPLAY_WIDTH; + bufferPtr += x; + + if (yOffset) { + yOffset = 8 - yOffset; + drawBit = ~(0xFF >> (yOffset)); + + if (length < yOffset) { + drawBit &= (0xFF >> (yOffset - length)); + } + + switch (color) { + case WHITE: *bufferPtr |= drawBit; break; + case BLACK: *bufferPtr &= ~drawBit; break; + case INVERSE: *bufferPtr ^= drawBit; break; + } + + if (length < yOffset) return; + + length -= yOffset; + bufferPtr += DISPLAY_WIDTH; + } + + if (length >= 8) { + switch (color) { + case WHITE: + case BLACK: + drawBit = (color == WHITE) ? 0xFF : 0x00; + do { + *bufferPtr = drawBit; + bufferPtr += DISPLAY_WIDTH; + length -= 8; + } while (length >= 8); + break; + case INVERSE: + do { + *bufferPtr = ~(*bufferPtr); + bufferPtr += DISPLAY_WIDTH; + length -= 8; + } while (length >= 8); + break; + } + } + + if (length > 0) { + drawBit = (1 << (length & 7)) - 1; + switch (color) { + case WHITE: *bufferPtr |= drawBit; break; + case BLACK: *bufferPtr &= ~drawBit; break; + case INVERSE: *bufferPtr ^= drawBit; break; + } + } +} + +void OLEDDisplay::drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress) { + uint16_t radius = height / 2; + uint16_t xRadius = x + radius; + uint16_t yRadius = y + radius; + uint16_t doubleRadius = 2 * radius; + uint16_t innerRadius = radius - 2; + + setColor(WHITE); + drawCircleQuads(xRadius, yRadius, radius, 0b00000110); + drawHorizontalLine(xRadius, y, width - doubleRadius + 1); + drawHorizontalLine(xRadius, y + height, width - doubleRadius + 1); + drawCircleQuads(x + width - radius, yRadius, radius, 0b00001001); + + uint16_t maxProgressWidth = (width - doubleRadius - 1) * progress / 100; + + fillCircle(xRadius, yRadius, innerRadius); + fillRect(xRadius + 1, y + 2, maxProgressWidth, height - 3); + fillCircle(xRadius + maxProgressWidth, yRadius, innerRadius); +} + +void OLEDDisplay::drawFastImage(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *image) { + drawInternal(xMove, yMove, width, height, image, 0, 0); +} + +void OLEDDisplay::drawXbm(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *xbm) { + int16_t widthInXbm = (width + 7) / 8; + uint8_t data; + + for(int16_t y = 0; y < height; y++) { + for(int16_t x = 0; x < width; x++ ) { + if (x & 7) { + data >>= 1; // Move a bit + } else { // Read new data every 8 bit + data = pgm_read_byte(xbm + (x / 8) + y * widthInXbm); + } + // if there is a bit draw it + if (data & 0x01) { + setPixel(xMove + x, yMove + y); + } + } + } +} + +void OLEDDisplay::drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth) { + uint8_t textHeight = pgm_read_byte(fontData + HEIGHT_POS); + uint8_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + uint16_t sizeOfJumpTable = pgm_read_byte(fontData + CHAR_NUM_POS) * JUMPTABLE_BYTES; + + uint8_t cursorX = 0; + uint8_t cursorY = 0; + + switch (textAlignment) { + case TEXT_ALIGN_CENTER_BOTH: + yMove -= textHeight >> 1; + // Fallthrough + case TEXT_ALIGN_CENTER: + xMove -= textWidth >> 1; // divide by 2 + break; + case TEXT_ALIGN_RIGHT: + xMove -= textWidth; + break; + } + + // Don't draw anything if it is not on the screen. + if (xMove + textWidth < 0 || xMove > DISPLAY_WIDTH ) {return;} + if (yMove + textHeight < 0 || yMove > DISPLAY_HEIGHT) {return;} + + for (uint16_t j = 0; j < textLength; j++) { + int16_t xPos = xMove + cursorX; + int16_t yPos = yMove + cursorY; + + byte code = text[j]; + if (code >= firstChar) { + byte charCode = code - firstChar; + + // 4 Bytes per char code + byte msbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES ); // MSB \ JumpAddress + byte lsbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_LSB); // LSB / + byte charByteSize = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_SIZE); // Size + byte currentCharWidth = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); // Width + + // Test if the char is drawable + if (!(msbJumpToChar == 255 && lsbJumpToChar == 255)) { + // Get the position of the char data + uint16_t charDataPosition = JUMPTABLE_START + sizeOfJumpTable + ((msbJumpToChar << 8) + lsbJumpToChar); + drawInternal(xPos, yPos, currentCharWidth, textHeight, fontData, charDataPosition, charByteSize); + } + + cursorX += currentCharWidth; + } + } +} + + +void OLEDDisplay::drawString(int16_t xMove, int16_t yMove, String strUser) { + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + + // char* text must be freed! + char* text = utf8ascii(strUser); + + uint16_t yOffset = 0; + // If the string should be centered vertically too + // we need to now how heigh the string is. + if (textAlignment == TEXT_ALIGN_CENTER_BOTH) { + uint16_t lb = 0; + // Find number of linebreaks in text + for (uint16_t i=0;text[i] != 0; i++) { + lb += (text[i] == 10); + } + // Calculate center + yOffset = (lb * lineHeight) / 2; + } + + uint16_t line = 0; + char* textPart = strtok(text,"\n"); + while (textPart != NULL) { + uint16_t length = strlen(textPart); + drawStringInternal(xMove, yMove - yOffset + (line++) * lineHeight, textPart, length, getStringWidth(textPart, length)); + textPart = strtok(NULL, "\n"); + } + free(text); +} + +void OLEDDisplay::drawStringMaxWidth(int16_t xMove, int16_t yMove, uint16_t maxLineWidth, String strUser) { + uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + + char* text = utf8ascii(strUser); + + uint16_t length = strlen(text); + uint16_t lastDrawnPos = 0; + uint16_t lineNumber = 0; + uint16_t strWidth = 0; + + uint16_t preferredBreakpoint = 0; + uint16_t widthAtBreakpoint = 0; + + for (uint16_t i = 0; i < length; i++) { + strWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[i] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); + + // Always try to break on a space or dash + if (text[i] == ' ' || text[i]== '-') { + preferredBreakpoint = i; + widthAtBreakpoint = strWidth; + } + + if (strWidth >= maxLineWidth) { + if (preferredBreakpoint == 0) { + preferredBreakpoint = i; + widthAtBreakpoint = strWidth; + } + drawStringInternal(xMove, yMove + (lineNumber++) * lineHeight , &text[lastDrawnPos], preferredBreakpoint - lastDrawnPos, widthAtBreakpoint); + lastDrawnPos = preferredBreakpoint + 1; + // It is possible that we did not draw all letters to i so we need + // to account for the width of the chars from `i - preferredBreakpoint` + // by calculating the width we did not draw yet. + strWidth = strWidth - widthAtBreakpoint; + preferredBreakpoint = 0; + } + } + + // Draw last part if needed + if (lastDrawnPos < length) { + drawStringInternal(xMove, yMove + lineNumber * lineHeight , &text[lastDrawnPos], length - lastDrawnPos, getStringWidth(&text[lastDrawnPos], length - lastDrawnPos)); + } + + free(text); +} + +uint16_t OLEDDisplay::getStringWidth(const char* text, uint16_t length) { + uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + + uint16_t stringWidth = 0; + uint16_t maxWidth = 0; + + while (length--) { + stringWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[length] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); + if (text[length] == 10) { + maxWidth = max(maxWidth, stringWidth); + stringWidth = 0; + } + } + + return max(maxWidth, stringWidth); +} + +uint16_t OLEDDisplay::getStringWidth(String strUser) { + char* text = utf8ascii(strUser); + uint16_t length = strlen(text); + uint16_t width = getStringWidth(text, length); + free(text); + return width; +} + +void OLEDDisplay::setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment) { + this->textAlignment = textAlignment; +} + +void OLEDDisplay::setFont(const char *fontData) { + this->fontData = fontData; +} + +void OLEDDisplay::displayOn(void) { + sendCommand(DISPLAYON); +} + +void OLEDDisplay::displayOff(void) { + sendCommand(DISPLAYOFF); +} + +void OLEDDisplay::invertDisplay(void) { + sendCommand(INVERTDISPLAY); +} + +void OLEDDisplay::normalDisplay(void) { + sendCommand(NORMALDISPLAY); +} + +void OLEDDisplay::setContrast(char contrast) { + sendCommand(SETCONTRAST); + sendCommand(contrast); +} + +void OLEDDisplay::flipScreenVertically() { + sendCommand(SEGREMAP | 0x01); + sendCommand(COMSCANDEC); //Rotate screen 180 Deg +} + +void OLEDDisplay::clear(void) { + memset(buffer, 0, DISPLAY_BUFFER_SIZE); +} + +void OLEDDisplay::drawLogBuffer(uint16_t xMove, uint16_t yMove) { + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + // Always align left + setTextAlignment(TEXT_ALIGN_LEFT); + + // State values + uint16_t length = 0; + uint16_t line = 0; + uint16_t lastPos = 0; + + for (uint16_t i=0;ilogBufferFilled;i++){ + // Everytime we have a \n print + if (this->logBuffer[i] == 10) { + length++; + // Draw string on line `line` from lastPos to length + // Passing 0 as the lenght because we are in TEXT_ALIGN_LEFT + drawStringInternal(xMove, yMove + (line++) * lineHeight, &this->logBuffer[lastPos], length, 0); + // Remember last pos + lastPos = i; + // Reset length + length = 0; + } else { + // Count chars until next linebreak + length++; + } + } + // Draw the remaining string + if (length > 0) { + drawStringInternal(xMove, yMove + line * lineHeight, &this->logBuffer[lastPos], length, 0); + } +} + +bool OLEDDisplay::setLogBuffer(uint16_t lines, uint16_t chars){ + if (logBuffer != NULL) free(logBuffer); + uint16_t size = lines * chars; + if (size > 0) { + this->logBufferLine = 0; // Lines printed + this->logBufferMaxLines = lines; // Lines max printable + this->logBufferSize = size; // Total number of characters the buffer can hold + this->logBuffer = (char *) malloc(size * sizeof(uint8_t)); + if(!this->logBuffer) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][setLogBuffer] Not enough memory to create log buffer\n"); + return false; + } + } + return true; +} + +size_t OLEDDisplay::write(uint8_t c) { + if (this->logBufferSize > 0) { + // Don't waste space on \r\n line endings, dropping \r + if (c == 13) return 1; + + bool maxLineNotReached = this->logBufferLine < this->logBufferMaxLines; + bool bufferNotFull = this->logBufferFilled < this->logBufferSize; + + // Can we write to the buffer? + if (bufferNotFull && maxLineNotReached) { + this->logBuffer[logBufferFilled] = utf8ascii(c); + this->logBufferFilled++; + // Keep track of lines written + if (c == 10) this->logBufferLine++; + } else { + // Max line number is reached + if (!maxLineNotReached) this->logBufferLine--; + + // Find the end of the first line + uint16_t firstLineEnd = 0; + for (uint16_t i=0;ilogBufferFilled;i++) { + if (this->logBuffer[i] == 10){ + // Include last char too + firstLineEnd = i + 1; + break; + } + } + // If there was a line ending + if (firstLineEnd > 0) { + // Calculate the new logBufferFilled value + this->logBufferFilled = logBufferFilled - firstLineEnd; + // Now we move the lines infront of the buffer + memcpy(this->logBuffer, &this->logBuffer[firstLineEnd], logBufferFilled); + } else { + // Let's reuse the buffer if it was full + if (!bufferNotFull) { + this->logBufferFilled = 0; + }// else { + // Nothing to do here + //} + } + write(c); + } + } + // We are always writing all uint8_t to the buffer + return 1; +} + +size_t OLEDDisplay::write(const char* str) { + if (str == NULL) return 0; + size_t length = strlen(str); + for (size_t i = 0; i < length; i++) { + write(str[i]); + } + return length; +} + +// Private functions +void OLEDDisplay::sendInitCommands(void) { + sendCommand(DISPLAYOFF); + sendCommand(SETDISPLAYCLOCKDIV); + sendCommand(0xF0); // Increase speed of the display max ~96Hz + sendCommand(SETMULTIPLEX); + sendCommand(0x3F); + sendCommand(SETDISPLAYOFFSET); + sendCommand(0x00); + sendCommand(SETSTARTLINE); + sendCommand(CHARGEPUMP); + sendCommand(0x14); + sendCommand(MEMORYMODE); + sendCommand(0x00); + sendCommand(SEGREMAP); + sendCommand(COMSCANINC); + sendCommand(SETCOMPINS); + sendCommand(0x12); + sendCommand(SETCONTRAST); + sendCommand(0xCF); + sendCommand(SETPRECHARGE); + sendCommand(0xF1); + sendCommand(DISPLAYALLON_RESUME); + sendCommand(NORMALDISPLAY); + sendCommand(0x2e); // stop scroll + sendCommand(DISPLAYON); +} + +void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *data, uint16_t offset, uint16_t bytesInData) { + if (width < 0 || height < 0) return; + if (yMove + height < 0 || yMove > DISPLAY_HEIGHT) return; + if (xMove + width < 0 || xMove > DISPLAY_WIDTH) return; + + uint8_t rasterHeight = 1 + ((height - 1) >> 3); // fast ceil(height / 8.0) + int8_t yOffset = yMove & 7; + + bytesInData = bytesInData == 0 ? width * rasterHeight : bytesInData; + + int16_t initYMove = yMove; + int8_t initYOffset = yOffset; + + + for (uint16_t i = 0; i < bytesInData; i++) { + + // Reset if next horizontal drawing phase is started. + if ( i % rasterHeight == 0) { + yMove = initYMove; + yOffset = initYOffset; + } + + byte currentByte = pgm_read_byte(data + offset + i); + + int16_t xPos = xMove + (i / rasterHeight); + int16_t yPos = ((yMove >> 3) + (i % rasterHeight)) * DISPLAY_WIDTH; + + int16_t yScreenPos = yMove + yOffset; + int16_t dataPos = xPos + yPos; + + if (dataPos >= 0 && dataPos < DISPLAY_BUFFER_SIZE && + xPos >= 0 && xPos < DISPLAY_WIDTH ) { + + if (yOffset >= 0) { + switch (this->color) { + case WHITE: buffer[dataPos] |= currentByte << yOffset; break; + case BLACK: buffer[dataPos] &= ~(currentByte << yOffset); break; + case INVERSE: buffer[dataPos] ^= currentByte << yOffset; break; + } + if (dataPos < (DISPLAY_BUFFER_SIZE - DISPLAY_WIDTH)) { + switch (this->color) { + case WHITE: buffer[dataPos + DISPLAY_WIDTH] |= currentByte >> (8 - yOffset); break; + case BLACK: buffer[dataPos + DISPLAY_WIDTH] &= ~(currentByte >> (8 - yOffset)); break; + case INVERSE: buffer[dataPos + DISPLAY_WIDTH] ^= currentByte >> (8 - yOffset); break; + } + } + } else { + // Make new offset position + yOffset = -yOffset; + + switch (this->color) { + case WHITE: buffer[dataPos] |= currentByte >> yOffset; break; + case BLACK: buffer[dataPos] &= ~(currentByte >> yOffset); break; + case INVERSE: buffer[dataPos] ^= currentByte >> yOffset; break; + } + + // Prepare for next iteration by moving one block up + yMove -= 8; + + // and setting the new yOffset + yOffset = 8 - yOffset; + } + optimistic_yield(10000); + } + } +} + +// Code form http://playground.arduino.cc/Main/Utf8ascii +uint8_t OLEDDisplay::utf8ascii(byte ascii) { + static uint8_t LASTCHAR; + + if ( ascii < 128 ) { // Standard ASCII-set 0..0x7F handling + LASTCHAR = 0; + return ascii; + } + + uint8_t last = LASTCHAR; // get last char + LASTCHAR = ascii; + + switch (last) { // conversion depnding on first UTF8-character + case 0xC2: return (ascii); break; + case 0xC3: return (ascii | 0xC0); break; + case 0x82: if (ascii == 0xAC) return (0x80); // special case Euro-symbol + } + + return 0; // otherwise: return zero, if character has to be ignored +} + +// You need to free the char! +char* OLEDDisplay::utf8ascii(String str) { + uint16_t k = 0; + uint16_t length = str.length() + 1; + + // Copy the string into a char array + char* s = (char*) malloc(length * sizeof(char)); + if(!s) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][utf8ascii] Can't allocate another char array. Drop support for UTF-8.\n"); + return (char*) str.c_str(); + } + str.toCharArray(s, length); + + length--; + + for (uint16_t i=0; i < length; i++) { + char c = utf8ascii(s[i]); + if (c!=0) { + s[k++]=c; + } + } + + s[k]=0; + + // This will leak 's' be sure to free it in the calling function. + return s; +} diff --git a/libraries/oled-ssd1306/OLEDDisplay.h b/libraries/oled-ssd1306/OLEDDisplay.h new file mode 100644 index 00000000..81537a24 --- /dev/null +++ b/libraries/oled-ssd1306/OLEDDisplay.h @@ -0,0 +1,268 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 by Daniel Eichhorn + * Copyright (c) 2016 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing! + */ + +#ifndef OLEDDISPLAY_h +#define OLEDDISPLAY_h + +#include +#include "OLEDDisplayFonts.h" + +//#define DEBUG_OLEDDISPLAY(...) Serial.printf( __VA_ARGS__ ) + +#ifndef DEBUG_OLEDDISPLAY +#define DEBUG_OLEDDISPLAY(...) +#endif + +// Use DOUBLE BUFFERING by default +#ifndef OLEDDISPLAY_REDUCE_MEMORY +#define OLEDDISPLAY_DOUBLE_BUFFER +#endif + + +// Display settings +#define DISPLAY_WIDTH 128 +#define DISPLAY_HEIGHT 64 +#define DISPLAY_BUFFER_SIZE 1024 + +// Header Values +#define JUMPTABLE_BYTES 4 + +#define JUMPTABLE_LSB 1 +#define JUMPTABLE_SIZE 2 +#define JUMPTABLE_WIDTH 3 +#define JUMPTABLE_START 4 + +#define WIDTH_POS 0 +#define HEIGHT_POS 1 +#define FIRST_CHAR_POS 2 +#define CHAR_NUM_POS 3 + + +// Display commands +#define CHARGEPUMP 0x8D +#define COLUMNADDR 0x21 +#define COMSCANDEC 0xC8 +#define COMSCANINC 0xC0 +#define DISPLAYALLON 0xA5 +#define DISPLAYALLON_RESUME 0xA4 +#define DISPLAYOFF 0xAE +#define DISPLAYON 0xAF +#define EXTERNALVCC 0x1 +#define INVERTDISPLAY 0xA7 +#define MEMORYMODE 0x20 +#define NORMALDISPLAY 0xA6 +#define PAGEADDR 0x22 +#define SEGREMAP 0xA0 +#define SETCOMPINS 0xDA +#define SETCONTRAST 0x81 +#define SETDISPLAYCLOCKDIV 0xD5 +#define SETDISPLAYOFFSET 0xD3 +#define SETHIGHCOLUMN 0x10 +#define SETLOWCOLUMN 0x00 +#define SETMULTIPLEX 0xA8 +#define SETPRECHARGE 0xD9 +#define SETSEGMENTREMAP 0xA1 +#define SETSTARTLINE 0x40 +#define SETVCOMDETECT 0xDB +#define SWITCHCAPVCC 0x2 + +#ifndef _swap_int16_t +#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; } +#endif + +enum OLEDDISPLAY_COLOR { + BLACK = 0, + WHITE = 1, + INVERSE = 2 +}; + +enum OLEDDISPLAY_TEXT_ALIGNMENT { + TEXT_ALIGN_LEFT = 0, + TEXT_ALIGN_RIGHT = 1, + TEXT_ALIGN_CENTER = 2, + TEXT_ALIGN_CENTER_BOTH = 3 +}; + + +class OLEDDisplay : public Print { + public: + // Initialize the display + bool init(); + + // Free the memory used by the display + void end(); + + // Cycle through the initialization + void resetDisplay(void); + + /* Drawing functions */ + // Sets the color of all pixel operations + void setColor(OLEDDISPLAY_COLOR color); + + // Draw a pixel at given position + void setPixel(int16_t x, int16_t y); + + // Draw a line from position 0 to position 1 + void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1); + + // Draw the border of a rectangle at the given location + void drawRect(int16_t x, int16_t y, int16_t width, int16_t height); + + // Fill the rectangle + void fillRect(int16_t x, int16_t y, int16_t width, int16_t height); + + // Draw the border of a circle + void drawCircle(int16_t x, int16_t y, int16_t radius); + + // Draw all Quadrants specified in the quads bit mask + void drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads); + + // Fill circle + void fillCircle(int16_t x, int16_t y, int16_t radius); + + // Draw a line horizontally + void drawHorizontalLine(int16_t x, int16_t y, int16_t length); + + // Draw a lin vertically + void drawVerticalLine(int16_t x, int16_t y, int16_t length); + + // Draws a rounded progress bar with the outer dimensions given by width and height. Progress is + // a unsigned byte value between 0 and 100 + void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress); + + // Draw a bitmap in the internal image format + void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const char *image); + + // Draw a XBM + void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const char *xbm); + + /* Text functions */ + + // Draws a string at the given location + void drawString(int16_t x, int16_t y, String text); + + // Draws a String with a maximum width at the given location. + // If the given String is wider than the specified width + // The text will be wrapped to the next line at a space or dash + void drawStringMaxWidth(int16_t x, int16_t y, uint16_t maxLineWidth, String text); + + // Returns the width of the const char* with the current + // font settings + uint16_t getStringWidth(const char* text, uint16_t length); + + // Convencience method for the const char version + uint16_t getStringWidth(String text); + + // Specifies relative to which anchor point + // the text is rendered. Available constants: + // TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH + void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment); + + // Sets the current font. Available default fonts + // ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24 + void setFont(const char *fontData); + + /* Display functions */ + + // Turn the display on + void displayOn(void); + + // Turn the display offs + void displayOff(void); + + // Inverted display mode + void invertDisplay(void); + + // Normal display mode + void normalDisplay(void); + + // Set display contrast + void setContrast(char contrast); + + // Turn the display upside down + void flipScreenVertically(); + + // Write the buffer to the display memory + virtual void display(void) = 0; + + // Clear the local pixel buffer + void clear(void); + + // Log buffer implementation + + // This will define the lines and characters you can + // print to the screen. When you exeed the buffer size (lines * chars) + // the output may be truncated due to the size constraint. + bool setLogBuffer(uint16_t lines, uint16_t chars); + + // Draw the log buffer at position (x, y) + void drawLogBuffer(uint16_t x, uint16_t y); + + // Implementent needed function to be compatible with Print class + size_t write(uint8_t c); + size_t write(const char* s); + + uint8_t *buffer; + + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t *buffer_back; + #endif + + protected: + + OLEDDISPLAY_TEXT_ALIGNMENT textAlignment = TEXT_ALIGN_LEFT; + OLEDDISPLAY_COLOR color = WHITE; + + const char *fontData = ArialMT_Plain_10; + + // State values for logBuffer + uint16_t logBufferSize = 0; + uint16_t logBufferFilled = 0; + uint16_t logBufferLine = 0; + uint16_t logBufferMaxLines = 0; + char *logBuffer = NULL; + + // Send a command to the display (low level function) + virtual void sendCommand(uint8_t com) {}; + + // Connect to the display + virtual bool connect() {}; + + // Send all the init commands + void sendInitCommands(); + + // converts utf8 characters to extended ascii + static char* utf8ascii(String s); + static byte utf8ascii(byte ascii); + + void inline drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *data, uint16_t offset, uint16_t bytesInData) __attribute__((always_inline)); + + void drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth); + +}; + +#endif diff --git a/libraries/oled-ssd1306/OLEDDisplayFonts.h b/libraries/oled-ssd1306/OLEDDisplayFonts.h new file mode 100644 index 00000000..6dd21ef6 --- /dev/null +++ b/libraries/oled-ssd1306/OLEDDisplayFonts.h @@ -0,0 +1,1274 @@ +#ifndef OLEDDISPLAYFONTS_h +#define OLEDDISPLAYFONTS_h + +const char ArialMT_Plain_10[] PROGMEM = { + 0x0A, // Width: 10 + 0x0D, // Height: 13 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x03, // 32:65535 + 0x00, 0x00, 0x04, 0x03, // 33:0 + 0x00, 0x04, 0x05, 0x04, // 34:4 + 0x00, 0x09, 0x09, 0x06, // 35:9 + 0x00, 0x12, 0x0A, 0x06, // 36:18 + 0x00, 0x1C, 0x10, 0x09, // 37:28 + 0x00, 0x2C, 0x0E, 0x07, // 38:44 + 0x00, 0x3A, 0x01, 0x02, // 39:58 + 0x00, 0x3B, 0x06, 0x03, // 40:59 + 0x00, 0x41, 0x06, 0x03, // 41:65 + 0x00, 0x47, 0x05, 0x04, // 42:71 + 0x00, 0x4C, 0x09, 0x06, // 43:76 + 0x00, 0x55, 0x04, 0x03, // 44:85 + 0x00, 0x59, 0x03, 0x03, // 45:89 + 0x00, 0x5C, 0x04, 0x03, // 46:92 + 0x00, 0x60, 0x05, 0x03, // 47:96 + 0x00, 0x65, 0x0A, 0x06, // 48:101 + 0x00, 0x6F, 0x08, 0x06, // 49:111 + 0x00, 0x77, 0x0A, 0x06, // 50:119 + 0x00, 0x81, 0x0A, 0x06, // 51:129 + 0x00, 0x8B, 0x0B, 0x06, // 52:139 + 0x00, 0x96, 0x0A, 0x06, // 53:150 + 0x00, 0xA0, 0x0A, 0x06, // 54:160 + 0x00, 0xAA, 0x09, 0x06, // 55:170 + 0x00, 0xB3, 0x0A, 0x06, // 56:179 + 0x00, 0xBD, 0x0A, 0x06, // 57:189 + 0x00, 0xC7, 0x04, 0x03, // 58:199 + 0x00, 0xCB, 0x04, 0x03, // 59:203 + 0x00, 0xCF, 0x0A, 0x06, // 60:207 + 0x00, 0xD9, 0x09, 0x06, // 61:217 + 0x00, 0xE2, 0x09, 0x06, // 62:226 + 0x00, 0xEB, 0x0B, 0x06, // 63:235 + 0x00, 0xF6, 0x14, 0x0A, // 64:246 + 0x01, 0x0A, 0x0E, 0x07, // 65:266 + 0x01, 0x18, 0x0C, 0x07, // 66:280 + 0x01, 0x24, 0x0C, 0x07, // 67:292 + 0x01, 0x30, 0x0B, 0x07, // 68:304 + 0x01, 0x3B, 0x0C, 0x07, // 69:315 + 0x01, 0x47, 0x09, 0x06, // 70:327 + 0x01, 0x50, 0x0D, 0x08, // 71:336 + 0x01, 0x5D, 0x0C, 0x07, // 72:349 + 0x01, 0x69, 0x04, 0x03, // 73:361 + 0x01, 0x6D, 0x08, 0x05, // 74:365 + 0x01, 0x75, 0x0E, 0x07, // 75:373 + 0x01, 0x83, 0x0C, 0x06, // 76:387 + 0x01, 0x8F, 0x10, 0x08, // 77:399 + 0x01, 0x9F, 0x0C, 0x07, // 78:415 + 0x01, 0xAB, 0x0E, 0x08, // 79:427 + 0x01, 0xB9, 0x0B, 0x07, // 80:441 + 0x01, 0xC4, 0x0E, 0x08, // 81:452 + 0x01, 0xD2, 0x0C, 0x07, // 82:466 + 0x01, 0xDE, 0x0C, 0x07, // 83:478 + 0x01, 0xEA, 0x0B, 0x06, // 84:490 + 0x01, 0xF5, 0x0C, 0x07, // 85:501 + 0x02, 0x01, 0x0D, 0x07, // 86:513 + 0x02, 0x0E, 0x11, 0x09, // 87:526 + 0x02, 0x1F, 0x0E, 0x07, // 88:543 + 0x02, 0x2D, 0x0D, 0x07, // 89:557 + 0x02, 0x3A, 0x0C, 0x06, // 90:570 + 0x02, 0x46, 0x06, 0x03, // 91:582 + 0x02, 0x4C, 0x06, 0x03, // 92:588 + 0x02, 0x52, 0x04, 0x03, // 93:594 + 0x02, 0x56, 0x09, 0x05, // 94:598 + 0x02, 0x5F, 0x0C, 0x06, // 95:607 + 0x02, 0x6B, 0x03, 0x03, // 96:619 + 0x02, 0x6E, 0x0A, 0x06, // 97:622 + 0x02, 0x78, 0x0A, 0x06, // 98:632 + 0x02, 0x82, 0x0A, 0x05, // 99:642 + 0x02, 0x8C, 0x0A, 0x06, // 100:652 + 0x02, 0x96, 0x0A, 0x06, // 101:662 + 0x02, 0xA0, 0x05, 0x03, // 102:672 + 0x02, 0xA5, 0x0A, 0x06, // 103:677 + 0x02, 0xAF, 0x0A, 0x06, // 104:687 + 0x02, 0xB9, 0x04, 0x02, // 105:697 + 0x02, 0xBD, 0x04, 0x02, // 106:701 + 0x02, 0xC1, 0x08, 0x05, // 107:705 + 0x02, 0xC9, 0x04, 0x02, // 108:713 + 0x02, 0xCD, 0x10, 0x08, // 109:717 + 0x02, 0xDD, 0x0A, 0x06, // 110:733 + 0x02, 0xE7, 0x0A, 0x06, // 111:743 + 0x02, 0xF1, 0x0A, 0x06, // 112:753 + 0x02, 0xFB, 0x0A, 0x06, // 113:763 + 0x03, 0x05, 0x05, 0x03, // 114:773 + 0x03, 0x0A, 0x08, 0x05, // 115:778 + 0x03, 0x12, 0x06, 0x03, // 116:786 + 0x03, 0x18, 0x0A, 0x06, // 117:792 + 0x03, 0x22, 0x09, 0x05, // 118:802 + 0x03, 0x2B, 0x0E, 0x07, // 119:811 + 0x03, 0x39, 0x0A, 0x05, // 120:825 + 0x03, 0x43, 0x09, 0x05, // 121:835 + 0x03, 0x4C, 0x0A, 0x05, // 122:844 + 0x03, 0x56, 0x06, 0x03, // 123:854 + 0x03, 0x5C, 0x04, 0x03, // 124:860 + 0x03, 0x60, 0x05, 0x03, // 125:864 + 0x03, 0x65, 0x09, 0x06, // 126:869 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 128:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 129:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 130:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 131:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 132:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 133:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 134:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 135:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 136:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 137:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 138:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 139:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 140:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 141:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 142:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 143:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 144:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 145:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 146:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 147:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 148:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 149:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 150:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 151:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 152:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 153:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 154:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 155:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 156:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 157:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 158:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 159:65535 + 0xFF, 0xFF, 0x00, 0x03, // 160:65535 + 0x03, 0x6E, 0x04, 0x03, // 161:878 + 0x03, 0x72, 0x0A, 0x06, // 162:882 + 0x03, 0x7C, 0x0C, 0x06, // 163:892 + 0x03, 0x88, 0x0A, 0x06, // 164:904 + 0x03, 0x92, 0x0A, 0x06, // 165:914 + 0x03, 0x9C, 0x04, 0x03, // 166:924 + 0x03, 0xA0, 0x0A, 0x06, // 167:928 + 0x03, 0xAA, 0x05, 0x03, // 168:938 + 0x03, 0xAF, 0x0D, 0x07, // 169:943 + 0x03, 0xBC, 0x07, 0x04, // 170:956 + 0x03, 0xC3, 0x0A, 0x06, // 171:963 + 0x03, 0xCD, 0x09, 0x06, // 172:973 + 0x03, 0xD6, 0x03, 0x03, // 173:982 + 0x03, 0xD9, 0x0D, 0x07, // 174:985 + 0x03, 0xE6, 0x0B, 0x06, // 175:998 + 0x03, 0xF1, 0x07, 0x04, // 176:1009 + 0x03, 0xF8, 0x0A, 0x05, // 177:1016 + 0x04, 0x02, 0x05, 0x03, // 178:1026 + 0x04, 0x07, 0x05, 0x03, // 179:1031 + 0x04, 0x0C, 0x05, 0x03, // 180:1036 + 0x04, 0x11, 0x0A, 0x06, // 181:1041 + 0x04, 0x1B, 0x09, 0x05, // 182:1051 + 0x04, 0x24, 0x03, 0x03, // 183:1060 + 0x04, 0x27, 0x06, 0x03, // 184:1063 + 0x04, 0x2D, 0x05, 0x03, // 185:1069 + 0x04, 0x32, 0x07, 0x04, // 186:1074 + 0x04, 0x39, 0x0A, 0x06, // 187:1081 + 0x04, 0x43, 0x10, 0x08, // 188:1091 + 0x04, 0x53, 0x10, 0x08, // 189:1107 + 0x04, 0x63, 0x10, 0x08, // 190:1123 + 0x04, 0x73, 0x0A, 0x06, // 191:1139 + 0x04, 0x7D, 0x0E, 0x07, // 192:1149 + 0x04, 0x8B, 0x0E, 0x07, // 193:1163 + 0x04, 0x99, 0x0E, 0x07, // 194:1177 + 0x04, 0xA7, 0x0E, 0x07, // 195:1191 + 0x04, 0xB5, 0x0E, 0x07, // 196:1205 + 0x04, 0xC3, 0x0E, 0x07, // 197:1219 + 0x04, 0xD1, 0x12, 0x0A, // 198:1233 + 0x04, 0xE3, 0x0C, 0x07, // 199:1251 + 0x04, 0xEF, 0x0C, 0x07, // 200:1263 + 0x04, 0xFB, 0x0C, 0x07, // 201:1275 + 0x05, 0x07, 0x0C, 0x07, // 202:1287 + 0x05, 0x13, 0x0C, 0x07, // 203:1299 + 0x05, 0x1F, 0x05, 0x03, // 204:1311 + 0x05, 0x24, 0x04, 0x03, // 205:1316 + 0x05, 0x28, 0x04, 0x03, // 206:1320 + 0x05, 0x2C, 0x05, 0x03, // 207:1324 + 0x05, 0x31, 0x0B, 0x07, // 208:1329 + 0x05, 0x3C, 0x0C, 0x07, // 209:1340 + 0x05, 0x48, 0x0E, 0x08, // 210:1352 + 0x05, 0x56, 0x0E, 0x08, // 211:1366 + 0x05, 0x64, 0x0E, 0x08, // 212:1380 + 0x05, 0x72, 0x0E, 0x08, // 213:1394 + 0x05, 0x80, 0x0E, 0x08, // 214:1408 + 0x05, 0x8E, 0x0A, 0x06, // 215:1422 + 0x05, 0x98, 0x0D, 0x08, // 216:1432 + 0x05, 0xA5, 0x0C, 0x07, // 217:1445 + 0x05, 0xB1, 0x0C, 0x07, // 218:1457 + 0x05, 0xBD, 0x0C, 0x07, // 219:1469 + 0x05, 0xC9, 0x0C, 0x07, // 220:1481 + 0x05, 0xD5, 0x0D, 0x07, // 221:1493 + 0x05, 0xE2, 0x0B, 0x07, // 222:1506 + 0x05, 0xED, 0x0C, 0x06, // 223:1517 + 0x05, 0xF9, 0x0A, 0x06, // 224:1529 + 0x06, 0x03, 0x0A, 0x06, // 225:1539 + 0x06, 0x0D, 0x0A, 0x06, // 226:1549 + 0x06, 0x17, 0x0A, 0x06, // 227:1559 + 0x06, 0x21, 0x0A, 0x06, // 228:1569 + 0x06, 0x2B, 0x0A, 0x06, // 229:1579 + 0x06, 0x35, 0x10, 0x09, // 230:1589 + 0x06, 0x45, 0x0A, 0x05, // 231:1605 + 0x06, 0x4F, 0x0A, 0x06, // 232:1615 + 0x06, 0x59, 0x0A, 0x06, // 233:1625 + 0x06, 0x63, 0x0A, 0x06, // 234:1635 + 0x06, 0x6D, 0x0A, 0x06, // 235:1645 + 0x06, 0x77, 0x05, 0x03, // 236:1655 + 0x06, 0x7C, 0x04, 0x03, // 237:1660 + 0x06, 0x80, 0x05, 0x03, // 238:1664 + 0x06, 0x85, 0x05, 0x03, // 239:1669 + 0x06, 0x8A, 0x0A, 0x06, // 240:1674 + 0x06, 0x94, 0x0A, 0x06, // 241:1684 + 0x06, 0x9E, 0x0A, 0x06, // 242:1694 + 0x06, 0xA8, 0x0A, 0x06, // 243:1704 + 0x06, 0xB2, 0x0A, 0x06, // 244:1714 + 0x06, 0xBC, 0x0A, 0x06, // 245:1724 + 0x06, 0xC6, 0x0A, 0x06, // 246:1734 + 0x06, 0xD0, 0x09, 0x05, // 247:1744 + 0x06, 0xD9, 0x0A, 0x06, // 248:1753 + 0x06, 0xE3, 0x0A, 0x06, // 249:1763 + 0x06, 0xED, 0x0A, 0x06, // 250:1773 + 0x06, 0xF7, 0x0A, 0x06, // 251:1783 + 0x07, 0x01, 0x0A, 0x06, // 252:1793 + 0x07, 0x0B, 0x09, 0x05, // 253:1803 + 0x07, 0x14, 0x0A, 0x06, // 254:1812 + 0x07, 0x1E, 0x09, 0x05, // 255:1822 + + // Font Data: + 0x00,0x00,0xF8,0x02, // 33 + 0x38,0x00,0x00,0x00,0x38, // 34 + 0xA0,0x03,0xE0,0x00,0xB8,0x03,0xE0,0x00,0xB8, // 35 + 0x30,0x01,0x28,0x02,0xF8,0x07,0x48,0x02,0x90,0x01, // 36 + 0x00,0x00,0x30,0x00,0x48,0x00,0x30,0x03,0xC0,0x00,0xB0,0x01,0x48,0x02,0x80,0x01, // 37 + 0x80,0x01,0x50,0x02,0x68,0x02,0xA8,0x02,0x18,0x01,0x80,0x03,0x80,0x02, // 38 + 0x38, // 39 + 0xE0,0x03,0x10,0x04,0x08,0x08, // 40 + 0x08,0x08,0x10,0x04,0xE0,0x03, // 41 + 0x28,0x00,0x18,0x00,0x28, // 42 + 0x40,0x00,0x40,0x00,0xF0,0x01,0x40,0x00,0x40, // 43 + 0x00,0x00,0x00,0x06, // 44 + 0x80,0x00,0x80, // 45 + 0x00,0x00,0x00,0x02, // 46 + 0x00,0x03,0xE0,0x00,0x18, // 47 + 0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0xF0,0x01, // 48 + 0x00,0x00,0x20,0x00,0x10,0x00,0xF8,0x03, // 49 + 0x10,0x02,0x08,0x03,0x88,0x02,0x48,0x02,0x30,0x02, // 50 + 0x10,0x01,0x08,0x02,0x48,0x02,0x48,0x02,0xB0,0x01, // 51 + 0xC0,0x00,0xA0,0x00,0x90,0x00,0x88,0x00,0xF8,0x03,0x80, // 52 + 0x60,0x01,0x38,0x02,0x28,0x02,0x28,0x02,0xC8,0x01, // 53 + 0xF0,0x01,0x28,0x02,0x28,0x02,0x28,0x02,0xD0,0x01, // 54 + 0x08,0x00,0x08,0x03,0xC8,0x00,0x38,0x00,0x08, // 55 + 0xB0,0x01,0x48,0x02,0x48,0x02,0x48,0x02,0xB0,0x01, // 56 + 0x70,0x01,0x88,0x02,0x88,0x02,0x88,0x02,0xF0,0x01, // 57 + 0x00,0x00,0x20,0x02, // 58 + 0x00,0x00,0x20,0x06, // 59 + 0x00,0x00,0x40,0x00,0xA0,0x00,0xA0,0x00,0x10,0x01, // 60 + 0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0, // 61 + 0x00,0x00,0x10,0x01,0xA0,0x00,0xA0,0x00,0x40, // 62 + 0x10,0x00,0x08,0x00,0x08,0x00,0xC8,0x02,0x48,0x00,0x30, // 63 + 0x00,0x00,0xC0,0x03,0x30,0x04,0xD0,0x09,0x28,0x0A,0x28,0x0A,0xC8,0x0B,0x68,0x0A,0x10,0x05,0xE0,0x04, // 64 + 0x00,0x02,0xC0,0x01,0xB0,0x00,0x88,0x00,0xB0,0x00,0xC0,0x01,0x00,0x02, // 65 + 0x00,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02,0xF0,0x01, // 66 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0x10,0x01, // 67 + 0x00,0x00,0xF8,0x03,0x08,0x02,0x08,0x02,0x10,0x01,0xE0, // 68 + 0x00,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02,0x48,0x02, // 69 + 0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0x08, // 70 + 0x00,0x00,0xE0,0x00,0x10,0x01,0x08,0x02,0x48,0x02,0x50,0x01,0xC0, // 71 + 0x00,0x00,0xF8,0x03,0x40,0x00,0x40,0x00,0x40,0x00,0xF8,0x03, // 72 + 0x00,0x00,0xF8,0x03, // 73 + 0x00,0x03,0x00,0x02,0x00,0x02,0xF8,0x01, // 74 + 0x00,0x00,0xF8,0x03,0x80,0x00,0x60,0x00,0x90,0x00,0x08,0x01,0x00,0x02, // 75 + 0x00,0x00,0xF8,0x03,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02, // 76 + 0x00,0x00,0xF8,0x03,0x30,0x00,0xC0,0x01,0x00,0x02,0xC0,0x01,0x30,0x00,0xF8,0x03, // 77 + 0x00,0x00,0xF8,0x03,0x30,0x00,0x40,0x00,0x80,0x01,0xF8,0x03, // 78 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0x08,0x02,0xF0,0x01, // 79 + 0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0x48,0x00,0x30, // 80 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x03,0x08,0x03,0xF0,0x02, // 81 + 0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0xC8,0x00,0x30,0x03, // 82 + 0x00,0x00,0x30,0x01,0x48,0x02,0x48,0x02,0x48,0x02,0x90,0x01, // 83 + 0x00,0x00,0x08,0x00,0x08,0x00,0xF8,0x03,0x08,0x00,0x08, // 84 + 0x00,0x00,0xF8,0x01,0x00,0x02,0x00,0x02,0x00,0x02,0xF8,0x01, // 85 + 0x08,0x00,0x70,0x00,0x80,0x01,0x00,0x02,0x80,0x01,0x70,0x00,0x08, // 86 + 0x18,0x00,0xE0,0x01,0x00,0x02,0xF0,0x01,0x08,0x00,0xF0,0x01,0x00,0x02,0xE0,0x01,0x18, // 87 + 0x00,0x02,0x08,0x01,0x90,0x00,0x60,0x00,0x90,0x00,0x08,0x01,0x00,0x02, // 88 + 0x08,0x00,0x10,0x00,0x20,0x00,0xC0,0x03,0x20,0x00,0x10,0x00,0x08, // 89 + 0x08,0x03,0x88,0x02,0xC8,0x02,0x68,0x02,0x38,0x02,0x18,0x02, // 90 + 0x00,0x00,0xF8,0x0F,0x08,0x08, // 91 + 0x18,0x00,0xE0,0x00,0x00,0x03, // 92 + 0x08,0x08,0xF8,0x0F, // 93 + 0x40,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x40, // 94 + 0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08, // 95 + 0x08,0x00,0x10, // 96 + 0x00,0x00,0x00,0x03,0xA0,0x02,0xA0,0x02,0xE0,0x03, // 97 + 0x00,0x00,0xF8,0x03,0x20,0x02,0x20,0x02,0xC0,0x01, // 98 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0x40,0x01, // 99 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xF8,0x03, // 100 + 0x00,0x00,0xC0,0x01,0xA0,0x02,0xA0,0x02,0xC0,0x02, // 101 + 0x20,0x00,0xF0,0x03,0x28, // 102 + 0x00,0x00,0xC0,0x05,0x20,0x0A,0x20,0x0A,0xE0,0x07, // 103 + 0x00,0x00,0xF8,0x03,0x20,0x00,0x20,0x00,0xC0,0x03, // 104 + 0x00,0x00,0xE8,0x03, // 105 + 0x00,0x08,0xE8,0x07, // 106 + 0xF8,0x03,0x80,0x00,0xC0,0x01,0x20,0x02, // 107 + 0x00,0x00,0xF8,0x03, // 108 + 0x00,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xC0,0x03, // 109 + 0x00,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xC0,0x03, // 110 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xC0,0x01, // 111 + 0x00,0x00,0xE0,0x0F,0x20,0x02,0x20,0x02,0xC0,0x01, // 112 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xE0,0x0F, // 113 + 0x00,0x00,0xE0,0x03,0x20, // 114 + 0x40,0x02,0xA0,0x02,0xA0,0x02,0x20,0x01, // 115 + 0x20,0x00,0xF8,0x03,0x20,0x02, // 116 + 0x00,0x00,0xE0,0x01,0x00,0x02,0x00,0x02,0xE0,0x03, // 117 + 0x20,0x00,0xC0,0x01,0x00,0x02,0xC0,0x01,0x20, // 118 + 0xE0,0x01,0x00,0x02,0xC0,0x01,0x20,0x00,0xC0,0x01,0x00,0x02,0xE0,0x01, // 119 + 0x20,0x02,0x40,0x01,0x80,0x00,0x40,0x01,0x20,0x02, // 120 + 0x20,0x00,0xC0,0x09,0x00,0x06,0xC0,0x01,0x20, // 121 + 0x20,0x02,0x20,0x03,0xA0,0x02,0x60,0x02,0x20,0x02, // 122 + 0x80,0x00,0x78,0x0F,0x08,0x08, // 123 + 0x00,0x00,0xF8,0x0F, // 124 + 0x08,0x08,0x78,0x0F,0x80, // 125 + 0xC0,0x00,0x40,0x00,0xC0,0x00,0x80,0x00,0xC0, // 126 + 0x00,0x00,0xA0,0x0F, // 161 + 0x00,0x00,0xC0,0x01,0xA0,0x0F,0x78,0x02,0x40,0x01, // 162 + 0x40,0x02,0x70,0x03,0xC8,0x02,0x48,0x02,0x08,0x02,0x10,0x02, // 163 + 0x00,0x00,0xE0,0x01,0x20,0x01,0x20,0x01,0xE0,0x01, // 164 + 0x48,0x01,0x70,0x01,0xC0,0x03,0x70,0x01,0x48,0x01, // 165 + 0x00,0x00,0x38,0x0F, // 166 + 0xD0,0x04,0x28,0x09,0x48,0x09,0x48,0x0A,0x90,0x05, // 167 + 0x08,0x00,0x00,0x00,0x08, // 168 + 0xE0,0x00,0x10,0x01,0x48,0x02,0xA8,0x02,0xA8,0x02,0x10,0x01,0xE0, // 169 + 0x68,0x00,0x68,0x00,0x68,0x00,0x78, // 170 + 0x00,0x00,0x80,0x01,0x40,0x02,0x80,0x01,0x40,0x02, // 171 + 0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0xE0, // 172 + 0x80,0x00,0x80, // 173 + 0xE0,0x00,0x10,0x01,0xE8,0x02,0x68,0x02,0xC8,0x02,0x10,0x01,0xE0, // 174 + 0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02, // 175 + 0x00,0x00,0x38,0x00,0x28,0x00,0x38, // 176 + 0x40,0x02,0x40,0x02,0xF0,0x03,0x40,0x02,0x40,0x02, // 177 + 0x48,0x00,0x68,0x00,0x58, // 178 + 0x48,0x00,0x58,0x00,0x68, // 179 + 0x00,0x00,0x10,0x00,0x08, // 180 + 0x00,0x00,0xE0,0x0F,0x00,0x02,0x00,0x02,0xE0,0x03, // 181 + 0x70,0x00,0xF8,0x0F,0x08,0x00,0xF8,0x0F,0x08, // 182 + 0x00,0x00,0x40, // 183 + 0x00,0x00,0x00,0x14,0x00,0x18, // 184 + 0x00,0x00,0x10,0x00,0x78, // 185 + 0x30,0x00,0x48,0x00,0x48,0x00,0x30, // 186 + 0x00,0x00,0x40,0x02,0x80,0x01,0x40,0x02,0x80,0x01, // 187 + 0x00,0x00,0x10,0x02,0x78,0x01,0xC0,0x00,0x20,0x01,0x90,0x01,0xC8,0x03,0x00,0x01, // 188 + 0x00,0x00,0x10,0x02,0x78,0x01,0x80,0x00,0x60,0x00,0x50,0x02,0x48,0x03,0xC0,0x02, // 189 + 0x48,0x00,0x58,0x00,0x68,0x03,0x80,0x00,0x60,0x01,0x90,0x01,0xC8,0x03,0x00,0x01, // 190 + 0x00,0x00,0x00,0x06,0x00,0x09,0xA0,0x09,0x00,0x04, // 191 + 0x00,0x02,0xC0,0x01,0xB0,0x00,0x89,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 192 + 0x00,0x02,0xC0,0x01,0xB0,0x00,0x8A,0x00,0xB1,0x00,0xC0,0x01,0x00,0x02, // 193 + 0x00,0x02,0xC0,0x01,0xB2,0x00,0x89,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 194 + 0x00,0x02,0xC2,0x01,0xB1,0x00,0x8A,0x00,0xB1,0x00,0xC0,0x01,0x00,0x02, // 195 + 0x00,0x02,0xC0,0x01,0xB2,0x00,0x88,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 196 + 0x00,0x02,0xC0,0x01,0xBE,0x00,0x8A,0x00,0xBE,0x00,0xC0,0x01,0x00,0x02, // 197 + 0x00,0x03,0xC0,0x00,0xE0,0x00,0x98,0x00,0x88,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02, // 198 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x16,0x08,0x1A,0x10,0x01, // 199 + 0x00,0x00,0xF8,0x03,0x49,0x02,0x4A,0x02,0x48,0x02,0x48,0x02, // 200 + 0x00,0x00,0xF8,0x03,0x48,0x02,0x4A,0x02,0x49,0x02,0x48,0x02, // 201 + 0x00,0x00,0xFA,0x03,0x49,0x02,0x4A,0x02,0x48,0x02,0x48,0x02, // 202 + 0x00,0x00,0xF8,0x03,0x4A,0x02,0x48,0x02,0x4A,0x02,0x48,0x02, // 203 + 0x00,0x00,0xF9,0x03,0x02, // 204 + 0x02,0x00,0xF9,0x03, // 205 + 0x01,0x00,0xFA,0x03, // 206 + 0x02,0x00,0xF8,0x03,0x02, // 207 + 0x40,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x10,0x01,0xE0, // 208 + 0x00,0x00,0xFA,0x03,0x31,0x00,0x42,0x00,0x81,0x01,0xF8,0x03, // 209 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x09,0x02,0x0A,0x02,0x08,0x02,0xF0,0x01, // 210 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x0A,0x02,0x09,0x02,0x08,0x02,0xF0,0x01, // 211 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x0A,0x02,0x09,0x02,0x0A,0x02,0xF0,0x01, // 212 + 0x00,0x00,0xF0,0x01,0x0A,0x02,0x09,0x02,0x0A,0x02,0x09,0x02,0xF0,0x01, // 213 + 0x00,0x00,0xF0,0x01,0x0A,0x02,0x08,0x02,0x0A,0x02,0x08,0x02,0xF0,0x01, // 214 + 0x10,0x01,0xA0,0x00,0xE0,0x00,0xA0,0x00,0x10,0x01, // 215 + 0x00,0x00,0xF0,0x02,0x08,0x03,0xC8,0x02,0x28,0x02,0x18,0x03,0xE8, // 216 + 0x00,0x00,0xF8,0x01,0x01,0x02,0x02,0x02,0x00,0x02,0xF8,0x01, // 217 + 0x00,0x00,0xF8,0x01,0x02,0x02,0x01,0x02,0x00,0x02,0xF8,0x01, // 218 + 0x00,0x00,0xF8,0x01,0x02,0x02,0x01,0x02,0x02,0x02,0xF8,0x01, // 219 + 0x00,0x00,0xF8,0x01,0x02,0x02,0x00,0x02,0x02,0x02,0xF8,0x01, // 220 + 0x08,0x00,0x10,0x00,0x20,0x00,0xC2,0x03,0x21,0x00,0x10,0x00,0x08, // 221 + 0x00,0x00,0xF8,0x03,0x10,0x01,0x10,0x01,0x10,0x01,0xE0, // 222 + 0x00,0x00,0xF0,0x03,0x08,0x01,0x48,0x02,0xB0,0x02,0x80,0x01, // 223 + 0x00,0x00,0x00,0x03,0xA4,0x02,0xA8,0x02,0xE0,0x03, // 224 + 0x00,0x00,0x00,0x03,0xA8,0x02,0xA4,0x02,0xE0,0x03, // 225 + 0x00,0x00,0x00,0x03,0xA8,0x02,0xA4,0x02,0xE8,0x03, // 226 + 0x00,0x00,0x08,0x03,0xA4,0x02,0xA8,0x02,0xE4,0x03, // 227 + 0x00,0x00,0x00,0x03,0xA8,0x02,0xA0,0x02,0xE8,0x03, // 228 + 0x00,0x00,0x00,0x03,0xAE,0x02,0xAA,0x02,0xEE,0x03, // 229 + 0x00,0x00,0x40,0x03,0xA0,0x02,0xA0,0x02,0xC0,0x01,0xA0,0x02,0xA0,0x02,0xC0,0x02, // 230 + 0x00,0x00,0xC0,0x01,0x20,0x16,0x20,0x1A,0x40,0x01, // 231 + 0x00,0x00,0xC0,0x01,0xA4,0x02,0xA8,0x02,0xC0,0x02, // 232 + 0x00,0x00,0xC0,0x01,0xA8,0x02,0xA4,0x02,0xC0,0x02, // 233 + 0x00,0x00,0xC0,0x01,0xA8,0x02,0xA4,0x02,0xC8,0x02, // 234 + 0x00,0x00,0xC0,0x01,0xA8,0x02,0xA0,0x02,0xC8,0x02, // 235 + 0x00,0x00,0xE4,0x03,0x08, // 236 + 0x08,0x00,0xE4,0x03, // 237 + 0x08,0x00,0xE4,0x03,0x08, // 238 + 0x08,0x00,0xE0,0x03,0x08, // 239 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x38,0x02,0xE0,0x01, // 240 + 0x00,0x00,0xE8,0x03,0x24,0x00,0x28,0x00,0xC4,0x03, // 241 + 0x00,0x00,0xC0,0x01,0x24,0x02,0x28,0x02,0xC0,0x01, // 242 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x24,0x02,0xC0,0x01, // 243 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x24,0x02,0xC8,0x01, // 244 + 0x00,0x00,0xC8,0x01,0x24,0x02,0x28,0x02,0xC4,0x01, // 245 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x20,0x02,0xC8,0x01, // 246 + 0x40,0x00,0x40,0x00,0x50,0x01,0x40,0x00,0x40, // 247 + 0x00,0x00,0xC0,0x02,0xA0,0x03,0x60,0x02,0xA0,0x01, // 248 + 0x00,0x00,0xE0,0x01,0x04,0x02,0x08,0x02,0xE0,0x03, // 249 + 0x00,0x00,0xE0,0x01,0x08,0x02,0x04,0x02,0xE0,0x03, // 250 + 0x00,0x00,0xE8,0x01,0x04,0x02,0x08,0x02,0xE0,0x03, // 251 + 0x00,0x00,0xE0,0x01,0x08,0x02,0x00,0x02,0xE8,0x03, // 252 + 0x20,0x00,0xC0,0x09,0x08,0x06,0xC4,0x01,0x20, // 253 + 0x00,0x00,0xF8,0x0F,0x20,0x02,0x20,0x02,0xC0,0x01, // 254 + 0x20,0x00,0xC8,0x09,0x00,0x06,0xC8,0x01,0x20 // 255 +}; + +const char ArialMT_Plain_16[] PROGMEM = { + 0x10, // Width: 16 + 0x13, // Height: 19 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x04, // 32:65535 + 0x00, 0x00, 0x08, 0x04, // 33:0 + 0x00, 0x08, 0x0D, 0x06, // 34:8 + 0x00, 0x15, 0x1A, 0x09, // 35:21 + 0x00, 0x2F, 0x17, 0x09, // 36:47 + 0x00, 0x46, 0x26, 0x0E, // 37:70 + 0x00, 0x6C, 0x1D, 0x0B, // 38:108 + 0x00, 0x89, 0x04, 0x03, // 39:137 + 0x00, 0x8D, 0x0C, 0x05, // 40:141 + 0x00, 0x99, 0x0B, 0x05, // 41:153 + 0x00, 0xA4, 0x0D, 0x06, // 42:164 + 0x00, 0xB1, 0x17, 0x09, // 43:177 + 0x00, 0xC8, 0x09, 0x04, // 44:200 + 0x00, 0xD1, 0x0B, 0x05, // 45:209 + 0x00, 0xDC, 0x08, 0x04, // 46:220 + 0x00, 0xE4, 0x0A, 0x04, // 47:228 + 0x00, 0xEE, 0x17, 0x09, // 48:238 + 0x01, 0x05, 0x11, 0x09, // 49:261 + 0x01, 0x16, 0x17, 0x09, // 50:278 + 0x01, 0x2D, 0x17, 0x09, // 51:301 + 0x01, 0x44, 0x17, 0x09, // 52:324 + 0x01, 0x5B, 0x17, 0x09, // 53:347 + 0x01, 0x72, 0x17, 0x09, // 54:370 + 0x01, 0x89, 0x16, 0x09, // 55:393 + 0x01, 0x9F, 0x17, 0x09, // 56:415 + 0x01, 0xB6, 0x17, 0x09, // 57:438 + 0x01, 0xCD, 0x05, 0x04, // 58:461 + 0x01, 0xD2, 0x06, 0x04, // 59:466 + 0x01, 0xD8, 0x17, 0x09, // 60:472 + 0x01, 0xEF, 0x17, 0x09, // 61:495 + 0x02, 0x06, 0x17, 0x09, // 62:518 + 0x02, 0x1D, 0x16, 0x09, // 63:541 + 0x02, 0x33, 0x2F, 0x10, // 64:563 + 0x02, 0x62, 0x1D, 0x0B, // 65:610 + 0x02, 0x7F, 0x1D, 0x0B, // 66:639 + 0x02, 0x9C, 0x20, 0x0C, // 67:668 + 0x02, 0xBC, 0x20, 0x0C, // 68:700 + 0x02, 0xDC, 0x1D, 0x0B, // 69:732 + 0x02, 0xF9, 0x19, 0x0A, // 70:761 + 0x03, 0x12, 0x20, 0x0C, // 71:786 + 0x03, 0x32, 0x1D, 0x0C, // 72:818 + 0x03, 0x4F, 0x05, 0x04, // 73:847 + 0x03, 0x54, 0x14, 0x08, // 74:852 + 0x03, 0x68, 0x1D, 0x0B, // 75:872 + 0x03, 0x85, 0x17, 0x09, // 76:901 + 0x03, 0x9C, 0x23, 0x0D, // 77:924 + 0x03, 0xBF, 0x1D, 0x0C, // 78:959 + 0x03, 0xDC, 0x20, 0x0C, // 79:988 + 0x03, 0xFC, 0x1C, 0x0B, // 80:1020 + 0x04, 0x18, 0x20, 0x0C, // 81:1048 + 0x04, 0x38, 0x1D, 0x0C, // 82:1080 + 0x04, 0x55, 0x1D, 0x0B, // 83:1109 + 0x04, 0x72, 0x19, 0x0A, // 84:1138 + 0x04, 0x8B, 0x1D, 0x0C, // 85:1163 + 0x04, 0xA8, 0x1C, 0x0B, // 86:1192 + 0x04, 0xC4, 0x2B, 0x0F, // 87:1220 + 0x04, 0xEF, 0x20, 0x0B, // 88:1263 + 0x05, 0x0F, 0x19, 0x0B, // 89:1295 + 0x05, 0x28, 0x1A, 0x0A, // 90:1320 + 0x05, 0x42, 0x0C, 0x04, // 91:1346 + 0x05, 0x4E, 0x0B, 0x04, // 92:1358 + 0x05, 0x59, 0x09, 0x04, // 93:1369 + 0x05, 0x62, 0x14, 0x08, // 94:1378 + 0x05, 0x76, 0x1B, 0x09, // 95:1398 + 0x05, 0x91, 0x07, 0x05, // 96:1425 + 0x05, 0x98, 0x17, 0x09, // 97:1432 + 0x05, 0xAF, 0x17, 0x09, // 98:1455 + 0x05, 0xC6, 0x14, 0x08, // 99:1478 + 0x05, 0xDA, 0x17, 0x09, // 100:1498 + 0x05, 0xF1, 0x17, 0x09, // 101:1521 + 0x06, 0x08, 0x0A, 0x04, // 102:1544 + 0x06, 0x12, 0x17, 0x09, // 103:1554 + 0x06, 0x29, 0x14, 0x09, // 104:1577 + 0x06, 0x3D, 0x05, 0x04, // 105:1597 + 0x06, 0x42, 0x06, 0x04, // 106:1602 + 0x06, 0x48, 0x17, 0x08, // 107:1608 + 0x06, 0x5F, 0x05, 0x04, // 108:1631 + 0x06, 0x64, 0x23, 0x0D, // 109:1636 + 0x06, 0x87, 0x14, 0x09, // 110:1671 + 0x06, 0x9B, 0x17, 0x09, // 111:1691 + 0x06, 0xB2, 0x17, 0x09, // 112:1714 + 0x06, 0xC9, 0x18, 0x09, // 113:1737 + 0x06, 0xE1, 0x0D, 0x05, // 114:1761 + 0x06, 0xEE, 0x14, 0x08, // 115:1774 + 0x07, 0x02, 0x0B, 0x04, // 116:1794 + 0x07, 0x0D, 0x14, 0x09, // 117:1805 + 0x07, 0x21, 0x13, 0x08, // 118:1825 + 0x07, 0x34, 0x1F, 0x0C, // 119:1844 + 0x07, 0x53, 0x14, 0x08, // 120:1875 + 0x07, 0x67, 0x13, 0x08, // 121:1895 + 0x07, 0x7A, 0x14, 0x08, // 122:1914 + 0x07, 0x8E, 0x0F, 0x05, // 123:1934 + 0x07, 0x9D, 0x06, 0x04, // 124:1949 + 0x07, 0xA3, 0x0E, 0x05, // 125:1955 + 0x07, 0xB1, 0x17, 0x09, // 126:1969 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x10, // 128:65535 + 0xFF, 0xFF, 0x00, 0x10, // 129:65535 + 0xFF, 0xFF, 0x00, 0x10, // 130:65535 + 0xFF, 0xFF, 0x00, 0x10, // 131:65535 + 0xFF, 0xFF, 0x00, 0x10, // 132:65535 + 0xFF, 0xFF, 0x00, 0x10, // 133:65535 + 0xFF, 0xFF, 0x00, 0x10, // 134:65535 + 0xFF, 0xFF, 0x00, 0x10, // 135:65535 + 0xFF, 0xFF, 0x00, 0x10, // 136:65535 + 0xFF, 0xFF, 0x00, 0x10, // 137:65535 + 0xFF, 0xFF, 0x00, 0x10, // 138:65535 + 0xFF, 0xFF, 0x00, 0x10, // 139:65535 + 0xFF, 0xFF, 0x00, 0x10, // 140:65535 + 0xFF, 0xFF, 0x00, 0x10, // 141:65535 + 0xFF, 0xFF, 0x00, 0x10, // 142:65535 + 0xFF, 0xFF, 0x00, 0x10, // 143:65535 + 0xFF, 0xFF, 0x00, 0x10, // 144:65535 + 0xFF, 0xFF, 0x00, 0x10, // 145:65535 + 0xFF, 0xFF, 0x00, 0x10, // 146:65535 + 0xFF, 0xFF, 0x00, 0x10, // 147:65535 + 0xFF, 0xFF, 0x00, 0x10, // 148:65535 + 0xFF, 0xFF, 0x00, 0x10, // 149:65535 + 0xFF, 0xFF, 0x00, 0x10, // 150:65535 + 0xFF, 0xFF, 0x00, 0x10, // 151:65535 + 0xFF, 0xFF, 0x00, 0x10, // 152:65535 + 0xFF, 0xFF, 0x00, 0x10, // 153:65535 + 0xFF, 0xFF, 0x00, 0x10, // 154:65535 + 0xFF, 0xFF, 0x00, 0x10, // 155:65535 + 0xFF, 0xFF, 0x00, 0x10, // 156:65535 + 0xFF, 0xFF, 0x00, 0x10, // 157:65535 + 0xFF, 0xFF, 0x00, 0x10, // 158:65535 + 0xFF, 0xFF, 0x00, 0x10, // 159:65535 + 0xFF, 0xFF, 0x00, 0x04, // 160:65535 + 0x07, 0xC8, 0x09, 0x05, // 161:1992 + 0x07, 0xD1, 0x17, 0x09, // 162:2001 + 0x07, 0xE8, 0x17, 0x09, // 163:2024 + 0x07, 0xFF, 0x14, 0x09, // 164:2047 + 0x08, 0x13, 0x1A, 0x09, // 165:2067 + 0x08, 0x2D, 0x06, 0x04, // 166:2093 + 0x08, 0x33, 0x17, 0x09, // 167:2099 + 0x08, 0x4A, 0x07, 0x05, // 168:2122 + 0x08, 0x51, 0x23, 0x0C, // 169:2129 + 0x08, 0x74, 0x0E, 0x06, // 170:2164 + 0x08, 0x82, 0x14, 0x09, // 171:2178 + 0x08, 0x96, 0x17, 0x09, // 172:2198 + 0x08, 0xAD, 0x0B, 0x05, // 173:2221 + 0x08, 0xB8, 0x23, 0x0C, // 174:2232 + 0x08, 0xDB, 0x19, 0x09, // 175:2267 + 0x08, 0xF4, 0x0D, 0x06, // 176:2292 + 0x09, 0x01, 0x17, 0x09, // 177:2305 + 0x09, 0x18, 0x0E, 0x05, // 178:2328 + 0x09, 0x26, 0x0D, 0x05, // 179:2342 + 0x09, 0x33, 0x0A, 0x05, // 180:2355 + 0x09, 0x3D, 0x17, 0x09, // 181:2365 + 0x09, 0x54, 0x19, 0x09, // 182:2388 + 0x09, 0x6D, 0x08, 0x05, // 183:2413 + 0x09, 0x75, 0x0C, 0x05, // 184:2421 + 0x09, 0x81, 0x0B, 0x05, // 185:2433 + 0x09, 0x8C, 0x0D, 0x06, // 186:2444 + 0x09, 0x99, 0x17, 0x09, // 187:2457 + 0x09, 0xB0, 0x26, 0x0D, // 188:2480 + 0x09, 0xD6, 0x26, 0x0D, // 189:2518 + 0x09, 0xFC, 0x26, 0x0D, // 190:2556 + 0x0A, 0x22, 0x1A, 0x0A, // 191:2594 + 0x0A, 0x3C, 0x1D, 0x0B, // 192:2620 + 0x0A, 0x59, 0x1D, 0x0B, // 193:2649 + 0x0A, 0x76, 0x1D, 0x0B, // 194:2678 + 0x0A, 0x93, 0x1D, 0x0B, // 195:2707 + 0x0A, 0xB0, 0x1D, 0x0B, // 196:2736 + 0x0A, 0xCD, 0x1D, 0x0B, // 197:2765 + 0x0A, 0xEA, 0x2C, 0x10, // 198:2794 + 0x0B, 0x16, 0x20, 0x0C, // 199:2838 + 0x0B, 0x36, 0x1D, 0x0B, // 200:2870 + 0x0B, 0x53, 0x1D, 0x0B, // 201:2899 + 0x0B, 0x70, 0x1D, 0x0B, // 202:2928 + 0x0B, 0x8D, 0x1D, 0x0B, // 203:2957 + 0x0B, 0xAA, 0x05, 0x04, // 204:2986 + 0x0B, 0xAF, 0x07, 0x04, // 205:2991 + 0x0B, 0xB6, 0x0A, 0x04, // 206:2998 + 0x0B, 0xC0, 0x07, 0x04, // 207:3008 + 0x0B, 0xC7, 0x20, 0x0C, // 208:3015 + 0x0B, 0xE7, 0x1D, 0x0C, // 209:3047 + 0x0C, 0x04, 0x20, 0x0C, // 210:3076 + 0x0C, 0x24, 0x20, 0x0C, // 211:3108 + 0x0C, 0x44, 0x20, 0x0C, // 212:3140 + 0x0C, 0x64, 0x20, 0x0C, // 213:3172 + 0x0C, 0x84, 0x20, 0x0C, // 214:3204 + 0x0C, 0xA4, 0x17, 0x09, // 215:3236 + 0x0C, 0xBB, 0x20, 0x0C, // 216:3259 + 0x0C, 0xDB, 0x1D, 0x0C, // 217:3291 + 0x0C, 0xF8, 0x1D, 0x0C, // 218:3320 + 0x0D, 0x15, 0x1D, 0x0C, // 219:3349 + 0x0D, 0x32, 0x1D, 0x0C, // 220:3378 + 0x0D, 0x4F, 0x19, 0x0B, // 221:3407 + 0x0D, 0x68, 0x1D, 0x0B, // 222:3432 + 0x0D, 0x85, 0x17, 0x0A, // 223:3461 + 0x0D, 0x9C, 0x17, 0x09, // 224:3484 + 0x0D, 0xB3, 0x17, 0x09, // 225:3507 + 0x0D, 0xCA, 0x17, 0x09, // 226:3530 + 0x0D, 0xE1, 0x17, 0x09, // 227:3553 + 0x0D, 0xF8, 0x17, 0x09, // 228:3576 + 0x0E, 0x0F, 0x17, 0x09, // 229:3599 + 0x0E, 0x26, 0x29, 0x0E, // 230:3622 + 0x0E, 0x4F, 0x14, 0x08, // 231:3663 + 0x0E, 0x63, 0x17, 0x09, // 232:3683 + 0x0E, 0x7A, 0x17, 0x09, // 233:3706 + 0x0E, 0x91, 0x17, 0x09, // 234:3729 + 0x0E, 0xA8, 0x17, 0x09, // 235:3752 + 0x0E, 0xBF, 0x05, 0x04, // 236:3775 + 0x0E, 0xC4, 0x07, 0x04, // 237:3780 + 0x0E, 0xCB, 0x0A, 0x04, // 238:3787 + 0x0E, 0xD5, 0x07, 0x04, // 239:3797 + 0x0E, 0xDC, 0x17, 0x09, // 240:3804 + 0x0E, 0xF3, 0x14, 0x09, // 241:3827 + 0x0F, 0x07, 0x17, 0x09, // 242:3847 + 0x0F, 0x1E, 0x17, 0x09, // 243:3870 + 0x0F, 0x35, 0x17, 0x09, // 244:3893 + 0x0F, 0x4C, 0x17, 0x09, // 245:3916 + 0x0F, 0x63, 0x17, 0x09, // 246:3939 + 0x0F, 0x7A, 0x17, 0x09, // 247:3962 + 0x0F, 0x91, 0x17, 0x0A, // 248:3985 + 0x0F, 0xA8, 0x14, 0x09, // 249:4008 + 0x0F, 0xBC, 0x14, 0x09, // 250:4028 + 0x0F, 0xD0, 0x14, 0x09, // 251:4048 + 0x0F, 0xE4, 0x14, 0x09, // 252:4068 + 0x0F, 0xF8, 0x13, 0x08, // 253:4088 + 0x10, 0x0B, 0x17, 0x09, // 254:4107 + 0x10, 0x22, 0x13, 0x08, // 255:4130 + + // Font Data: + 0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x5F, // 33 + 0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78, // 34 + 0x80,0x08,0x00,0x80,0x78,0x00,0xC0,0x0F,0x00,0xB8,0x08,0x00,0x80,0x08,0x00,0x80,0x78,0x00,0xC0,0x0F,0x00,0xB8,0x08,0x00,0x80,0x08, // 35 + 0x00,0x00,0x00,0xE0,0x10,0x00,0x10,0x21,0x00,0x08,0x41,0x00,0xFC,0xFF,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x1C, // 36 + 0x00,0x00,0x00,0xF0,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x08,0x61,0x00,0xF0,0x18,0x00,0x00,0x06,0x00,0xC0,0x01,0x00,0x30,0x3C,0x00,0x08,0x42,0x00,0x00,0x42,0x00,0x00,0x42,0x00,0x00,0x3C, // 37 + 0x00,0x00,0x00,0x00,0x1C,0x00,0x70,0x22,0x00,0x88,0x41,0x00,0x08,0x43,0x00,0x88,0x44,0x00,0x70,0x28,0x00,0x00,0x10,0x00,0x00,0x28,0x00,0x00,0x44, // 38 + 0x00,0x00,0x00,0x78, // 39 + 0x00,0x00,0x00,0x80,0x3F,0x00,0x70,0xC0,0x01,0x08,0x00,0x02, // 40 + 0x00,0x00,0x00,0x08,0x00,0x02,0x70,0xC0,0x01,0x80,0x3F, // 41 + 0x10,0x00,0x00,0xD0,0x00,0x00,0x38,0x00,0x00,0xD0,0x00,0x00,0x10, // 42 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0xC0,0x1F,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 43 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x01, // 44 + 0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 45 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40, // 46 + 0x00,0x60,0x00,0x00,0x1E,0x00,0xE0,0x01,0x00,0x18, // 47 + 0x00,0x00,0x00,0xE0,0x1F,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0xE0,0x1F, // 48 + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0xF8,0x7F, // 49 + 0x00,0x00,0x00,0x20,0x40,0x00,0x10,0x60,0x00,0x08,0x50,0x00,0x08,0x48,0x00,0x08,0x44,0x00,0x10,0x43,0x00,0xE0,0x40, // 50 + 0x00,0x00,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x88,0x41,0x00,0xF0,0x22,0x00,0x00,0x1C, // 51 + 0x00,0x0C,0x00,0x00,0x0A,0x00,0x00,0x09,0x00,0xC0,0x08,0x00,0x20,0x08,0x00,0x10,0x08,0x00,0xF8,0x7F,0x00,0x00,0x08, // 52 + 0x00,0x00,0x00,0xC0,0x11,0x00,0xB8,0x20,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x08,0x21,0x00,0x08,0x1E, // 53 + 0x00,0x00,0x00,0xE0,0x1F,0x00,0x10,0x21,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x10,0x21,0x00,0x20,0x1E, // 54 + 0x00,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x78,0x00,0x08,0x07,0x00,0xC8,0x00,0x00,0x28,0x00,0x00,0x18, // 55 + 0x00,0x00,0x00,0x60,0x1C,0x00,0x90,0x22,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x90,0x22,0x00,0x60,0x1C, // 56 + 0x00,0x00,0x00,0xE0,0x11,0x00,0x10,0x22,0x00,0x08,0x44,0x00,0x08,0x44,0x00,0x08,0x44,0x00,0x10,0x22,0x00,0xE0,0x1F, // 57 + 0x00,0x00,0x00,0x40,0x40, // 58 + 0x00,0x00,0x00,0x40,0xC0,0x01, // 59 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x05,0x00,0x00,0x05,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x40,0x10, // 60 + 0x00,0x00,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08, // 61 + 0x00,0x00,0x00,0x40,0x10,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x00,0x05,0x00,0x00,0x05,0x00,0x00,0x02, // 62 + 0x00,0x00,0x00,0x60,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0x08,0x5C,0x00,0x08,0x02,0x00,0x10,0x01,0x00,0xE0, // 63 + 0x00,0x00,0x00,0x00,0x3F,0x00,0xC0,0x40,0x00,0x20,0x80,0x00,0x10,0x1E,0x01,0x10,0x21,0x01,0x88,0x40,0x02,0x48,0x40,0x02,0x48,0x40,0x02,0x48,0x20,0x02,0x88,0x7C,0x02,0xC8,0x43,0x02,0x10,0x40,0x02,0x10,0x20,0x01,0x60,0x10,0x01,0x80,0x8F, // 64 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x70,0x04,0x00,0x08,0x04,0x00,0x70,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 65 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x90,0x22,0x00,0x60,0x1C, // 66 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10, // 67 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 68 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 69 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08, // 70 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x12,0x00,0x00,0x0E, // 71 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0xF8,0x7F, // 72 + 0x00,0x00,0x00,0xF8,0x7F, // 73 + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0xF8,0x3F, // 74 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x04,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0x80,0x03,0x00,0x40,0x04,0x00,0x20,0x18,0x00,0x10,0x20,0x00,0x08,0x40, // 75 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40, // 76 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x30,0x00,0x00,0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0,0x00,0x00,0x30,0x00,0x00,0xF8,0x7F, // 77 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x10,0x00,0x00,0x60,0x00,0x00,0x80,0x00,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x18,0x00,0x00,0x20,0x00,0xF8,0x7F, // 78 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 79 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x10,0x01,0x00,0xE0, // 80 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x50,0x00,0x08,0x50,0x00,0x10,0x20,0x00,0x20,0x70,0x00,0xC0,0x4F, // 81 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x06,0x00,0x08,0x1A,0x00,0x10,0x21,0x00,0xE0,0x40, // 82 + 0x00,0x00,0x00,0x60,0x10,0x00,0x90,0x20,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x1C, // 83 + 0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0xF8,0x7F,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 84 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 85 + 0x00,0x00,0x00,0x18,0x00,0x00,0xE0,0x00,0x00,0x00,0x07,0x00,0x00,0x18,0x00,0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x07,0x00,0xE0,0x00,0x00,0x18, // 86 + 0x18,0x00,0x00,0xE0,0x01,0x00,0x00,0x1E,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x03,0x00,0x70,0x00,0x00,0x08,0x00,0x00,0x70,0x00,0x00,0x80,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1E,0x00,0xE0,0x01,0x00,0x18, // 87 + 0x00,0x40,0x00,0x08,0x20,0x00,0x10,0x10,0x00,0x60,0x0C,0x00,0x80,0x02,0x00,0x00,0x01,0x00,0x80,0x02,0x00,0x60,0x0C,0x00,0x10,0x10,0x00,0x08,0x20,0x00,0x00,0x40, // 88 + 0x08,0x00,0x00,0x30,0x00,0x00,0x40,0x00,0x00,0x80,0x01,0x00,0x00,0x7E,0x00,0x80,0x01,0x00,0x40,0x00,0x00,0x30,0x00,0x00,0x08, // 89 + 0x00,0x40,0x00,0x08,0x60,0x00,0x08,0x58,0x00,0x08,0x44,0x00,0x08,0x43,0x00,0x88,0x40,0x00,0x68,0x40,0x00,0x18,0x40,0x00,0x08,0x40, // 90 + 0x00,0x00,0x00,0xF8,0xFF,0x03,0x08,0x00,0x02,0x08,0x00,0x02, // 91 + 0x18,0x00,0x00,0xE0,0x01,0x00,0x00,0x1E,0x00,0x00,0x60, // 92 + 0x08,0x00,0x02,0x08,0x00,0x02,0xF8,0xFF,0x03, // 93 + 0x00,0x01,0x00,0xC0,0x00,0x00,0x30,0x00,0x00,0x08,0x00,0x00,0x30,0x00,0x00,0xC0,0x00,0x00,0x00,0x01, // 94 + 0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 95 + 0x00,0x00,0x00,0x08,0x00,0x00,0x10, // 96 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 97 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 98 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20, // 99 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0xF8,0x7F, // 100 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 101 + 0x40,0x00,0x00,0xF0,0x7F,0x00,0x48,0x00,0x00,0x48, // 102 + 0x00,0x00,0x00,0x00,0x1F,0x01,0x80,0x20,0x02,0x40,0x40,0x02,0x40,0x40,0x02,0x40,0x40,0x02,0x80,0x20,0x01,0xC0,0xFF, // 103 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 104 + 0x00,0x00,0x00,0xC8,0x7F, // 105 + 0x00,0x00,0x02,0xC8,0xFF,0x01, // 106 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x06,0x00,0x00,0x19,0x00,0x80,0x20,0x00,0x40,0x40, // 107 + 0x00,0x00,0x00,0xF8,0x7F, // 108 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 109 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 110 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 111 + 0x00,0x00,0x00,0xC0,0xFF,0x03,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 112 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0xC0,0xFF,0x03, // 113 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40, // 114 + 0x00,0x00,0x00,0x80,0x23,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x38, // 115 + 0x40,0x00,0x00,0xF0,0x7F,0x00,0x40,0x40,0x00,0x40,0x40, // 116 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 117 + 0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0, // 118 + 0xC0,0x00,0x00,0x00,0x1F,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1F,0x00,0xC0, // 119 + 0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20,0x00,0x40,0x40, // 120 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x00,0x38,0x02,0x00,0xE0,0x01,0x00,0x38,0x00,0x00,0x07,0x00,0xC0, // 121 + 0x40,0x40,0x00,0x40,0x60,0x00,0x40,0x58,0x00,0x40,0x44,0x00,0x40,0x43,0x00,0xC0,0x40,0x00,0x40,0x40, // 122 + 0x00,0x04,0x00,0x00,0x04,0x00,0xF0,0xFB,0x01,0x08,0x00,0x02,0x08,0x00,0x02, // 123 + 0x00,0x00,0x00,0xF8,0xFF,0x03, // 124 + 0x08,0x00,0x02,0x08,0x00,0x02,0xF0,0xFB,0x01,0x00,0x04,0x00,0x00,0x04, // 125 + 0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x01, // 126 + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0xFF,0x03, // 161 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x03,0x40,0xF0,0x00,0x40,0x4E,0x00,0xC0,0x41,0x00,0xB8,0x20,0x00,0x00,0x11, // 162 + 0x00,0x41,0x00,0xE0,0x31,0x00,0x10,0x2F,0x00,0x08,0x21,0x00,0x08,0x21,0x00,0x08,0x40,0x00,0x10,0x40,0x00,0x20,0x20, // 163 + 0x00,0x00,0x00,0x40,0x0B,0x00,0x80,0x04,0x00,0x40,0x08,0x00,0x40,0x08,0x00,0x80,0x04,0x00,0x40,0x0B, // 164 + 0x08,0x0A,0x00,0x10,0x0A,0x00,0x60,0x0A,0x00,0x80,0x0B,0x00,0x00,0x7E,0x00,0x80,0x0B,0x00,0x60,0x0A,0x00,0x10,0x0A,0x00,0x08,0x0A, // 165 + 0x00,0x00,0x00,0xF8,0xF1,0x03, // 166 + 0x00,0x86,0x00,0x70,0x09,0x01,0xC8,0x10,0x02,0x88,0x10,0x02,0x08,0x21,0x02,0x08,0x61,0x02,0x30,0xD2,0x01,0x00,0x0C, // 167 + 0x08,0x00,0x00,0x00,0x00,0x00,0x08, // 168 + 0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0xC8,0x47,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x48,0x44,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 169 + 0xD0,0x00,0x00,0x48,0x01,0x00,0x28,0x01,0x00,0x28,0x01,0x00,0xF0,0x01, // 170 + 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20, // 171 + 0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x0F, // 172 + 0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 173 + 0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0xE8,0x4F,0x00,0x28,0x41,0x00,0x28,0x41,0x00,0x28,0x43,0x00,0x28,0x45,0x00,0xC8,0x48,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 174 + 0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04, // 175 + 0x00,0x00,0x00,0x30,0x00,0x00,0x48,0x00,0x00,0x48,0x00,0x00,0x30, // 176 + 0x00,0x00,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0xE0,0x4F,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0x00,0x41, // 177 + 0x10,0x01,0x00,0x88,0x01,0x00,0x48,0x01,0x00,0x48,0x01,0x00,0x30,0x01, // 178 + 0x90,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x28,0x01,0x00,0xD8, // 179 + 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x08, // 180 + 0x00,0x00,0x00,0xC0,0xFF,0x03,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 181 + 0xF0,0x00,0x00,0xF8,0x00,0x00,0xF8,0x01,0x00,0xF8,0x01,0x00,0xF8,0xFF,0x03,0x08,0x00,0x00,0x08,0x00,0x00,0xF8,0xFF,0x03,0x08, // 182 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02, // 183 + 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x80,0x02,0x00,0x00,0x03, // 184 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0xF8,0x01, // 185 + 0xF0,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0xF0, // 186 + 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04, // 187 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x40,0x00,0xF8,0x21,0x00,0x00,0x10,0x00,0x00,0x0C,0x00,0x00,0x02,0x00,0x80,0x01,0x00,0x40,0x30,0x00,0x30,0x28,0x00,0x08,0x24,0x00,0x00,0x7E,0x00,0x00,0x20, // 188 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x40,0x00,0xF8,0x31,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x60,0x44,0x00,0x10,0x62,0x00,0x08,0x52,0x00,0x00,0x52,0x00,0x00,0x4C, // 189 + 0x90,0x00,0x00,0x08,0x01,0x00,0x08,0x41,0x00,0x28,0x21,0x00,0xD8,0x18,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x40,0x30,0x00,0x30,0x28,0x00,0x08,0x24,0x00,0x00,0x7E,0x00,0x00,0x20, // 190 + 0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x10,0x01,0x00,0x08,0x02,0x40,0x07,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0xC0, // 191 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x71,0x04,0x00,0x0A,0x04,0x00,0x70,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 192 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x70,0x04,0x00,0x0A,0x04,0x00,0x71,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 193 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x09,0x04,0x00,0x71,0x04,0x00,0x82,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 194 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x09,0x04,0x00,0x72,0x04,0x00,0x81,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 195 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x08,0x04,0x00,0x72,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 196 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x7E,0x04,0x00,0x0A,0x04,0x00,0x7E,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 197 + 0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x06,0x00,0x80,0x05,0x00,0x60,0x04,0x00,0x18,0x04,0x00,0x08,0x04,0x00,0x08,0x04,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41, // 198 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x02,0x08,0xC0,0x02,0x08,0x40,0x03,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10, // 199 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x09,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 200 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x09,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 201 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x09,0x41,0x00,0x09,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 202 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 203 + 0x01,0x00,0x00,0xFA,0x7F, // 204 + 0x00,0x00,0x00,0xFA,0x7F,0x00,0x01, // 205 + 0x02,0x00,0x00,0xF9,0x7F,0x00,0x01,0x00,0x00,0x02, // 206 + 0x02,0x00,0x00,0xF8,0x7F,0x00,0x02, // 207 + 0x00,0x02,0x00,0xF8,0x7F,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 208 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x10,0x00,0x00,0x60,0x00,0x00,0x82,0x00,0x00,0x01,0x03,0x00,0x02,0x04,0x00,0x01,0x18,0x00,0x00,0x20,0x00,0xF8,0x7F, // 209 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 210 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 211 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 212 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 213 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 214 + 0x00,0x00,0x00,0x40,0x10,0x00,0x80,0x08,0x00,0x00,0x05,0x00,0x00,0x07,0x00,0x00,0x05,0x00,0x80,0x08,0x00,0x40,0x10, // 215 + 0x00,0x00,0x00,0xC0,0x4F,0x00,0x20,0x30,0x00,0x10,0x30,0x00,0x08,0x4C,0x00,0x08,0x42,0x00,0x08,0x41,0x00,0xC8,0x40,0x00,0x30,0x20,0x00,0x30,0x10,0x00,0xC8,0x0F, // 216 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x01,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 217 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x01,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 218 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x01,0x40,0x00,0x01,0x40,0x00,0x02,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 219 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 220 + 0x08,0x00,0x00,0x30,0x00,0x00,0x40,0x00,0x00,0x80,0x01,0x00,0x02,0x7E,0x00,0x81,0x01,0x00,0x40,0x00,0x00,0x30,0x00,0x00,0x08, // 221 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x40,0x08,0x00,0x80,0x07, // 222 + 0x00,0x00,0x00,0xE0,0x7F,0x00,0x10,0x00,0x00,0x08,0x20,0x00,0x88,0x43,0x00,0x70,0x42,0x00,0x00,0x44,0x00,0x00,0x38, // 223 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x48,0x44,0x00,0x50,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 224 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x48,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 225 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x48,0x42,0x00,0x50,0x22,0x00,0x80,0x7F, // 226 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x50,0x42,0x00,0x48,0x22,0x00,0x80,0x7F, // 227 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x50,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 228 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x5C,0x44,0x00,0x54,0x44,0x00,0x5C,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 229 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x40,0x22,0x00,0x80,0x3F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 230 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x02,0x40,0xC0,0x02,0x40,0x40,0x03,0x80,0x20, // 231 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x48,0x44,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 232 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 233 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x48,0x44,0x00,0x90,0x24,0x00,0x00,0x17, // 234 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 235 + 0x08,0x00,0x00,0xD0,0x7F, // 236 + 0x00,0x00,0x00,0xD0,0x7F,0x00,0x08, // 237 + 0x10,0x00,0x00,0xC8,0x7F,0x00,0x08,0x00,0x00,0x10, // 238 + 0x10,0x00,0x00,0xC0,0x7F,0x00,0x10, // 239 + 0x00,0x00,0x00,0x00,0x1F,0x00,0xA0,0x20,0x00,0x68,0x40,0x00,0x58,0x40,0x00,0x70,0x40,0x00,0xE8,0x20,0x00,0x00,0x1F, // 240 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x90,0x00,0x00,0x48,0x00,0x00,0x50,0x00,0x00,0x48,0x00,0x00,0x80,0x7F, // 241 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x48,0x40,0x00,0x50,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 242 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 243 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x48,0x40,0x00,0x90,0x20,0x00,0x00,0x1F, // 244 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x50,0x40,0x00,0x88,0x20,0x00,0x00,0x1F, // 245 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x40,0x40,0x00,0x50,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 246 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x80,0x0A,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 247 + 0x00,0x00,0x00,0x00,0x5F,0x00,0x80,0x30,0x00,0x40,0x48,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x80,0x21,0x00,0x40,0x1F, // 248 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x08,0x40,0x00,0x10,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 249 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x10,0x40,0x00,0x08,0x20,0x00,0xC0,0x7F, // 250 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x10,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0xC0,0x7F, // 251 + 0x00,0x00,0x00,0xD0,0x3F,0x00,0x00,0x40,0x00,0x10,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 252 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x00,0x38,0x02,0x10,0xE0,0x01,0x08,0x38,0x00,0x00,0x07,0x00,0xC0, // 253 + 0x00,0x00,0x00,0xF8,0xFF,0x03,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 254 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x10,0x38,0x02,0x00,0xE0,0x01,0x10,0x38,0x00,0x00,0x07,0x00,0xC0 // 255 +}; +const char ArialMT_Plain_24[] PROGMEM = { + 0x18, // Width: 24 + 0x1C, // Height: 28 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x07, // 32:65535 + 0x00, 0x00, 0x13, 0x07, // 33:0 + 0x00, 0x13, 0x1A, 0x09, // 34:19 + 0x00, 0x2D, 0x33, 0x0D, // 35:45 + 0x00, 0x60, 0x2F, 0x0D, // 36:96 + 0x00, 0x8F, 0x4F, 0x15, // 37:143 + 0x00, 0xDE, 0x3B, 0x10, // 38:222 + 0x01, 0x19, 0x0A, 0x05, // 39:281 + 0x01, 0x23, 0x1C, 0x08, // 40:291 + 0x01, 0x3F, 0x1B, 0x08, // 41:319 + 0x01, 0x5A, 0x21, 0x09, // 42:346 + 0x01, 0x7B, 0x32, 0x0E, // 43:379 + 0x01, 0xAD, 0x10, 0x07, // 44:429 + 0x01, 0xBD, 0x1B, 0x08, // 45:445 + 0x01, 0xD8, 0x0F, 0x07, // 46:472 + 0x01, 0xE7, 0x19, 0x07, // 47:487 + 0x02, 0x00, 0x2F, 0x0D, // 48:512 + 0x02, 0x2F, 0x23, 0x0D, // 49:559 + 0x02, 0x52, 0x2F, 0x0D, // 50:594 + 0x02, 0x81, 0x2F, 0x0D, // 51:641 + 0x02, 0xB0, 0x2F, 0x0D, // 52:688 + 0x02, 0xDF, 0x2F, 0x0D, // 53:735 + 0x03, 0x0E, 0x2F, 0x0D, // 54:782 + 0x03, 0x3D, 0x2D, 0x0D, // 55:829 + 0x03, 0x6A, 0x2F, 0x0D, // 56:874 + 0x03, 0x99, 0x2F, 0x0D, // 57:921 + 0x03, 0xC8, 0x0F, 0x07, // 58:968 + 0x03, 0xD7, 0x10, 0x07, // 59:983 + 0x03, 0xE7, 0x2F, 0x0E, // 60:999 + 0x04, 0x16, 0x2F, 0x0E, // 61:1046 + 0x04, 0x45, 0x2E, 0x0E, // 62:1093 + 0x04, 0x73, 0x2E, 0x0D, // 63:1139 + 0x04, 0xA1, 0x5B, 0x18, // 64:1185 + 0x04, 0xFC, 0x3B, 0x10, // 65:1276 + 0x05, 0x37, 0x3B, 0x10, // 66:1335 + 0x05, 0x72, 0x3F, 0x11, // 67:1394 + 0x05, 0xB1, 0x3F, 0x11, // 68:1457 + 0x05, 0xF0, 0x3B, 0x10, // 69:1520 + 0x06, 0x2B, 0x35, 0x0F, // 70:1579 + 0x06, 0x60, 0x43, 0x13, // 71:1632 + 0x06, 0xA3, 0x3B, 0x11, // 72:1699 + 0x06, 0xDE, 0x0F, 0x07, // 73:1758 + 0x06, 0xED, 0x27, 0x0C, // 74:1773 + 0x07, 0x14, 0x3F, 0x10, // 75:1812 + 0x07, 0x53, 0x2F, 0x0D, // 76:1875 + 0x07, 0x82, 0x43, 0x14, // 77:1922 + 0x07, 0xC5, 0x3B, 0x11, // 78:1989 + 0x08, 0x00, 0x47, 0x13, // 79:2048 + 0x08, 0x47, 0x3A, 0x10, // 80:2119 + 0x08, 0x81, 0x47, 0x13, // 81:2177 + 0x08, 0xC8, 0x3F, 0x11, // 82:2248 + 0x09, 0x07, 0x3B, 0x10, // 83:2311 + 0x09, 0x42, 0x35, 0x0F, // 84:2370 + 0x09, 0x77, 0x3B, 0x11, // 85:2423 + 0x09, 0xB2, 0x39, 0x10, // 86:2482 + 0x09, 0xEB, 0x59, 0x17, // 87:2539 + 0x0A, 0x44, 0x3B, 0x10, // 88:2628 + 0x0A, 0x7F, 0x3D, 0x10, // 89:2687 + 0x0A, 0xBC, 0x37, 0x0F, // 90:2748 + 0x0A, 0xF3, 0x14, 0x07, // 91:2803 + 0x0B, 0x07, 0x1B, 0x07, // 92:2823 + 0x0B, 0x22, 0x18, 0x07, // 93:2850 + 0x0B, 0x3A, 0x2A, 0x0B, // 94:2874 + 0x0B, 0x64, 0x34, 0x0D, // 95:2916 + 0x0B, 0x98, 0x11, 0x08, // 96:2968 + 0x0B, 0xA9, 0x2F, 0x0D, // 97:2985 + 0x0B, 0xD8, 0x33, 0x0D, // 98:3032 + 0x0C, 0x0B, 0x2B, 0x0C, // 99:3083 + 0x0C, 0x36, 0x2F, 0x0D, // 100:3126 + 0x0C, 0x65, 0x2F, 0x0D, // 101:3173 + 0x0C, 0x94, 0x1A, 0x07, // 102:3220 + 0x0C, 0xAE, 0x2F, 0x0D, // 103:3246 + 0x0C, 0xDD, 0x2F, 0x0D, // 104:3293 + 0x0D, 0x0C, 0x0F, 0x05, // 105:3340 + 0x0D, 0x1B, 0x10, 0x05, // 106:3355 + 0x0D, 0x2B, 0x2F, 0x0C, // 107:3371 + 0x0D, 0x5A, 0x0F, 0x05, // 108:3418 + 0x0D, 0x69, 0x47, 0x14, // 109:3433 + 0x0D, 0xB0, 0x2F, 0x0D, // 110:3504 + 0x0D, 0xDF, 0x2F, 0x0D, // 111:3551 + 0x0E, 0x0E, 0x33, 0x0D, // 112:3598 + 0x0E, 0x41, 0x30, 0x0D, // 113:3649 + 0x0E, 0x71, 0x1E, 0x08, // 114:3697 + 0x0E, 0x8F, 0x2B, 0x0C, // 115:3727 + 0x0E, 0xBA, 0x1B, 0x07, // 116:3770 + 0x0E, 0xD5, 0x2F, 0x0D, // 117:3797 + 0x0F, 0x04, 0x2A, 0x0C, // 118:3844 + 0x0F, 0x2E, 0x42, 0x11, // 119:3886 + 0x0F, 0x70, 0x2B, 0x0C, // 120:3952 + 0x0F, 0x9B, 0x2A, 0x0C, // 121:3995 + 0x0F, 0xC5, 0x2B, 0x0C, // 122:4037 + 0x0F, 0xF0, 0x1C, 0x08, // 123:4080 + 0x10, 0x0C, 0x10, 0x06, // 124:4108 + 0x10, 0x1C, 0x1B, 0x08, // 125:4124 + 0x10, 0x37, 0x32, 0x0E, // 126:4151 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x18, // 128:65535 + 0xFF, 0xFF, 0x00, 0x18, // 129:65535 + 0xFF, 0xFF, 0x00, 0x18, // 130:65535 + 0xFF, 0xFF, 0x00, 0x18, // 131:65535 + 0xFF, 0xFF, 0x00, 0x18, // 132:65535 + 0xFF, 0xFF, 0x00, 0x18, // 133:65535 + 0xFF, 0xFF, 0x00, 0x18, // 134:65535 + 0xFF, 0xFF, 0x00, 0x18, // 135:65535 + 0xFF, 0xFF, 0x00, 0x18, // 136:65535 + 0xFF, 0xFF, 0x00, 0x18, // 137:65535 + 0xFF, 0xFF, 0x00, 0x18, // 138:65535 + 0xFF, 0xFF, 0x00, 0x18, // 139:65535 + 0xFF, 0xFF, 0x00, 0x18, // 140:65535 + 0xFF, 0xFF, 0x00, 0x18, // 141:65535 + 0xFF, 0xFF, 0x00, 0x18, // 142:65535 + 0xFF, 0xFF, 0x00, 0x18, // 143:65535 + 0xFF, 0xFF, 0x00, 0x18, // 144:65535 + 0xFF, 0xFF, 0x00, 0x18, // 145:65535 + 0xFF, 0xFF, 0x00, 0x18, // 146:65535 + 0xFF, 0xFF, 0x00, 0x18, // 147:65535 + 0xFF, 0xFF, 0x00, 0x18, // 148:65535 + 0xFF, 0xFF, 0x00, 0x18, // 149:65535 + 0xFF, 0xFF, 0x00, 0x18, // 150:65535 + 0xFF, 0xFF, 0x00, 0x18, // 151:65535 + 0xFF, 0xFF, 0x00, 0x18, // 152:65535 + 0xFF, 0xFF, 0x00, 0x18, // 153:65535 + 0xFF, 0xFF, 0x00, 0x18, // 154:65535 + 0xFF, 0xFF, 0x00, 0x18, // 155:65535 + 0xFF, 0xFF, 0x00, 0x18, // 156:65535 + 0xFF, 0xFF, 0x00, 0x18, // 157:65535 + 0xFF, 0xFF, 0x00, 0x18, // 158:65535 + 0xFF, 0xFF, 0x00, 0x18, // 159:65535 + 0xFF, 0xFF, 0x00, 0x07, // 160:65535 + 0x10, 0x69, 0x14, 0x08, // 161:4201 + 0x10, 0x7D, 0x2B, 0x0D, // 162:4221 + 0x10, 0xA8, 0x2F, 0x0D, // 163:4264 + 0x10, 0xD7, 0x33, 0x0D, // 164:4311 + 0x11, 0x0A, 0x31, 0x0D, // 165:4362 + 0x11, 0x3B, 0x10, 0x06, // 166:4411 + 0x11, 0x4B, 0x2F, 0x0D, // 167:4427 + 0x11, 0x7A, 0x19, 0x08, // 168:4474 + 0x11, 0x93, 0x46, 0x12, // 169:4499 + 0x11, 0xD9, 0x1A, 0x09, // 170:4569 + 0x11, 0xF3, 0x27, 0x0D, // 171:4595 + 0x12, 0x1A, 0x2F, 0x0E, // 172:4634 + 0x12, 0x49, 0x1B, 0x08, // 173:4681 + 0x12, 0x64, 0x46, 0x12, // 174:4708 + 0x12, 0xAA, 0x31, 0x0D, // 175:4778 + 0x12, 0xDB, 0x1E, 0x0A, // 176:4827 + 0x12, 0xF9, 0x33, 0x0D, // 177:4857 + 0x13, 0x2C, 0x1A, 0x08, // 178:4908 + 0x13, 0x46, 0x1A, 0x08, // 179:4934 + 0x13, 0x60, 0x19, 0x08, // 180:4960 + 0x13, 0x79, 0x2F, 0x0E, // 181:4985 + 0x13, 0xA8, 0x31, 0x0D, // 182:5032 + 0x13, 0xD9, 0x12, 0x08, // 183:5081 + 0x13, 0xEB, 0x18, 0x08, // 184:5099 + 0x14, 0x03, 0x16, 0x08, // 185:5123 + 0x14, 0x19, 0x1E, 0x09, // 186:5145 + 0x14, 0x37, 0x2E, 0x0D, // 187:5175 + 0x14, 0x65, 0x4F, 0x14, // 188:5221 + 0x14, 0xB4, 0x4B, 0x14, // 189:5300 + 0x14, 0xFF, 0x4B, 0x14, // 190:5375 + 0x15, 0x4A, 0x33, 0x0F, // 191:5450 + 0x15, 0x7D, 0x3B, 0x10, // 192:5501 + 0x15, 0xB8, 0x3B, 0x10, // 193:5560 + 0x15, 0xF3, 0x3B, 0x10, // 194:5619 + 0x16, 0x2E, 0x3B, 0x10, // 195:5678 + 0x16, 0x69, 0x3B, 0x10, // 196:5737 + 0x16, 0xA4, 0x3B, 0x10, // 197:5796 + 0x16, 0xDF, 0x5B, 0x18, // 198:5855 + 0x17, 0x3A, 0x3F, 0x11, // 199:5946 + 0x17, 0x79, 0x3B, 0x10, // 200:6009 + 0x17, 0xB4, 0x3B, 0x10, // 201:6068 + 0x17, 0xEF, 0x3B, 0x10, // 202:6127 + 0x18, 0x2A, 0x3B, 0x10, // 203:6186 + 0x18, 0x65, 0x11, 0x07, // 204:6245 + 0x18, 0x76, 0x11, 0x07, // 205:6262 + 0x18, 0x87, 0x15, 0x07, // 206:6279 + 0x18, 0x9C, 0x15, 0x07, // 207:6300 + 0x18, 0xB1, 0x3F, 0x11, // 208:6321 + 0x18, 0xF0, 0x3B, 0x11, // 209:6384 + 0x19, 0x2B, 0x47, 0x13, // 210:6443 + 0x19, 0x72, 0x47, 0x13, // 211:6514 + 0x19, 0xB9, 0x47, 0x13, // 212:6585 + 0x1A, 0x00, 0x47, 0x13, // 213:6656 + 0x1A, 0x47, 0x47, 0x13, // 214:6727 + 0x1A, 0x8E, 0x2B, 0x0E, // 215:6798 + 0x1A, 0xB9, 0x47, 0x13, // 216:6841 + 0x1B, 0x00, 0x3B, 0x11, // 217:6912 + 0x1B, 0x3B, 0x3B, 0x11, // 218:6971 + 0x1B, 0x76, 0x3B, 0x11, // 219:7030 + 0x1B, 0xB1, 0x3B, 0x11, // 220:7089 + 0x1B, 0xEC, 0x3D, 0x10, // 221:7148 + 0x1C, 0x29, 0x3A, 0x10, // 222:7209 + 0x1C, 0x63, 0x37, 0x0F, // 223:7267 + 0x1C, 0x9A, 0x2F, 0x0D, // 224:7322 + 0x1C, 0xC9, 0x2F, 0x0D, // 225:7369 + 0x1C, 0xF8, 0x2F, 0x0D, // 226:7416 + 0x1D, 0x27, 0x2F, 0x0D, // 227:7463 + 0x1D, 0x56, 0x2F, 0x0D, // 228:7510 + 0x1D, 0x85, 0x2F, 0x0D, // 229:7557 + 0x1D, 0xB4, 0x53, 0x15, // 230:7604 + 0x1E, 0x07, 0x2B, 0x0C, // 231:7687 + 0x1E, 0x32, 0x2F, 0x0D, // 232:7730 + 0x1E, 0x61, 0x2F, 0x0D, // 233:7777 + 0x1E, 0x90, 0x2F, 0x0D, // 234:7824 + 0x1E, 0xBF, 0x2F, 0x0D, // 235:7871 + 0x1E, 0xEE, 0x11, 0x07, // 236:7918 + 0x1E, 0xFF, 0x11, 0x07, // 237:7935 + 0x1F, 0x10, 0x15, 0x07, // 238:7952 + 0x1F, 0x25, 0x15, 0x07, // 239:7973 + 0x1F, 0x3A, 0x2F, 0x0D, // 240:7994 + 0x1F, 0x69, 0x2F, 0x0D, // 241:8041 + 0x1F, 0x98, 0x2F, 0x0D, // 242:8088 + 0x1F, 0xC7, 0x2F, 0x0D, // 243:8135 + 0x1F, 0xF6, 0x2F, 0x0D, // 244:8182 + 0x20, 0x25, 0x2F, 0x0D, // 245:8229 + 0x20, 0x54, 0x2F, 0x0D, // 246:8276 + 0x20, 0x83, 0x32, 0x0D, // 247:8323 + 0x20, 0xB5, 0x33, 0x0F, // 248:8373 + 0x20, 0xE8, 0x2F, 0x0D, // 249:8424 + 0x21, 0x17, 0x2F, 0x0D, // 250:8471 + 0x21, 0x46, 0x2F, 0x0D, // 251:8518 + 0x21, 0x75, 0x2F, 0x0D, // 252:8565 + 0x21, 0xA4, 0x2A, 0x0C, // 253:8612 + 0x21, 0xCE, 0x2F, 0x0D, // 254:8654 + 0x21, 0xFD, 0x2A, 0x0C, // 255:8701 + + // Font Data: + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x33,0x00,0xE0,0xFF,0x33, // 33 + 0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07, // 34 + 0x00,0x0C,0x03,0x00,0x00,0x0C,0x33,0x00,0x00,0x0C,0x3F,0x00,0x00,0xFC,0x0F,0x00,0x80,0xFF,0x03,0x00,0xE0,0x0F,0x03,0x00,0x60,0x0C,0x33,0x00,0x00,0x0C,0x3F,0x00,0x00,0xFC,0x0F,0x00,0x80,0xFF,0x03,0x00,0xE0,0x0F,0x03,0x00,0x60,0x0C,0x03,0x00,0x00,0x0C,0x03, // 35 + 0x00,0x00,0x00,0x00,0x80,0x07,0x06,0x00,0xC0,0x0F,0x1E,0x00,0xC0,0x18,0x1C,0x00,0x60,0x18,0x38,0x00,0x60,0x30,0x30,0x00,0xF0,0xFF,0xFF,0x00,0x60,0x30,0x30,0x00,0x60,0x60,0x38,0x00,0xC0,0x60,0x18,0x00,0xC0,0xC1,0x1F,0x00,0x00,0x81,0x07, // 36 + 0x00,0x00,0x00,0x00,0x80,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x20,0x20,0x00,0x60,0x30,0x38,0x00,0xC0,0x1F,0x1E,0x00,0x80,0x8F,0x0F,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x8F,0x0F,0x00,0xC0,0xC3,0x1F,0x00,0xE0,0x60,0x30,0x00,0x20,0x20,0x20,0x00,0x00,0x20,0x20,0x00,0x00,0x60,0x30,0x00,0x00,0xC0,0x1F,0x00,0x00,0x80,0x0F, // 37 + 0x00,0x00,0x00,0x00,0x00,0x80,0x07,0x00,0x00,0xC0,0x0F,0x00,0x80,0xE3,0x1C,0x00,0xC0,0x77,0x38,0x00,0xE0,0x3C,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x78,0x30,0x00,0xE0,0xEC,0x38,0x00,0xC0,0x8F,0x1B,0x00,0x80,0x03,0x1F,0x00,0x00,0x00,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0xC0,0x38,0x00,0x00,0x00,0x10, // 38 + 0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07, // 39 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x0F,0x00,0x00,0xFE,0x7F,0x00,0x80,0x0F,0xF0,0x01,0xC0,0x01,0x80,0x03,0x60,0x00,0x00,0x06,0x20,0x00,0x00,0x04, // 40 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x04,0x60,0x00,0x00,0x06,0xC0,0x01,0x80,0x03,0x80,0x0F,0xF0,0x01,0x00,0xFE,0x7F,0x00,0x00,0xF0,0x0F, // 41 + 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x04,0x00,0x00,0x80,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x80,0x04,0x00,0x00,0x80, // 42 + 0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xFF,0x0F,0x00,0x00,0xFF,0x0F,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 43 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x03,0x00,0x00,0xF0,0x01, // 44 + 0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01, // 45 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 46 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0xE0,0x0F,0x00,0x00,0xFC,0x01,0x00,0x80,0x3F,0x00,0x00,0xE0,0x03,0x00,0x00,0x60, // 47 + 0x00,0x00,0x00,0x00,0x00,0xFE,0x03,0x00,0x80,0xFF,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x01,0x1C,0x00,0x80,0xFF,0x0F,0x00,0x00,0xFE,0x03, // 48 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x00,0x80,0x01,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 49 + 0x00,0x00,0x00,0x00,0x00,0x03,0x30,0x00,0xC0,0x03,0x38,0x00,0xC0,0x00,0x3C,0x00,0x60,0x00,0x36,0x00,0x60,0x00,0x33,0x00,0x60,0x80,0x31,0x00,0x60,0xC0,0x30,0x00,0x60,0x60,0x30,0x00,0xC0,0x30,0x30,0x00,0xC0,0x1F,0x30,0x00,0x00,0x0F,0x30, // 50 + 0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x00,0xC0,0x01,0x0E,0x00,0xC0,0x00,0x1C,0x00,0x60,0x00,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xC0,0x38,0x30,0x00,0xC0,0x6F,0x18,0x00,0x80,0xC7,0x0F,0x00,0x00,0x80,0x07, // 51 + 0x00,0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x03,0x00,0x00,0x3C,0x03,0x00,0x00,0x0E,0x03,0x00,0x80,0x07,0x03,0x00,0xC0,0x01,0x03,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03, // 52 + 0x00,0x00,0x00,0x00,0x00,0x30,0x06,0x00,0x80,0x3F,0x0E,0x00,0xE0,0x1F,0x18,0x00,0x60,0x08,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x18,0x1C,0x00,0x60,0xF0,0x0F,0x00,0x00,0xE0,0x03, // 53 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x03,0x00,0x80,0xFF,0x0F,0x00,0xC0,0x63,0x1C,0x00,0xC0,0x30,0x38,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0xE0,0x30,0x18,0x00,0xC0,0xF1,0x0F,0x00,0x80,0xC1,0x07, // 54 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x3C,0x00,0x60,0x80,0x3F,0x00,0x60,0xE0,0x03,0x00,0x60,0x78,0x00,0x00,0x60,0x0E,0x00,0x00,0x60,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60, // 55 + 0x00,0x00,0x00,0x00,0x00,0x80,0x07,0x00,0x80,0xC7,0x1F,0x00,0xC0,0x6F,0x18,0x00,0xE0,0x38,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xE0,0x38,0x30,0x00,0xC0,0x6F,0x18,0x00,0x80,0xC7,0x1F,0x00,0x00,0x80,0x07, // 56 + 0x00,0x00,0x00,0x00,0x00,0x1F,0x0C,0x00,0x80,0x7F,0x1C,0x00,0xC0,0x61,0x38,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0x60,0x18,0x00,0xC0,0x31,0x1E,0x00,0x80,0xFF,0x0F,0x00,0x00,0xFE,0x01, // 57 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30, // 58 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x30,0x03,0x00,0x06,0xF0,0x01, // 59 + 0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x04,0x01,0x00,0x00,0x06,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x03,0x06, // 60 + 0x00,0x00,0x00,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01, // 61 + 0x00,0x00,0x00,0x00,0x00,0x03,0x06,0x00,0x00,0x06,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x04,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0xD8,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x20, // 62 + 0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x60,0x80,0x33,0x00,0x60,0xC0,0x33,0x00,0x60,0xE0,0x00,0x00,0x60,0x30,0x00,0x00,0xC0,0x38,0x00,0x00,0xC0,0x1F,0x00,0x00,0x00,0x07, // 63 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x0F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x1E,0xF0,0x00,0x00,0x07,0xC0,0x01,0x80,0xC3,0x87,0x01,0xC0,0xF1,0x9F,0x03,0xC0,0x38,0x18,0x03,0xC0,0x0C,0x30,0x03,0x60,0x0E,0x30,0x06,0x60,0x06,0x30,0x06,0x60,0x06,0x18,0x06,0x60,0x06,0x0C,0x06,0x60,0x0C,0x1E,0x06,0x60,0xF8,0x3F,0x06,0xE0,0xFE,0x31,0x06,0xC0,0x0E,0x30,0x06,0xC0,0x01,0x18,0x03,0x80,0x03,0x1C,0x03,0x00,0x07,0x8F,0x01,0x00,0xFE,0x87,0x01,0x00,0xF8,0xC1,0x00,0x00,0x00,0x40, // 64 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x80,0x8F,0x01,0x00,0xE0,0x83,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0x83,0x01,0x00,0x80,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 65 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xC0,0x78,0x30,0x00,0xC0,0xFF,0x18,0x00,0x80,0xC7,0x1F,0x00,0x00,0x80,0x07, // 66 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0F,0x00,0x00,0x02,0x03, // 67 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0E,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 68 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 69 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60, // 70 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x60,0x30,0x00,0x60,0x60,0x30,0x00,0xE0,0x60,0x38,0x00,0xC0,0x60,0x18,0x00,0xC0,0x61,0x18,0x00,0x80,0xE3,0x0F,0x00,0x00,0xE2,0x0F, // 71 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 72 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 73 + 0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0xE0,0xFF,0x1F,0x00,0xE0,0xFF,0x0F, // 74 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xE7,0x01,0x00,0x80,0x83,0x07,0x00,0xC0,0x01,0x0F,0x00,0xE0,0x00,0x1E,0x00,0x60,0x00,0x38,0x00,0x20,0x00,0x30,0x00,0x00,0x00,0x20, // 75 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 76 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3F,0x00,0x00,0xE0,0x07,0x00,0x00,0xFE,0x00,0x00,0xC0,0x0F,0x00,0x00,0xE0,0x01,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 77 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 78 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 79 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0xC0,0x30,0x00,0x00,0xC0,0x3F,0x00,0x00,0x00,0x0F, // 80 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x0C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x36,0x00,0x60,0x00,0x36,0x00,0xE0,0x00,0x3C,0x00,0xC0,0x00,0x1C,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x3F,0x00,0x00,0xFF,0x77,0x00,0x00,0xFC,0x61, // 81 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x70,0x00,0x00,0x60,0xF0,0x00,0x00,0x60,0xF0,0x03,0x00,0x60,0xB0,0x07,0x00,0xE0,0x18,0x1F,0x00,0xC0,0x1F,0x3C,0x00,0x80,0x0F,0x30,0x00,0x00,0x00,0x20, // 82 + 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x0F,0x00,0xC0,0x1F,0x1C,0x00,0xC0,0x18,0x18,0x00,0x60,0x38,0x38,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x70,0x30,0x00,0xC0,0x60,0x18,0x00,0xC0,0xE1,0x18,0x00,0x80,0xC3,0x0F,0x00,0x00,0x83,0x07, // 83 + 0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 84 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 85 + 0x20,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0xF8,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x3E,0x00,0x00,0xC0,0x0F,0x00,0x00,0xF8,0x01,0x00,0x00,0x3E,0x00,0x00,0xC0,0x0F,0x00,0x00,0xE0,0x01,0x00,0x00,0x20, // 86 + 0x60,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0x80,0xFF,0x00,0x00,0x00,0xF8,0x0F,0x00,0x00,0x80,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3F,0x00,0x00,0xE0,0x0F,0x00,0x00,0xFC,0x01,0x00,0x80,0x1F,0x00,0x00,0xE0,0x03,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xE0,0x0F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x80,0x3F,0x00,0x00,0xF8,0x0F,0x00,0x80,0xFF,0x00,0x00,0xE0,0x07,0x00,0x00,0x60, // 87 + 0x00,0x00,0x20,0x00,0x20,0x00,0x30,0x00,0x60,0x00,0x3C,0x00,0xE0,0x01,0x1E,0x00,0xC0,0x83,0x07,0x00,0x00,0xCF,0x03,0x00,0x00,0xFE,0x01,0x00,0x00,0x38,0x00,0x00,0x00,0xFE,0x01,0x00,0x00,0xCF,0x03,0x00,0xC0,0x03,0x07,0x00,0xE0,0x01,0x1E,0x00,0x60,0x00,0x3C,0x00,0x20,0x00,0x30,0x00,0x00,0x00,0x20, // 88 + 0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0xF0,0x3F,0x00,0x00,0xF0,0x3F,0x00,0x00,0x3C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 89 + 0x00,0x00,0x30,0x00,0x60,0x00,0x38,0x00,0x60,0x00,0x3C,0x00,0x60,0x00,0x37,0x00,0x60,0x80,0x33,0x00,0x60,0xC0,0x31,0x00,0x60,0xE0,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x1C,0x30,0x00,0x60,0x0E,0x30,0x00,0x60,0x07,0x30,0x00,0xE0,0x01,0x30,0x00,0xE0,0x00,0x30,0x00,0x60,0x00,0x30, // 90 + 0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06, // 91 + 0x60,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x3F,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xE0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 92 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07, // 93 + 0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1F,0x00,0x00,0xC0,0x07,0x00,0x00,0xE0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xC0,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x20, // 94 + 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06, // 95 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x80, // 96 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x00,0x86,0x31,0x00,0x00,0x86,0x31,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x18,0x00,0x00,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 97 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x18,0x0C,0x00,0x00,0x0C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xE0,0x03, // 98 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0x18,0x0C, // 99 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0C,0x18,0x00,0x00,0x18,0x0C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 100 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 101 + 0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0xC0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x06,0x00,0x00,0x60,0x06,0x00,0x00,0x60,0x06, // 102 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x83,0x01,0x00,0xF8,0x8F,0x03,0x00,0x1C,0x1C,0x07,0x00,0x0E,0x38,0x06,0x00,0x06,0x30,0x06,0x00,0x06,0x30,0x06,0x00,0x06,0x30,0x06,0x00,0x0C,0x18,0x07,0x00,0x18,0x8C,0x03,0x00,0xFE,0xFF,0x01,0x00,0xFE,0xFF, // 103 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 104 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0x60,0xFE,0x3F, // 105 + 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x60,0xFE,0xFF,0x07,0x60,0xFE,0xFF,0x03, // 106 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xF0,0x01,0x00,0x00,0x98,0x07,0x00,0x00,0x0C,0x0E,0x00,0x00,0x06,0x3C,0x00,0x00,0x02,0x30,0x00,0x00,0x00,0x20, // 107 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 108 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 109 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 110 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 111 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07,0x00,0x18,0x0C,0x00,0x00,0x0C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xE0,0x03, // 112 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0C,0x18,0x00,0x00,0x18,0x0C,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07, // 113 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06, // 114 + 0x00,0x00,0x00,0x00,0x00,0x38,0x0C,0x00,0x00,0x7C,0x1C,0x00,0x00,0xEE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x31,0x00,0x00,0xC6,0x31,0x00,0x00,0x8E,0x39,0x00,0x00,0x9C,0x1F,0x00,0x00,0x18,0x0F, // 115 + 0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0xC0,0xFF,0x1F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30, // 116 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 117 + 0x00,0x06,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xC0,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1F,0x00,0x00,0xC0,0x07,0x00,0x00,0xF8,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 118 + 0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x80,0x1F,0x00,0x00,0xE0,0x03,0x00,0x00,0x7C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x80,0x1F,0x00,0x00,0xF0,0x03,0x00,0x00,0x7E,0x00,0x00,0x00,0x0E, // 119 + 0x00,0x02,0x20,0x00,0x00,0x06,0x30,0x00,0x00,0x1E,0x3C,0x00,0x00,0x38,0x0E,0x00,0x00,0xF0,0x07,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x07,0x00,0x00,0x38,0x0E,0x00,0x00,0x1C,0x3C,0x00,0x00,0x0E,0x30,0x00,0x00,0x02,0x20, // 120 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0x00,0xF0,0x01,0x06,0x00,0x80,0x0F,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xFC,0x00,0x00,0xC0,0x1F,0x00,0x00,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 121 + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x06,0x3C,0x00,0x00,0x06,0x3E,0x00,0x00,0x06,0x37,0x00,0x00,0xC6,0x33,0x00,0x00,0xE6,0x30,0x00,0x00,0x76,0x30,0x00,0x00,0x3E,0x30,0x00,0x00,0x1E,0x30,0x00,0x00,0x06,0x30, // 122 + 0x00,0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x03,0x00,0xC0,0x7F,0xFE,0x03,0xE0,0x3F,0xFC,0x07,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06, // 123 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x0F,0xE0,0xFF,0xFF,0x0F, // 124 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,0xE0,0x3F,0xFC,0x07,0xC0,0x7F,0xFF,0x03,0x00,0xC0,0x03,0x00,0x00,0x80,0x01, // 125 + 0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x60, // 126 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE6,0xFF,0x07,0x00,0xE6,0xFF,0x07, // 161 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x9C,0x07,0x00,0x0E,0x78,0x00,0x00,0x06,0x3F,0x00,0x00,0xF6,0x30,0x00,0x00,0x0E,0x30,0x00,0xE0,0x0D,0x1C,0x00,0x00,0x1C,0x0E,0x00,0x00,0x10,0x06, // 162 + 0x00,0x60,0x10,0x00,0x00,0x60,0x38,0x00,0x00,0x7F,0x1C,0x00,0xC0,0xFF,0x1F,0x00,0xE0,0xE0,0x19,0x00,0x60,0x60,0x18,0x00,0x60,0x60,0x18,0x00,0x60,0x60,0x30,0x00,0xE0,0x00,0x30,0x00,0xC0,0x01,0x30,0x00,0x80,0x01,0x38,0x00,0x00,0x00,0x10, // 163 + 0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x00,0x00,0xF7,0x0E,0x00,0x00,0xFE,0x07,0x00,0x00,0x0C,0x03,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x0C,0x03,0x00,0x00,0xFE,0x07,0x00,0x00,0xF7,0x0E,0x00,0x00,0x02,0x04, // 164 + 0xE0,0x60,0x06,0x00,0xC0,0x61,0x06,0x00,0x80,0x67,0x06,0x00,0x00,0x7E,0x06,0x00,0x00,0x7C,0x06,0x00,0x00,0xF0,0x3F,0x00,0x00,0xF0,0x3F,0x00,0x00,0x7C,0x06,0x00,0x00,0x7E,0x06,0x00,0x80,0x67,0x06,0x00,0xC0,0x61,0x06,0x00,0xE0,0x60,0x06,0x00,0x20, // 165 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x7F,0xF8,0x0F,0xE0,0x7F,0xF8,0x0F, // 166 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x80,0xF3,0xC1,0x00,0xC0,0x1F,0xC3,0x03,0xE0,0x0C,0x07,0x03,0x60,0x1C,0x06,0x06,0x60,0x18,0x0C,0x06,0x60,0x30,0x1C,0x06,0xE0,0x70,0x38,0x07,0xC0,0xE1,0xF4,0x03,0x80,0xC1,0xE7,0x01,0x00,0x80,0x03, // 167 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 168 + 0x00,0xF8,0x00,0x00,0x00,0xFE,0x03,0x00,0x00,0x07,0x07,0x00,0x80,0x01,0x0C,0x00,0xC0,0x79,0x1C,0x00,0xC0,0xFE,0x19,0x00,0x60,0x86,0x31,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x87,0x33,0x00,0xC0,0x86,0x19,0x00,0xC0,0x85,0x1C,0x00,0x80,0x01,0x0C,0x00,0x00,0x07,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xF8, // 169 + 0x00,0x00,0x00,0x00,0xC0,0x1C,0x00,0x00,0xE0,0x3E,0x00,0x00,0x60,0x32,0x00,0x00,0x60,0x32,0x00,0x00,0xE0,0x3F,0x00,0x00,0xC0,0x3F, // 170 + 0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x78,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x84,0x10,0x00,0x00,0xE0,0x03,0x00,0x00,0x78,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x04,0x10, // 171 + 0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFC,0x01, // 172 + 0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01, // 173 + 0x00,0xF8,0x00,0x00,0x00,0xFE,0x03,0x00,0x00,0x07,0x07,0x00,0x80,0x01,0x0C,0x00,0xC0,0x01,0x1C,0x00,0xC0,0xFE,0x1B,0x00,0x60,0xFE,0x33,0x00,0x60,0x66,0x30,0x00,0x60,0x66,0x30,0x00,0x60,0xE6,0x30,0x00,0x60,0xFE,0x31,0x00,0x60,0x3C,0x33,0x00,0xC0,0x00,0x1A,0x00,0xC0,0x01,0x1C,0x00,0x80,0x01,0x0C,0x00,0x00,0x07,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xF8, // 174 + 0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C, // 175 + 0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x40,0x04,0x00,0x00,0x20,0x08,0x00,0x00,0x20,0x08,0x00,0x00,0x20,0x08,0x00,0x00,0x40,0x04,0x00,0x00,0x80,0x03, // 176 + 0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0xFF,0x3F,0x00,0x00,0xFF,0x3F,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30, // 177 + 0x40,0x20,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x38,0x00,0x00,0x20,0x2C,0x00,0x00,0x20,0x26,0x00,0x00,0xE0,0x23,0x00,0x00,0xC0,0x21, // 178 + 0x40,0x10,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x22,0x00,0x00,0x20,0x22,0x00,0x00,0xE0,0x3D,0x00,0x00,0xC0,0x1D, // 179 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 180 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07,0x00,0x00,0x1C,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x1C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 181 + 0x00,0x0F,0x00,0x00,0xC0,0x3F,0x00,0x00,0xC0,0x3F,0x00,0x00,0xE0,0x7F,0x00,0x00,0xE0,0x7F,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x00,0x60, // 182 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 183 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0xC0,0x02,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x01, // 184 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x3F,0x00,0x00,0xE0,0x3F, // 185 + 0x00,0x00,0x00,0x00,0x80,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0xE0,0x38,0x00,0x00,0x60,0x30,0x00,0x00,0xE0,0x38,0x00,0x00,0xC0,0x1F,0x00,0x00,0x80,0x0F, // 186 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x00,0x00,0x1C,0x1C,0x00,0x00,0x78,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0x84,0x10,0x00,0x00,0x1C,0x1C,0x00,0x00,0x78,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0x80, // 187 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x20,0x00,0xE0,0x3F,0x38,0x00,0xE0,0x3F,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x07,0x0C,0x00,0xC0,0x01,0x0E,0x00,0xE0,0x80,0x0B,0x00,0x60,0xC0,0x08,0x00,0x00,0xE0,0x3F,0x00,0x00,0xE0,0x3F,0x00,0x00,0x00,0x08, // 188 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x20,0x00,0xE0,0x3F,0x30,0x00,0xE0,0x3F,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x4E,0x20,0x00,0x00,0x67,0x30,0x00,0xC0,0x21,0x38,0x00,0xE0,0x20,0x2C,0x00,0x60,0x20,0x26,0x00,0x00,0xE0,0x27,0x00,0x00,0xC0,0x21, // 189 + 0x40,0x10,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x22,0x20,0x00,0x20,0x22,0x30,0x00,0xE0,0x3D,0x38,0x00,0xC0,0x1D,0x0E,0x00,0x00,0x00,0x07,0x00,0x00,0x80,0x03,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x0E,0x0C,0x00,0x00,0x07,0x0E,0x00,0x80,0x83,0x0B,0x00,0xE0,0xC0,0x08,0x00,0x60,0xE0,0x3F,0x00,0x20,0xE0,0x3F,0x00,0x00,0x00,0x08, // 190 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xF8,0x03,0x00,0x00,0x1E,0x03,0x00,0x00,0x07,0x07,0x00,0xE6,0x03,0x06,0x00,0xE6,0x01,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xC0, // 191 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x82,0x8F,0x01,0x00,0xE6,0x83,0x01,0x00,0x6E,0x80,0x01,0x00,0xE8,0x83,0x01,0x00,0x80,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 192 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x80,0x8F,0x01,0x00,0xE8,0x83,0x01,0x00,0x6E,0x80,0x01,0x00,0xE6,0x83,0x01,0x00,0x82,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 193 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x88,0x8F,0x01,0x00,0xEC,0x83,0x01,0x00,0x66,0x80,0x01,0x00,0xE6,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x08,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 194 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x0C,0xFE,0x01,0x00,0x8E,0x8F,0x01,0x00,0xE6,0x83,0x01,0x00,0x66,0x80,0x01,0x00,0xEC,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x0E,0xFE,0x01,0x00,0x06,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 195 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x8C,0x8F,0x01,0x00,0xEC,0x83,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x0C,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 196 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x9C,0x8F,0x01,0x00,0xE2,0x83,0x01,0x00,0x62,0x80,0x01,0x00,0xE2,0x83,0x01,0x00,0x9C,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 197 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x0F,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x01,0x00,0x00,0xBC,0x01,0x00,0x00,0x8F,0x01,0x00,0xC0,0x83,0x01,0x00,0xE0,0x80,0x01,0x00,0x60,0x80,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 198 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0x60,0x00,0x30,0x02,0x60,0x00,0x30,0x02,0x60,0x00,0xF0,0x02,0x60,0x00,0xB0,0x03,0x60,0x00,0x30,0x01,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0F,0x00,0x00,0x02,0x03, // 199 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x62,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x6E,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 200 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x6E,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x62,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 201 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 202 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 203 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0xE6,0xFF,0x3F,0x00,0xEE,0xFF,0x3F,0x00,0x08, // 204 + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0xEE,0xFF,0x3F,0x00,0xE6,0xFF,0x3F,0x00,0x02, // 205 + 0x08,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xE6,0xFF,0x3F,0x00,0xE6,0xFF,0x3F,0x00,0x0C,0x00,0x00,0x00,0x08, // 206 + 0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x0C,0x00,0x00,0x00,0x0C, // 207 + 0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0E,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 208 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xC0,0x01,0x00,0x00,0x8C,0x03,0x00,0x00,0x0E,0x0E,0x00,0x00,0x06,0x3C,0x00,0x00,0x06,0x70,0x00,0x00,0x0C,0xE0,0x01,0x00,0x0C,0x80,0x03,0x00,0x0E,0x00,0x0F,0x00,0x06,0x00,0x1C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 209 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x62,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0x68,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 210 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x68,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x62,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 211 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x68,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0xE8,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 212 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xCC,0x00,0x18,0x00,0xEE,0x00,0x38,0x00,0x66,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0xE6,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 213 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x6C,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0xEC,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 214 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x03,0x00,0x00,0x8E,0x03,0x00,0x00,0xDC,0x01,0x00,0x00,0xF8,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xDC,0x01,0x00,0x00,0x8E,0x03,0x00,0x00,0x06,0x03, // 215 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x21,0x00,0x00,0xFF,0x77,0x00,0x80,0x07,0x3F,0x00,0xC0,0x01,0x1E,0x00,0xC0,0x00,0x1F,0x00,0xE0,0x80,0x3B,0x00,0x60,0xC0,0x31,0x00,0x60,0xE0,0x30,0x00,0x60,0x70,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x1C,0x30,0x00,0xE0,0x0E,0x38,0x00,0xC0,0x07,0x18,0x00,0xC0,0x03,0x1C,0x00,0xE0,0x07,0x0F,0x00,0x70,0xFF,0x07,0x00,0x20,0xFC,0x01, // 216 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x02,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x0E,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 217 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x0E,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x02,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 218 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x08,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x08,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 219 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x0C,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x0C,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 220 + 0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x3C,0x00,0x00,0x08,0xF0,0x3F,0x00,0x0E,0xF0,0x3F,0x00,0x06,0x3C,0x00,0x00,0x02,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 221 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x07,0x00,0x00,0x86,0x03,0x00,0x00,0xFE,0x01,0x00,0x00,0xF8, // 222 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xFF,0x3F,0x00,0xC0,0xFF,0x3F,0x00,0xC0,0x00,0x00,0x00,0x60,0x00,0x08,0x00,0x60,0x00,0x1C,0x00,0x60,0x00,0x38,0x00,0xE0,0x78,0x30,0x00,0xC0,0x7F,0x30,0x00,0x80,0xC7,0x30,0x00,0x00,0x80,0x39,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x0F, // 223 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x20,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0xE0,0xC6,0x30,0x00,0x80,0xC6,0x18,0x00,0x00,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 224 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x00,0x86,0x31,0x00,0x80,0x86,0x31,0x00,0xE0,0xC6,0x30,0x00,0x60,0xC6,0x18,0x00,0x20,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 225 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x80,0x8C,0x39,0x00,0xC0,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0x60,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0x80,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 226 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0xC0,0x1C,0x1F,0x00,0xE0,0x8C,0x39,0x00,0x60,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0xC0,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0xE0,0xCE,0x0C,0x00,0x60,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 227 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0xC0,0x8C,0x39,0x00,0xC0,0x86,0x31,0x00,0x00,0x86,0x31,0x00,0x00,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0xC0,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 228 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x70,0x86,0x31,0x00,0x88,0x86,0x31,0x00,0x88,0xC6,0x30,0x00,0x88,0xC6,0x18,0x00,0x70,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 229 + 0x00,0x00,0x00,0x00,0x00,0x10,0x0F,0x00,0x00,0x9C,0x1F,0x00,0x00,0xCC,0x39,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0x66,0x18,0x00,0x00,0x6E,0x1C,0x00,0x00,0xFC,0x0F,0x00,0x00,0xFC,0x1F,0x00,0x00,0xCC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xCC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xE0,0x04, // 230 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x02,0x00,0x06,0x30,0x02,0x00,0x06,0xF0,0x02,0x00,0x06,0xB0,0x03,0x00,0x0E,0x38,0x01,0x00,0x1C,0x1C,0x00,0x00,0x18,0x0C, // 231 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x20,0xCE,0x38,0x00,0x60,0xC6,0x30,0x00,0xE0,0xC6,0x30,0x00,0x80,0xC6,0x30,0x00,0x00,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 232 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x80,0xC6,0x30,0x00,0xE0,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0x20,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 233 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x80,0xCE,0x38,0x00,0xC0,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0xC0,0xCE,0x38,0x00,0x80,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 234 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0xC0,0xCE,0x38,0x00,0xC0,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0xC0,0xCE,0x38,0x00,0xC0,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 235 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0xE0,0xFE,0x3F,0x00,0x80, // 236 + 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0xFE,0x3F,0x00,0x60,0xFE,0x3F,0x00,0x20, // 237 + 0x80,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0x60,0xFE,0x3F,0x00,0xC0,0x00,0x00,0x00,0x80, // 238 + 0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0xC0,0x00,0x00,0x00,0xC0, // 239 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1D,0x1C,0x00,0xA0,0x0F,0x38,0x00,0xA0,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0xC0,0x06,0x30,0x00,0xC0,0x0F,0x38,0x00,0x20,0x1F,0x1C,0x00,0x00,0xFC,0x0F,0x00,0x00,0xE0,0x07, // 240 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0xC0,0xFE,0x3F,0x00,0xE0,0x18,0x00,0x00,0x60,0x0C,0x00,0x00,0x60,0x06,0x00,0x00,0xC0,0x06,0x00,0x00,0xC0,0x06,0x00,0x00,0xE0,0x0E,0x00,0x00,0x60,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 241 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x20,0x0E,0x38,0x00,0x60,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0x80,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 242 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x80,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0x20,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 243 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x80,0x0E,0x38,0x00,0xC0,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0x80,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 244 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0xC0,0x1C,0x1C,0x00,0xE0,0x0E,0x38,0x00,0x60,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0xC0,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0xE0,0x1C,0x1C,0x00,0x60,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 245 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0xC0,0x0E,0x38,0x00,0xC0,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0xC0,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 246 + 0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0xB6,0x01,0x00,0x00,0xB6,0x01,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 247 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x67,0x00,0x00,0xF8,0x7F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x3F,0x00,0x00,0x86,0x33,0x00,0x00,0xE6,0x31,0x00,0x00,0x76,0x30,0x00,0x00,0x3E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xFF,0x0F,0x00,0x00,0xF3,0x07, // 248 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x20,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x30,0x00,0x80,0x00,0x30,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 249 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x80,0x00,0x30,0x00,0xE0,0x00,0x30,0x00,0x60,0x00,0x18,0x00,0x20,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 250 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x80,0x00,0x38,0x00,0xC0,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0x80,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 251 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0xC0,0x00,0x38,0x00,0xC0,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 252 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0x00,0xF0,0x01,0x06,0x00,0x80,0x0F,0x07,0x80,0x00,0xFE,0x03,0xE0,0x00,0xFC,0x00,0x60,0xC0,0x1F,0x00,0x20,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 253 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x00,0x1C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x03, // 254 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0xC0,0xF0,0x01,0x06,0xC0,0x80,0x0F,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xFC,0x00,0xC0,0xC0,0x1F,0x00,0xC0,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06 // 255 +}; +#endif diff --git a/libraries/oled-ssd1306/OLEDDisplayUi.cpp b/libraries/oled-ssd1306/OLEDDisplayUi.cpp new file mode 100644 index 00000000..94232cdf --- /dev/null +++ b/libraries/oled-ssd1306/OLEDDisplayUi.cpp @@ -0,0 +1,406 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 by Daniel Eichhorn + * Copyright (c) 2016 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include "OLEDDisplayUi.h" + +OLEDDisplayUi::OLEDDisplayUi(OLEDDisplay *display) { + this->display = display; +} + +void OLEDDisplayUi::init() { + this->display->init(); +} + +void OLEDDisplayUi::setTargetFPS(uint8_t fps){ + float oldInterval = this->updateInterval; + this->updateInterval = ((float) 1.0 / (float) fps) * 1000; + + // Calculate new ticksPerFrame + float changeRatio = oldInterval / (float) this->updateInterval; + this->ticksPerFrame *= changeRatio; + this->ticksPerTransition *= changeRatio; +} + +// -/------ Automatic controll ------\- + +void OLEDDisplayUi::enableAutoTransition(){ + this->autoTransition = true; +} +void OLEDDisplayUi::disableAutoTransition(){ + this->autoTransition = false; +} +void OLEDDisplayUi::setAutoTransitionForwards(){ + this->state.frameTransitionDirection = 1; + this->lastTransitionDirection = 1; +} +void OLEDDisplayUi::setAutoTransitionBackwards(){ + this->state.frameTransitionDirection = -1; + this->lastTransitionDirection = -1; +} +void OLEDDisplayUi::setTimePerFrame(uint16_t time){ + this->ticksPerFrame = (int) ( (float) time / (float) updateInterval); +} +void OLEDDisplayUi::setTimePerTransition(uint16_t time){ + this->ticksPerTransition = (int) ( (float) time / (float) updateInterval); +} + +// -/------ Customize indicator position and style -------\- +void OLEDDisplayUi::enableIndicator(){ + this->state.isIndicatorDrawen = true; +} + +void OLEDDisplayUi::disableIndicator(){ + this->state.isIndicatorDrawen = false; +} + +void OLEDDisplayUi::enableAllIndicators(){ + this->shouldDrawIndicators = true; +} + +void OLEDDisplayUi::disableAllIndicators(){ + this->shouldDrawIndicators = false; +} + +void OLEDDisplayUi::setIndicatorPosition(IndicatorPosition pos) { + this->indicatorPosition = pos; +} +void OLEDDisplayUi::setIndicatorDirection(IndicatorDirection dir) { + this->indicatorDirection = dir; +} +void OLEDDisplayUi::setActiveSymbol(const char* symbol) { + this->activeSymbol = symbol; +} +void OLEDDisplayUi::setInactiveSymbol(const char* symbol) { + this->inactiveSymbol = symbol; +} + + +// -/----- Frame settings -----\- +void OLEDDisplayUi::setFrameAnimation(AnimationDirection dir) { + this->frameAnimationDirection = dir; +} +void OLEDDisplayUi::setFrames(FrameCallback* frameFunctions, uint8_t frameCount) { + this->frameFunctions = frameFunctions; + this->frameCount = frameCount; + this->resetState(); +} + +// -/----- Overlays ------\- +void OLEDDisplayUi::setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount){ + this->overlayFunctions = overlayFunctions; + this->overlayCount = overlayCount; +} + +// -/----- Loading Process -----\- + +void OLEDDisplayUi::setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction) { + this->loadingDrawFunction = loadingDrawFunction; +} + +void OLEDDisplayUi::runLoadingProcess(LoadingStage* stages, uint8_t stagesCount) { + uint8_t progress = 0; + uint8_t increment = 100 / stagesCount; + + for (uint8_t i = 0; i < stagesCount; i++) { + display->clear(); + this->loadingDrawFunction(this->display, &stages[i], progress); + display->display(); + + stages[i].callback(); + + progress += increment; + optimistic_yield(10000); + } + + display->clear(); + this->loadingDrawFunction(this->display, &stages[stagesCount-1], progress); + display->display(); + + delay(150); +} + +// -/----- Manuel control -----\- +void OLEDDisplayUi::nextFrame() { + if (this->state.frameState != IN_TRANSITION) { + this->state.manuelControll = true; + this->state.frameState = IN_TRANSITION; + this->state.ticksSinceLastStateSwitch = 0; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.frameTransitionDirection = 1; + } +} +void OLEDDisplayUi::previousFrame() { + if (this->state.frameState != IN_TRANSITION) { + this->state.manuelControll = true; + this->state.frameState = IN_TRANSITION; + this->state.ticksSinceLastStateSwitch = 0; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.frameTransitionDirection = -1; + } +} + +void OLEDDisplayUi::switchToFrame(uint8_t frame) { + if (frame >= this->frameCount) return; + this->state.ticksSinceLastStateSwitch = 0; + if (frame == this->state.currentFrame) return; + this->state.frameState = FIXED; + this->state.currentFrame = frame; + this->state.isIndicatorDrawen = true; +} + +void OLEDDisplayUi::transitionToFrame(uint8_t frame) { + if (frame >= this->frameCount) return; + this->state.ticksSinceLastStateSwitch = 0; + if (frame == this->state.currentFrame) return; + this->nextFrameNumber = frame; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.manuelControll = true; + this->state.frameState = IN_TRANSITION; + this->state.frameTransitionDirection = frame < this->state.currentFrame ? -1 : 1; +} + + +// -/----- State information -----\- +OLEDDisplayUiState* OLEDDisplayUi::getUiState(){ + return &this->state; +} + + +int8_t OLEDDisplayUi::update(){ + long frameStart = millis(); + int8_t timeBudget = this->updateInterval - (frameStart - this->state.lastUpdate); + if ( timeBudget <= 0) { + // Implement frame skipping to ensure time budget is keept + if (this->autoTransition && this->state.lastUpdate != 0) this->state.ticksSinceLastStateSwitch += ceil(-timeBudget / this->updateInterval); + + this->state.lastUpdate = frameStart; + this->tick(); + } + return this->updateInterval - (millis() - frameStart); +} + + +void OLEDDisplayUi::tick() { + this->state.ticksSinceLastStateSwitch++; + + switch (this->state.frameState) { + case IN_TRANSITION: + if (this->state.ticksSinceLastStateSwitch >= this->ticksPerTransition){ + this->state.frameState = FIXED; + this->state.currentFrame = getNextFrameNumber(); + this->state.ticksSinceLastStateSwitch = 0; + this->nextFrameNumber = -1; + } + break; + case FIXED: + // Revert manuelControll + if (this->state.manuelControll) { + this->state.frameTransitionDirection = this->lastTransitionDirection; + this->state.manuelControll = false; + } + if (this->state.ticksSinceLastStateSwitch >= this->ticksPerFrame){ + if (this->autoTransition){ + this->state.frameState = IN_TRANSITION; + } + this->state.ticksSinceLastStateSwitch = 0; + } + break; + } + + this->display->clear(); + this->drawFrame(); + if (shouldDrawIndicators) { + this->drawIndicator(); + } + this->drawOverlays(); + this->display->display(); +} + +void OLEDDisplayUi::resetState() { + this->state.lastUpdate = 0; + this->state.ticksSinceLastStateSwitch = 0; + this->state.frameState = FIXED; + this->state.currentFrame = 0; + this->state.isIndicatorDrawen = true; +} + +void OLEDDisplayUi::drawFrame(){ + switch (this->state.frameState){ + case IN_TRANSITION: { + float progress = (float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition; + int16_t x, y, x1, y1; + switch(this->frameAnimationDirection){ + case SLIDE_LEFT: + x = -128 * progress; + y = 0; + x1 = x + 128; + y1 = 0; + break; + case SLIDE_RIGHT: + x = 128 * progress; + y = 0; + x1 = x - 128; + y1 = 0; + break; + case SLIDE_UP: + x = 0; + y = -64 * progress; + x1 = 0; + y1 = y + 64; + break; + case SLIDE_DOWN: + x = 0; + y = 64 * progress; + x1 = 0; + y1 = y - 64; + break; + } + + // Invert animation if direction is reversed. + int8_t dir = this->state.frameTransitionDirection >= 0 ? 1 : -1; + x *= dir; y *= dir; x1 *= dir; y1 *= dir; + + bool drawenCurrentFrame; + + + // Prope each frameFunction for the indicator Drawen state + this->enableIndicator(); + (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, x, y); + drawenCurrentFrame = this->state.isIndicatorDrawen; + + this->enableIndicator(); + (this->frameFunctions[this->getNextFrameNumber()])(this->display, &this->state, x1, y1); + + // Build up the indicatorDrawState + if (drawenCurrentFrame && !this->state.isIndicatorDrawen) { + // Drawen now but not next + this->indicatorDrawState = 2; + } else if (!drawenCurrentFrame && this->state.isIndicatorDrawen) { + // Not drawen now but next + this->indicatorDrawState = 1; + } else if (!drawenCurrentFrame && !this->state.isIndicatorDrawen) { + // Not drawen in both frames + this->indicatorDrawState = 3; + } + + // If the indicator isn't draw in the current frame + // reflect it in state.isIndicatorDrawen + if (!drawenCurrentFrame) this->state.isIndicatorDrawen = false; + + break; + } + case FIXED: + // Always assume that the indicator is drawn! + // And set indicatorDrawState to "not known yet" + this->indicatorDrawState = 0; + this->enableIndicator(); + (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, 0, 0); + break; + } +} + +void OLEDDisplayUi::drawIndicator() { + + // Only draw if the indicator is invisible + // for both frames or + // the indiactor is shown and we are IN_TRANSITION + if (this->indicatorDrawState == 3 || (!this->state.isIndicatorDrawen && this->state.frameState != IN_TRANSITION)) { + return; + } + + uint8_t posOfHighlightFrame; + float indicatorFadeProgress = 0; + + // if the indicator needs to be slided in we want to + // highlight the next frame in the transition + uint8_t frameToHighlight = this->indicatorDrawState == 1 ? this->getNextFrameNumber() : this->state.currentFrame; + + // Calculate the frame that needs to be highlighted + // based on the Direction the indiactor is drawn + switch (this->indicatorDirection){ + case LEFT_RIGHT: + posOfHighlightFrame = frameToHighlight; + break; + case RIGHT_LEFT: + posOfHighlightFrame = this->frameCount - frameToHighlight; + break; + } + + switch (this->indicatorDrawState) { + case 1: // Indicator was not drawn in this frame but will be in next + // Slide IN + indicatorFadeProgress = 1 - ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); + break; + case 2: // Indicator was drawn in this frame but not in next + // Slide OUT + indicatorFadeProgress = ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); + break; + } + + uint16_t frameStartPos = (12 * frameCount / 2); + const char *image; + uint16_t x,y; + for (byte i = 0; i < this->frameCount; i++) { + + switch (this->indicatorPosition){ + case TOP: + y = 0 - (8 * indicatorFadeProgress); + x = 64 - frameStartPos + 12 * i; + break; + case BOTTOM: + y = 56 + (8 * indicatorFadeProgress); + x = 64 - frameStartPos + 12 * i; + break; + case RIGHT: + x = 120 + (8 * indicatorFadeProgress); + y = 32 - frameStartPos + 2 + 12 * i; + break; + case LEFT: + x = 0 - (8 * indicatorFadeProgress); + y = 32 - frameStartPos + 2 + 12 * i; + break; + } + + if (posOfHighlightFrame == i) { + image = this->activeSymbol; + } else { + image = this->inactiveSymbol; + } + + this->display->drawFastImage(x, y, 8, 8, image); + } +} + +void OLEDDisplayUi::drawOverlays() { + for (uint8_t i=0;ioverlayCount;i++){ + (this->overlayFunctions[i])(this->display, &this->state); + } +} + +uint8_t OLEDDisplayUi::getNextFrameNumber(){ + if (this->nextFrameNumber != -1) return this->nextFrameNumber; + return (this->state.currentFrame + this->frameCount + this->state.frameTransitionDirection) % this->frameCount; +} diff --git a/libraries/oled-ssd1306/OLEDDisplayUi.h b/libraries/oled-ssd1306/OLEDDisplayUi.h new file mode 100644 index 00000000..35a1e99b --- /dev/null +++ b/libraries/oled-ssd1306/OLEDDisplayUi.h @@ -0,0 +1,305 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 by Daniel Eichhorn + * Copyright (c) 2016 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef OLEDDISPLAYUI_h +#define OLEDDISPLAYUI_h + +#include +#include "OLEDDisplay.h" + +//#define DEBUG_OLEDDISPLAYUI(...) Serial.printf( __VA_ARGS__ ) + +#ifndef DEBUG_OLEDDISPLAYUI +#define DEBUG_OLEDDISPLAYUI(...) +#endif + +enum AnimationDirection { + SLIDE_UP, + SLIDE_DOWN, + SLIDE_LEFT, + SLIDE_RIGHT +}; + +enum IndicatorPosition { + TOP, + RIGHT, + BOTTOM, + LEFT +}; + +enum IndicatorDirection { + LEFT_RIGHT, + RIGHT_LEFT +}; + +enum FrameState { + IN_TRANSITION, + FIXED +}; + + +const char ANIMATION_activeSymbol[] PROGMEM = { + 0x00, 0x18, 0x3c, 0x7e, 0x7e, 0x3c, 0x18, 0x00 +}; + +const char ANIMATION_inactiveSymbol[] PROGMEM = { + 0x00, 0x0, 0x0, 0x18, 0x18, 0x0, 0x0, 0x00 +}; + + +// Structure of the UiState +struct OLEDDisplayUiState { + uint64_t lastUpdate = 0; + uint16_t ticksSinceLastStateSwitch = 0; + + FrameState frameState = FIXED; + uint8_t currentFrame = 0; + + bool isIndicatorDrawen = true; + + // Normal = 1, Inverse = -1; + int8_t frameTransitionDirection = 1; + + bool manuelControll = false; + + // Custom data that can be used by the user + void* userData = NULL; +}; + +struct LoadingStage { + const char* process; + void (*callback)(); +}; + +typedef void (*FrameCallback)(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +typedef void (*OverlayCallback)(OLEDDisplay *display, OLEDDisplayUiState* state); +typedef void (*LoadingDrawFunction)(OLEDDisplay *display, LoadingStage* stage, uint8_t progress); + +class OLEDDisplayUi { + private: + OLEDDisplay *display; + + // Symbols for the Indicator + IndicatorPosition indicatorPosition = BOTTOM; + IndicatorDirection indicatorDirection = LEFT_RIGHT; + + const char* activeSymbol = ANIMATION_activeSymbol; + const char* inactiveSymbol = ANIMATION_inactiveSymbol; + + bool shouldDrawIndicators = true; + + // Values for the Frames + AnimationDirection frameAnimationDirection = SLIDE_RIGHT; + + int8_t lastTransitionDirection = 1; + + uint16_t ticksPerFrame = 151; // ~ 5000ms at 30 FPS + uint16_t ticksPerTransition = 15; // ~ 500ms at 30 FPS + + bool autoTransition = true; + + FrameCallback* frameFunctions; + uint8_t frameCount = 0; + + // Internally used to transition to a specific frame + int8_t nextFrameNumber = -1; + + // Values for Overlays + OverlayCallback* overlayFunctions; + uint8_t overlayCount = 0; + + // Will the Indicator be drawen + // 3 Not drawn in both frames + // 2 Drawn this frame but not next + // 1 Not drown this frame but next + // 0 Not known yet + uint8_t indicatorDrawState = 1; + + // Loading screen + LoadingDrawFunction loadingDrawFunction = [](OLEDDisplay *display, LoadingStage* stage, uint8_t progress) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_10); + display->drawString(64, 18, stage->process); + display->drawProgressBar(4, 32, 120, 8, progress); + }; + + // UI State + OLEDDisplayUiState state; + + // Bookeeping for update + uint8_t updateInterval = 33; + + uint8_t getNextFrameNumber(); + void drawIndicator(); + void drawFrame(); + void drawOverlays(); + void tick(); + void resetState(); + + public: + + OLEDDisplayUi(OLEDDisplay *display); + + /** + * Initialise the display + */ + void init(); + + /** + * Configure the internal used target FPS + */ + void setTargetFPS(uint8_t fps); + + // Automatic Controll + /** + * Enable automatic transition to next frame after the some time can be configured with `setTimePerFrame` and `setTimePerTransition`. + */ + void enableAutoTransition(); + + /** + * Disable automatic transition to next frame. + */ + void disableAutoTransition(); + + /** + * Set the direction if the automatic transitioning + */ + void setAutoTransitionForwards(); + void setAutoTransitionBackwards(); + + /** + * Set the approx. time a frame is displayed + */ + void setTimePerFrame(uint16_t time); + + /** + * Set the approx. time a transition will take + */ + void setTimePerTransition(uint16_t time); + + // Customize indicator position and style + + /** + * Draw the indicator. + * This is the defaut state for all frames if + * the indicator was hidden on the previous frame + * it will be slided in. + */ + void enableIndicator(); + + /** + * Don't draw the indicator. + * This will slide out the indicator + * when transitioning to the next frame. + */ + void disableIndicator(); + + /** + * Enable drawing of indicators + */ + void enableAllIndicators(); + + /** + * Disable draw of indicators. + */ + void disableAllIndicators(); + + /** + * Set the position of the indicator bar. + */ + void setIndicatorPosition(IndicatorPosition pos); + + /** + * Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING + */ + void setIndicatorDirection(IndicatorDirection dir); + + /** + * Set the symbol to indicate an active frame in the indicator bar. + */ + void setActiveSymbol(const char* symbol); + + /** + * Set the symbol to indicate an inactive frame in the indicator bar. + */ + void setInactiveSymbol(const char* symbol); + + + // Frame settings + + /** + * Configure what animation is used to transition from one frame to another + */ + void setFrameAnimation(AnimationDirection dir); + + /** + * Add frame drawing functions + */ + void setFrames(FrameCallback* frameFunctions, uint8_t frameCount); + + // Overlay + + /** + * Add overlays drawing functions that are draw independent of the Frames + */ + void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount); + + + // Loading animation + /** + * Set the function that will draw each step + * in the loading animation + */ + void setLoadingDrawFunction(LoadingDrawFunction loadingFunction); + + + /** + * Run the loading process + */ + void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount); + + + // Manual Control + void nextFrame(); + void previousFrame(); + + /** + * Switch without transition to frame `frame`. + */ + void switchToFrame(uint8_t frame); + + /** + * Transition to frame `frame`, when the `frame` number is bigger than the current + * frame the forward animation will be used, otherwise the backwards animation is used. + */ + void transitionToFrame(uint8_t frame); + + // State Info + OLEDDisplayUiState* getUiState(); + + int8_t update(); +}; +#endif diff --git a/libraries/oled-ssd1306/README.md b/libraries/oled-ssd1306/README.md new file mode 100644 index 00000000..01c134c3 --- /dev/null +++ b/libraries/oled-ssd1306/README.md @@ -0,0 +1,393 @@ +esp8266-oled-ssd1306 [![Build Status](https://travis-ci.org/squix78/esp8266-oled-ssd1306.svg?branch=dev-branch-3.0.0)](https://travis-ci.org/squix78/esp8266-oled-ssd1306) +============ + +> We just released version 3.0.0. Please have a look at our [upgrade guide](UPGRADE-3.0.md) + +This is a driver for the SSD1306 based 128x64 pixel OLED display running on the Arduino/ESP8266 platform. +Can be used with either the I2C or SPI version of the display + +You can either download this library as a zip file and unpack it to your Arduino/libraries folder or (once it has been added) choose it from the Arduino library manager. + +It is also available as a platformio library. Just execute the following command: +``` +platformio lib install 562 +``` + +## Credits +This library has initially been written by Daniel Eichhorn (@squix78). Many thanks go to Fabrice Weinberg (@FWeinb) for optimizing and refactoring many aspects of the library. Also many thanks to the many committers who helped to add new features and who fixed many bugs. +The init sequence for the SSD1306 was inspired by Adafruit's library for the same display. + +## Usage + +Check out the examples folder for a few comprehensive demonstrations how to use the library. Also check out the ESP8266 Weather Station library (https://github.com/squix78/esp8266-weather-station) which uses the OLED library to display beautiful weather information. + +## Upgrade + +The API changed a lot with the 3.0 release. If you were using this library with older versions please have a look at the [Upgrade Guide](UPGRADE-3.0.md). + +## Features + +* Draw pixels at given coordinates +* Draw lines from given coordinates to given coordinates +* Draw or fill a rectangle with given dimensions +* Draw Text at given coordinates: + * Define Alignment: Left, Right and Center + * Set the Fontface you want to use (see section Fonts below) + * Limit the width of the text by an amount of pixels. Before this widths will be reached, the renderer will wrap the text to a new line if possible +* Display content in automatically side scrolling carousel + * Define transition cycles + * Define how long one frame will be displayed + * Draw the different frames in callback methods + * One indicator per frame will be automatically displayed. The active frame will be displayed from inactive once + +## Fonts + +Fonts are defined in a proprietary but open format. You can create new font files by choosing from a given list +of open sourced Fonts from this web app: http://oleddisplay.squix.ch +Choose the font family, style and size, check the preview image and if you like what you see click the "Create" button. This will create the font array in a text area form where you can copy and paste it into a new or existing header file. + + +![FontTool](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/FontTool.png) + +## Hardware Abstraction + +The library supports different protocols to access the OLED display. Currently there is support for I2C using the built in Wire.h library, I2C by using the much faster BRZO I2C library [https://github.com/pasko-zh/brzo_i2c] written in assembler and it also supports displays which come with the SPI interface. + +### I2C with Wire.h + +```C++ +#include +#include "SSD1306.h" + +SSD1306 display(ADDRESS, SDA, SDC); +``` +or for a SH1106: +```C++ +#include +#include "SH1106.h" + +SH1106 display(ADDRESS, SDA, SDC); +``` + +### I2C with brzo_i2c + +```C++ +#include +#include "SSD1306Brzo.h" + +SSD1306Brzo display(ADDRESS, SDA, SDC); +``` +or for the SH1106: +```C++ +#include +#include "SH1106Brzo.h" + +SH1106Brzo display(ADDRESS, SDA, SDC); +``` + +### SPI + +```C++ +#include +#include "SSD1306Spi.h" + +SSD1306Spi display(RES, DC, CS); +``` +or for the SH1106: +```C++ +#include +#include "SH1106Spi.h" + +SH1106Spi display(RES, DC, CS); +``` + +## API + +### Display Control + +```C++ +// Initialize the display +void init(); + +// Free the memory used by the display +void end(); + +// Cycle through the initialization +void resetDisplay(void); + +// Connect again to the display through I2C +void reconnect(void); + +// Turn the display on +void displayOn(void); + +// Turn the display offs +void displayOff(void); + +// Clear the local pixel buffer +void clear(void); + +// Write the buffer to the display memory +void display(void); + +// Inverted display mode +void invertDisplay(void); + +// Normal display mode +void normalDisplay(void); + +// Set display contrast +void setContrast(char contrast); + +// Turn the display upside down +void flipScreenVertically(); +``` + +## Pixel drawing + +```C++ + +/* Drawing functions */ +// Sets the color of all pixel operations +void setColor(OLEDDISPLAY_COLOR color); + +// Draw a pixel at given position +void setPixel(int16_t x, int16_t y); + +// Draw a line from position 0 to position 1 +void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1); + +// Draw the border of a rectangle at the given location +void drawRect(int16_t x, int16_t y, int16_t width, int16_t height); + +// Fill the rectangle +void fillRect(int16_t x, int16_t y, int16_t width, int16_t height); + +// Draw the border of a circle +void drawCircle(int16_t x, int16_t y, int16_t radius); + +// Fill circle +void fillCircle(int16_t x, int16_t y, int16_t radius); + +// Draw a line horizontally +void drawHorizontalLine(int16_t x, int16_t y, int16_t length); + +// Draw a lin vertically +void drawVerticalLine(int16_t x, int16_t y, int16_t length); + +// Draws a rounded progress bar with the outer dimensions given by width and height. Progress is +// a unsigned byte value between 0 and 100 +void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress); + +// Draw a bitmap in the internal image format +void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const char *image); + +// Draw a XBM +void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const char* xbm); +``` + +## Text operations + +``` C++ +void drawString(int16_t x, int16_t y, String text); + +// Draws a String with a maximum width at the given location. +// If the given String is wider than the specified width +// The text will be wrapped to the next line at a space or dash +void drawStringMaxWidth(int16_t x, int16_t y, int16_t maxLineWidth, String text); + +// Returns the width of the const char* with the current +// font settings +uint16_t getStringWidth(const char* text, uint16_t length); + +// Convencience method for the const char version +uint16_t getStringWidth(String text); + +// Specifies relative to which anchor point +// the text is rendered. Available constants: +// TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH +void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment); + +// Sets the current font. Available default fonts +// ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24 +// Or create one with the font tool at http://oleddisplay.squix.ch +void setFont(const char* fontData); +``` + +## Ui Library (OLEDDisplayUi) + +The Ui Library is used to provide a basic set of Ui elements called, `Frames` and `Overlays`. A `Frame` is used to provide +information the default behaviour is to display a `Frame` for a defined time and than move to the next. The library also provides an `Indicator` that will be updated accordingly. An `Overlay` on the other hand is a pieces of information (e.g. a clock) that is displayed always at the same position. + + +```C++ +/** + * Initialise the display + */ +void init(); + +/** + * Configure the internal used target FPS + */ +void setTargetFPS(uint8_t fps); + +/** + * Enable automatic transition to next frame after the some time can be configured with + * `setTimePerFrame` and `setTimePerTransition`. + */ +void enableAutoTransition(); + +/** + * Disable automatic transition to next frame. + */ +void disableAutoTransition(); + +/** + * Set the direction if the automatic transitioning + */ +void setAutoTransitionForwards(); +void setAutoTransitionBackwards(); + +/** + * Set the approx. time a frame is displayed + */ +void setTimePerFrame(uint16_t time); + +/** + * Set the approx. time a transition will take + */ +void setTimePerTransition(uint16_t time); + +/** + * Draw the indicator. + * This is the default state for all frames if + * the indicator was hidden on the previous frame + * it will be slided in. + */ +void enableIndicator(); + +/** + * Don't draw the indicator. + * This will slide out the indicator + * when transitioning to the next frame. + */ +void disableIndicator(); + +/** + * Enable drawing of all indicators. + */ +void enableAllIndicators(); + +/** + * Disable drawing of all indicators. + */ +void disableAllIndicators(); + +/** + * Set the position of the indicator bar. + */ +void setIndicatorPosition(IndicatorPosition pos); + +/** + * Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING + */ +void setIndicatorDirection(IndicatorDirection dir); + +/** + * Set the symbol to indicate an active frame in the indicator bar. + */ +void setActiveSymbol(const char* symbol); + +/** + * Set the symbol to indicate an inactive frame in the indicator bar. + */ +void setInactiveSymbol(const char* symbol); + +/** + * Configure what animation is used to transition from one frame to another + */ +void setFrameAnimation(AnimationDirection dir); + +/** + * Add frame drawing functions + */ +void setFrames(FrameCallback* frameFunctions, uint8_t frameCount); + +/** + * Add overlays drawing functions that are draw independent of the Frames + */ +void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount); + +/** + * Set the function that will draw each step + * in the loading animation + */ +void setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction); + +/** + * Run the loading process + */ +void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount); + +// Manuell Controll +void nextFrame(); +void previousFrame(); + +/** + * Switch without transition to frame `frame`. + */ +void switchToFrame(uint8_t frame); + +/** + * Transition to frame `frame`, when the `frame` number is bigger than the current + * frame the forward animation will be used, otherwise the backwards animation is used. + */ +void transitionToFrame(uint8_t frame); + +// State Info +OLEDDisplayUiState* getUiState(); + +// This needs to be called in the main loop +// the returned value is the remaining time (in ms) +// you have to draw after drawing to keep the frame budget. +int8_t update(); +``` + +## Example: SSD1306Demo + +### Frame 1 +![DemoFrame1](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame1.jpg) + +This frame shows three things: + * How to draw an xbm image + * How to draw a static text which is not moved by the frame transition + * The active/inactive frame indicators + +### Frame 2 +![DemoFrame2](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame2.jpg) + +Currently there are one fontface with three sizes included in the library: Arial 10, 16 and 24. Once the converter is published you will be able to convert any ttf font into the used format. + +### Frame 3 + +![DemoFrame3](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame3.jpg) + +This frame demonstrates the text alignment. The coordinates in the frame show relative to which position the texts have been rendered. + +### Frame 4 + +![DemoFrame4](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame4.jpg) + +This shows how to use define a maximum width after which the driver automatically wraps a word to the next line. This comes in very handy if you have longer texts to display. + +### SPI version + +![SPIVersion](https://github.com/neptune2/esp8266-oled-ssd1306/raw/master/resources/SPI_version.jpg) + +This shows the code working on the SPI version of the display. See demo code for ESP8266 pins used. + +## Project using this library + + * [QRCode ESP8266](https://github.com/anunpanya/ESP8266_QRcode) (by @anunpanya) + * [Scan I2C](https://github.com/hallard/Scan-I2C-WiFi) (by @hallard) + * [Weather Station](https://github.com/squix78/esp8266-weather-station) (by @squix) diff --git a/libraries/oled-ssd1306/SH1106.h b/libraries/oled-ssd1306/SH1106.h new file mode 100644 index 00000000..55dd4090 --- /dev/null +++ b/libraries/oled-ssd1306/SH1106.h @@ -0,0 +1,36 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 by Daniel Eichhorn + * Copyright (c) 2016 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing! + */ + +#ifndef SH1106_h +#define SH1106_h +#include "SH1106Wire.h" + +// For make SH1106 an alias for SH1106Wire +typedef SH1106Wire SH1106; + + +#endif diff --git a/libraries/oled-ssd1306/SH1106Brzo.h b/libraries/oled-ssd1306/SH1106Brzo.h new file mode 100644 index 00000000..4270066d --- /dev/null +++ b/libraries/oled-ssd1306/SH1106Brzo.h @@ -0,0 +1,133 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 by Daniel Eichhorn + * Copyright (c) 2016 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing! + */ + +#ifndef SH1106Brzo_h +#define SH1106Brzo_h + +#include "OLEDDisplay.h" +#include + +#if F_CPU == 160000000L + #define BRZO_I2C_SPEED 1000 +#else + #define BRZO_I2C_SPEED 800 +#endif + +class SH1106Brzo : public OLEDDisplay { + private: + uint8_t _address; + uint8_t _sda; + uint8_t _scl; + + public: + SH1106Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl) { + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; + } + + bool connect(){ + brzo_i2c_setup(_sda, _scl, 0); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = ~0; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = ~0; + uint8_t maxBoundX = 0; + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (DISPLAY_HEIGHT / 8); y++) { + for (x = 0; x < DISPLAY_WIDTH; x++) { + uint16_t pos = x + y * DISPLAY_WIDTH; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + optimistic_yield(10000); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == ~0) return; + + byte k = 0; + uint8_t sendBuffer[17]; + sendBuffer[0] = 0x40; + + // Calculate the colum offset + uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F; + uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 ); + + brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED); + + for (y = minBoundY; y <= maxBoundY; y++) { + sendCommand(0xB0 + y); + sendCommand(minBoundXp2H); + sendCommand(minBoundXp2L); + for (x = minBoundX; x <= maxBoundX; x++) { + k++; + sendBuffer[k] = buffer[x + y * DISPLAY_WIDTH]; + if (k == 16) { + brzo_i2c_write(sendBuffer, 17, true); + k = 0; + } + } + if (k != 0) { + brzo_i2c_write(sendBuffer, k + 1, true); + k = 0; + } + optimistic_yield(10000); + } + if (k != 0) { + brzo_i2c_write(sendBuffer, k + 1, true); + } + brzo_i2c_end_transaction(); + #else + #endif + } + + private: + inline void sendCommand(uint8_t com) __attribute__((always_inline)){ + uint8_t command[2] = {0x80 /* command mode */, com}; + brzo_i2c_start_transaction(_address, BRZO_I2C_SPEED); + brzo_i2c_write(command, 2, true); + brzo_i2c_end_transaction(); + } +}; + +#endif diff --git a/libraries/oled-ssd1306/SH1106Spi.h b/libraries/oled-ssd1306/SH1106Spi.h new file mode 100644 index 00000000..48d2cfba --- /dev/null +++ b/libraries/oled-ssd1306/SH1106Spi.h @@ -0,0 +1,128 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 by Daniel Eichhorn + * Copyright (c) 2016 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing! + */ + +#ifndef SH1106Spi_h +#define SH1106Spi_h + +#include "OLEDDisplay.h" +#include + +class SH1106Spi : public OLEDDisplay { + private: + uint8_t _rst; + uint8_t _dc; + + public: + + SH1106Spi(uint8_t _rst, uint8_t _dc) { + this->_rst = _rst; + this->_dc = _dc; + } + + bool connect(){ + pinMode(_dc, OUTPUT); + pinMode(_rst, OUTPUT); + + SPI.begin (); + SPI.setClockDivider (SPI_CLOCK_DIV2); + + // Pulse Reset low for 10ms + digitalWrite(_rst, HIGH); + delay(1); + digitalWrite(_rst, LOW); + delay(10); + digitalWrite(_rst, HIGH); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = ~0; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = ~0; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (DISPLAY_HEIGHT / 8); y++) { + for (x = 0; x < DISPLAY_WIDTH; x++) { + uint16_t pos = x + y * DISPLAY_WIDTH; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + optimistic_yield(10000); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == ~0) return; + + // Calculate the colum offset + uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F; + uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 ); + + for (y = minBoundY; y <= maxBoundY; y++) { + sendCommand(0xB0 + y); + sendCommand(minBoundXp2H); + sendCommand(minBoundXp2L); + digitalWrite(_dc, HIGH); // data mode + for (x = minBoundX; x <= maxBoundX; x++) { + SPI.transfer(buffer[x + y * DISPLAY_WIDTH]); + } + optimistic_yield(10000); + } + #else + for (uint8_t y=0; y + +#define SH1106_SET_PUMP_VOLTAGE 0X30 +#define SH1106_SET_PUMP_MODE 0XAD +#define SH1106_PUMP_ON 0X8B +#define SH1106_PUMP_OFF 0X8A +//-------------------------------------- + +class SH1106Wire : public OLEDDisplay { + private: + uint8_t _address; + uint8_t _sda; + uint8_t _scl; + + public: + SH1106Wire(uint8_t _address, uint8_t _sda, uint8_t _scl) { + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; + } + + bool connect() { + Wire.begin(this->_sda, this->_scl); + // Let's use ~700khz if ESP8266 is in 160Mhz mode + // this will be limited to ~400khz if the ESP8266 in 80Mhz mode. + Wire.setClock(700000); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = ~0; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = ~0; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (DISPLAY_HEIGHT / 8); y++) { + for (x = 0; x < DISPLAY_WIDTH; x++) { + uint16_t pos = x + y * DISPLAY_WIDTH; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + optimistic_yield(10000); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == ~0) return; + + // Calculate the colum offset + uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F; + uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 ); + + byte k = 0; + for (y = minBoundY; y <= maxBoundY; y++) { + sendCommand(0xB0 + y); + sendCommand(minBoundXp2H); + sendCommand(minBoundXp2L); + for (x = minBoundX; x <= maxBoundX; x++) { + if (k == 0) { + Wire.beginTransmission(_address); + Wire.write(0x40); + } + Wire.write(buffer[x + y * DISPLAY_WIDTH]); + k++; + if (k == 16) { + Wire.endTransmission(); + k = 0; + } + } + if (k != 0) { + Wire.endTransmission(); + k = 0; + } + optimistic_yield(10000); + } + + if (k != 0) { + Wire.endTransmission(); + } + #else + uint8_t * p = &buffer[0]; + for (uint8_t y=0; y<8; y++) { + sendCommand(0xB0+y); + sendCommand(0x02); + sendCommand(0x10); + for( uint8_t x=0; x<8; x++) { + Wire.beginTransmission(_address); + Wire.write(0x40); + for (uint8_t k = 0; k < 16; k++) { + Wire.write(*p++); + } + Wire.endTransmission(); + } + } + #endif + } + + private: + inline void sendCommand(uint8_t command) __attribute__((always_inline)){ + Wire.beginTransmission(_address); + Wire.write(0x80); + Wire.write(command); + Wire.endTransmission(); + } + + +}; + +#endif diff --git a/libraries/oled-ssd1306/SSD1306.h b/libraries/oled-ssd1306/SSD1306.h new file mode 100644 index 00000000..f3b79094 --- /dev/null +++ b/libraries/oled-ssd1306/SSD1306.h @@ -0,0 +1,36 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 by Daniel Eichhorn + * Copyright (c) 2016 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing! + */ + +#ifndef SSD1306_h +#define SSD1306_h +#include "SSD1306Wire.h" + +// For legacy support make SSD1306 an alias for SSD1306 +typedef SSD1306Wire SSD1306; + + +#endif diff --git a/libraries/oled-ssd1306/SSD1306Brzo.h b/libraries/oled-ssd1306/SSD1306Brzo.h new file mode 100644 index 00000000..1cccd5da --- /dev/null +++ b/libraries/oled-ssd1306/SSD1306Brzo.h @@ -0,0 +1,149 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 by Daniel Eichhorn + * Copyright (c) 2016 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing! + */ + +#ifndef SSD1306Brzo_h +#define SSD1306Brzo_h + +#include "OLEDDisplay.h" +#include + +#if F_CPU == 160000000L + #define BRZO_I2C_SPEED 1000 +#else + #define BRZO_I2C_SPEED 800 +#endif + +class SSD1306Brzo : public OLEDDisplay { + private: + uint8_t _address; + uint8_t _sda; + uint8_t _scl; + + public: + SSD1306Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl) { + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; + } + + bool connect(){ + brzo_i2c_setup(_sda, _scl, 0); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = ~0; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = ~0; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (DISPLAY_HEIGHT / 8); y++) { + for (x = 0; x < DISPLAY_WIDTH; x++) { + uint16_t pos = x + y * DISPLAY_WIDTH; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + optimistic_yield(10000); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == ~0) return; + + sendCommand(COLUMNADDR); + sendCommand(minBoundX); + sendCommand(maxBoundX); + + sendCommand(PAGEADDR); + sendCommand(minBoundY); + sendCommand(maxBoundY); + + byte k = 0; + uint8_t sendBuffer[17]; + sendBuffer[0] = 0x40; + brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED); + for (y = minBoundY; y <= maxBoundY; y++) { + for (x = minBoundX; x <= maxBoundX; x++) { + k++; + sendBuffer[k] = buffer[x + y * DISPLAY_WIDTH]; + if (k == 16) { + brzo_i2c_write(sendBuffer, 17, true); + k = 0; + } + } + optimistic_yield(10000); + } + brzo_i2c_write(sendBuffer, k + 1, true); + brzo_i2c_end_transaction(); + #else + // No double buffering + sendCommand(COLUMNADDR); + sendCommand(0x0); + sendCommand(0x7F); + + sendCommand(PAGEADDR); + sendCommand(0x0); + sendCommand(0x7); + + uint8_t sendBuffer[17]; + sendBuffer[0] = 0x40; + brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED); + for (uint16_t i=0; i + +#if F_CPU == 160000000L + #define BRZO_I2C_SPEED 1000 +#else + #define BRZO_I2C_SPEED 800 +#endif + +class SSD1306Spi : public OLEDDisplay { + private: + uint8_t _rst; + uint8_t _dc; + uint8_t _cs; + + public: + SSD1306Spi(uint8_t _rst, uint8_t _dc, uint8_t _cs) { + this->_rst = _rst; + this->_dc = _dc; + this->_cs = _cs; + } + + bool connect(){ + pinMode(_dc, OUTPUT); + pinMode(_cs, OUTPUT); + pinMode(_rst, OUTPUT); + + SPI.begin (); + SPI.setClockDivider (SPI_CLOCK_DIV2); + + // Pulse Reset low for 10ms + digitalWrite(_rst, HIGH); + delay(1); + digitalWrite(_rst, LOW); + delay(10); + digitalWrite(_rst, HIGH); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = ~0; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = ~0; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (DISPLAY_HEIGHT / 8); y++) { + for (x = 0; x < DISPLAY_WIDTH; x++) { + uint16_t pos = x + y * DISPLAY_WIDTH; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + optimistic_yield(10000); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == ~0) return; + + sendCommand(COLUMNADDR); + sendCommand(minBoundX); + sendCommand(maxBoundX); + + sendCommand(PAGEADDR); + sendCommand(minBoundY); + sendCommand(maxBoundY); + + digitalWrite(_cs, HIGH); + digitalWrite(_dc, HIGH); // data mode + digitalWrite(_cs, LOW); + for (y = minBoundY; y <= maxBoundY; y++) { + for (x = minBoundX; x <= maxBoundX; x++) { + SPI.transfer(buffer[x + y * DISPLAY_WIDTH]); + } + optimistic_yield(10000); + } + digitalWrite(_cs, HIGH); + #else + // No double buffering + sendCommand(COLUMNADDR); + sendCommand(0x0); + sendCommand(0x7F); + + sendCommand(PAGEADDR); + sendCommand(0x0); + sendCommand(0x7); + + digitalWrite(_cs, HIGH); + digitalWrite(_dc, HIGH); // data mode + digitalWrite(_cs, LOW); + for (uint16_t i=0; i + +class SSD1306Wire : public OLEDDisplay { + private: + uint8_t _address; + uint8_t _sda; + uint8_t _scl; + + public: + SSD1306Wire(uint8_t _address, uint8_t _sda, uint8_t _scl) { + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; + } + + bool connect() { + Wire.begin(this->_sda, this->_scl); + // Let's use ~700khz if ESP8266 is in 160Mhz mode + // this will be limited to ~400khz if the ESP8266 in 80Mhz mode. + Wire.setClock(700000); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = ~0; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = ~0; + uint8_t maxBoundX = 0; + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (DISPLAY_HEIGHT / 8); y++) { + for (x = 0; x < DISPLAY_WIDTH; x++) { + uint16_t pos = x + y * DISPLAY_WIDTH; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + optimistic_yield(10000); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == ~0) return; + + sendCommand(COLUMNADDR); + sendCommand(minBoundX); + sendCommand(maxBoundX); + + sendCommand(PAGEADDR); + sendCommand(minBoundY); + sendCommand(maxBoundY); + + byte k = 0; + for (y = minBoundY; y <= maxBoundY; y++) { + for (x = minBoundX; x <= maxBoundX; x++) { + if (k == 0) { + Wire.beginTransmission(_address); + Wire.write(0x40); + } + Wire.write(buffer[x + y * DISPLAY_WIDTH]); + k++; + if (k == 16) { + Wire.endTransmission(); + k = 0; + } + } + optimistic_yield(10000); + } + + if (k != 0) { + Wire.endTransmission(); + } + #else + + sendCommand(COLUMNADDR); + sendCommand(0x0); + sendCommand(0x7F); + + sendCommand(PAGEADDR); + sendCommand(0x0); + sendCommand(0x7); + + for (uint16_t i=0; i < DISPLAY_BUFFER_SIZE; i++) { + Wire.beginTransmission(this->_address); + Wire.write(0x40); + for (uint8_t x = 0; x < 16; x++) { + Wire.write(buffer[i]); + i++; + } + i--; + Wire.endTransmission(); + } + #endif + } + + private: + inline void sendCommand(uint8_t command) __attribute__((always_inline)){ + Wire.beginTransmission(_address); + Wire.write(0x80); + Wire.write(command); + Wire.endTransmission(); + } + + +}; + +#endif diff --git a/libraries/oled-ssd1306/UPGRADE-3.0.md b/libraries/oled-ssd1306/UPGRADE-3.0.md new file mode 100644 index 00000000..e7a315bc --- /dev/null +++ b/libraries/oled-ssd1306/UPGRADE-3.0.md @@ -0,0 +1,125 @@ +# Upgrade from 2.0 to 3.0 + +While developing version 3.0 we made some breaking changes to the public +API of this library. This document will help you update your code to work with +version 3.0 + +## Font Definitions + +To get better performance and a smaller font definition format, we change the memory +layout of the font definition format. If you are using custom fonts not included in +this library we updated the font generator [here](http://oleddisplay.squix.ch/#/home). +Please update your fonts to be working with 3.0 by selecting the respective version in the dropdown. + + +## Architectural Changes + +To become a more versatile library for the SSD1306 chipset we abstracted the +hardware connection into subclasses of the base display class now called `OLEDDisplay`. +This library is currently shipping with three implementations: + + * `SSD1306Wire` implementing the I2C protocol using the Wire Library. + * `SSD1306Brzo` implementing the I2C protocol using the faster [`brzo_i2c`](https://github.com/pasko-zh/brzo_i2c) library. + * `SSD1306Spi` implementing the SPI protocol. + +To keep backwards compatiblity with the old API `SSD1306` is an alias of `SSD1306Wire`. +If you are not using the UI components you don't have to change anything to keep your code working. + +## Name Changes + +[Naming things is hard](http://martinfowler.com/bliki/TwoHardThings.html), to better reflect our intention with this library +we changed the name of the base class to `OLEDDisplay` and the UI library accordingly to `OLEDDisplayUi`. +As a consequence the type definitions of all frame and overlay related functions changed. +This means that you have to update all your frame drawing callbacks from: + +```c +bool frame1(SSD1306 *display, SSD1306UiState* state, int x, int y); +``` + +too + +```c +void frame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +``` + +And your overlay drawing functions from: + +```c +bool overlay1(SSD1306 *display, SSD1306UiState* state); +``` + +too + +```c +void overlay1(OLEDDisplay *display, OLEDDisplayUiState* state); +``` + +## New Features + +### Loading Animation + +While using this library ourself we noticed a pattern emerging. We want to drawing +a loading progress while connecting to WiFi and updating weather data etc. + +The simplest thing was to add the function `drawProgressBar(x, y, width, height, progress)` +,where `progress` is between `0` and `100`, right to the `OLEDDisplay` class. + +But we didn't stop there. We added a new feature to the `OLEDDisplayUi` called `LoadingStages`. +You can define your loading process like this: + +```c++ +LoadingStage loadingStages[] = { + { + .process = "Connect to WiFi", + .callback = []() { + // Connect to WiFi + } + }, + { + .process = "Get time from NTP", + .callback = []() { + // Get current time via NTP + } + } + // more steps +}; + +int LOADING_STAGES_COUNT = sizeof(loadingStages) / sizeof(LoadingStage); +``` + +After defining your array of `LoadingStages` you can then run the loading process by using +`ui.runLoadingProcess(loadingStages, LOADING_STAGES_COUNT)`. This will give you a +nice little loading animation you can see in the beginning of [this](https://vimeo.com/168362918) +video. + +To further customize this you are free to define your own `LoadingDrawFunction` like this: + +```c +void myLoadingDraw(OLEDDisplay *display, LoadingStage* stage, uint8_t progress) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_10); + // stage->process contains the text of the current progress e.q. "Connect to WiFi" + display->drawString(64, 18, stage->process); + // you could just print the current process without the progress bar + display->drawString(64, 28, progress); +} +``` + +After defining a function like that, you can pass it to the Ui library by use +`ui.setLoadingDrawFunction(myLoadingDraw)`. + + +### Text Logging + +It is always useful to display some text on the display without worrying to much +where it goes and managing it. In 3.0 we made the `OLEDDisplay` class implement +[`Print`](https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/Print.h) +so you can use it like you would use `Serial`. We calls this feature `LogBuffer` +and the only thing you have to do is to define how many lines you want to display +and how many characters there are on average on each. This is done by calling +`setLogBuffer(lines, chars);`. If there is not enough memory the function will +return false. + +After that you can draw the `LogBuffer` anywhere you want by calling `drawLogBuffer(x, y)`. +(Note: You have to call `display()` to update the screen) +We made a [video](https://www.youtube.com/watch?v=8Fiss77A3TE) showing this feature in action. diff --git a/libraries/oled-ssd1306/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino b/libraries/oled-ssd1306/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino new file mode 100644 index 00000000..e9db7d64 --- /dev/null +++ b/libraries/oled-ssd1306/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino @@ -0,0 +1,211 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 by Daniel Eichhorn + * Copyright (c) 2016 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306.h" // alias for `#include "SSD1306Wire.h"` +// or #include "SH1106.h" alis for `#include "SH1106Wire.h"` +// For a connection via I2C using brzo_i2c (must be installed) include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// #include "SH1106Brzo.h" +// For a connection via SPI include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// #include "SH1106SPi.h" + +// Include the UI lib +#include "OLEDDisplayUi.h" + +// Include custom images +#include "images.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306 display(0x3c, D3, D5); +// SH1106 display(0x3c, D3, D5); + +OLEDDisplayUi ui ( &display ); + +int screenW = 128; +int screenH = 64; +int clockCenterX = screenW/2; +int clockCenterY = ((screenH-16)/2)+16; // top yellow part is 16 px height +int clockRadius = 23; + +// utility function for digital clock display: prints leading 0 +String twoDigits(int digits){ + if(digits < 10) { + String i = '0'+String(digits); + return i; + } + else { + return String(digits); + } +} + +void clockOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { + +} + +void analogClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { +// ui.disableIndicator(); + + // Draw the clock face +// display->drawCircle(clockCenterX + x, clockCenterY + y, clockRadius); + display->drawCircle(clockCenterX + x, clockCenterY + y, 2); + // + //hour ticks + for( int z=0; z < 360;z= z + 30 ){ + //Begin at 0° and stop at 360° + float angle = z ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + int x2 = ( clockCenterX + ( sin(angle) * clockRadius ) ); + int y2 = ( clockCenterY - ( cos(angle) * clockRadius ) ); + int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) ); + int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) ); + display->drawLine( x2 + x , y2 + y , x3 + x , y3 + y); + } + + // display second hand + float angle = second() * 6 ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) ); + int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) ); + display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y); + // + // display minute hand + angle = minute() * 6 ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) ); + y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) ); + display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y); + // + // display hour hand + angle = hour() * 30 + int( ( minute() / 12 ) * 6 ) ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) ); + y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) ); + display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y); +} + +void digitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + String timenow = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second()); + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_24); + display->drawString(clockCenterX + x , clockCenterY + y, timenow ); +} + +// This array keeps function pointers to all frames +// frames are the single views that slide in +FrameCallback frames[] = { analogClockFrame, digitalClockFrame }; + +// how many frames are there? +int frameCount = 2; + +// Overlays are statically drawn on top of a frame eg. a clock +OverlayCallback overlays[] = { clockOverlay }; +int overlaysCount = 1; + +void setup() { + Serial.begin(9600); + Serial.println(); + + // The ESP is capable of rendering 60fps in 80Mhz mode + // but that won't give you much time for anything else + // run it in 160Mhz mode or just set it to 30 fps + ui.setTargetFPS(60); + + // Customize the active and inactive symbol + ui.setActiveSymbol(activeSymbol); + ui.setInactiveSymbol(inactiveSymbol); + + // You can change this to + // TOP, LEFT, BOTTOM, RIGHT + ui.setIndicatorPosition(TOP); + + // Defines where the first frame is located in the bar. + ui.setIndicatorDirection(LEFT_RIGHT); + + // You can change the transition that is used + // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN + ui.setFrameAnimation(SLIDE_LEFT); + + // Add frames + ui.setFrames(frames, frameCount); + + // Add overlays + ui.setOverlays(overlays, overlaysCount); + + // Initialising the UI will init the display too. + ui.init(); + + display.flipScreenVertically(); + + unsigned long secsSinceStart = millis(); + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const unsigned long seventyYears = 2208988800UL; + // subtract seventy years: + unsigned long epoch = secsSinceStart - seventyYears * SECS_PER_HOUR; + setTime(epoch); + +} + + +void loop() { + int remainingTimeBudget = ui.update(); + + if (remainingTimeBudget > 0) { + // You can do some work here + // Don't do stuff if you are below your + // time budget. + delay(remainingTimeBudget); + + } + + +} diff --git a/libraries/oled-ssd1306/examples/SSD1306ClockDemo/images.h b/libraries/oled-ssd1306/examples/SSD1306ClockDemo/images.h new file mode 100644 index 00000000..a220a27c --- /dev/null +++ b/libraries/oled-ssd1306/examples/SSD1306ClockDemo/images.h @@ -0,0 +1,21 @@ +const char activeSymbol[] PROGMEM = { + B00000000, + B00000000, + B00011000, + B00100100, + B01000010, + B01000010, + B00100100, + B00011000 +}; + +const char inactiveSymbol[] PROGMEM = { + B00000000, + B00000000, + B00000000, + B00000000, + B00011000, + B00011000, + B00000000, + B00000000 +}; diff --git a/libraries/oled-ssd1306/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino b/libraries/oled-ssd1306/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino new file mode 100644 index 00000000..cf37fb04 --- /dev/null +++ b/libraries/oled-ssd1306/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino @@ -0,0 +1,229 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 by Daniel Eichhorn + * Copyright (c) 2016 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + + // Include the correct display library + // For a connection via I2C using Wire include + #include // Only needed for Arduino 1.6.5 and earlier + #include "SSD1306.h" // alias for `#include "SSD1306Wire.h"` + // or #include "SH1106.h" alis for `#include "SH1106Wire.h"` + // For a connection via I2C using brzo_i2c (must be installed) include + // #include // Only needed for Arduino 1.6.5 and earlier + // #include "SSD1306Brzo.h" + // #include "SH1106Brzo.h" + // For a connection via SPI include + // #include // Only needed for Arduino 1.6.5 and earlier + // #include "SSD1306Spi.h" + // #include "SH1106SPi.h" + + // Use the corresponding display class: + + // Initialize the OLED display using SPI + // D5 -> CLK + // D7 -> MOSI (DOUT) + // D0 -> RES + // D2 -> DC + // D8 -> CS + // SSD1306Spi display(D0, D2, D8); + // or + // SH1106Spi display(D0, D2); + + // Initialize the OLED display using brzo_i2c + // D3 -> SDA + // D5 -> SCL + // SSD1306Brzo display(0x3c, D3, D5); + // or + // SH1106Brzo display(0x3c, D3, D5); + + // Initialize the OLED display using Wire library + SSD1306 display(0x3c, D3, D5); + // SH1106 display(0x3c, D3, D5); + +// Adapted from Adafruit_SSD1306 +void drawLines() { + for (int16_t i=0; i=0; i-=4) { + display.drawLine(0, DISPLAY_HEIGHT-1, DISPLAY_WIDTH-1, i); + display.display(); + delay(10); + } + delay(250); + + display.clear(); + for (int16_t i=DISPLAY_WIDTH-1; i>=0; i-=4) { + display.drawLine(DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, i, 0); + display.display(); + delay(10); + } + for (int16_t i=DISPLAY_HEIGHT-1; i>=0; i-=4) { + display.drawLine(DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, 0, i); + display.display(); + delay(10); + } + delay(250); + display.clear(); + for (int16_t i=0; i + + // OTA Includes + #include + #include + + const char *ssid = "[Your SSID]"; + const char *password = "[Your Password]"; + + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306.h" // alias for `#include "SSD1306Wire.h"` +// or #include "SH1106.h" alis for `#include "SH1106Wire.h"` +// For a connection via I2C using brzo_i2c (must be installed) include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// #include "SH1106Brzo.h" +// For a connection via SPI include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// #include "SH1106SPi.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306 display(0x3c, D3, D5); +// SH1106 display(0x3c, D3, D5); + + +void setup() { + WiFi.begin ( ssid, password ); + + // Wait for connection + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 10 ); + } + + display.init(); + display.flipScreenVertically(); + display.setContrast(255); + + ArduinoOTA.begin(); + ArduinoOTA.onStart([]() { + display.clear(); + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display.drawString(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2 - 10, "OTA Update"); + display.display(); + }); + + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + display.drawProgressBar(4, 32, 120, 8, progress / (total / 100) ); + display.display(); + }); + + ArduinoOTA.onEnd([]() { + display.clear(); + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display.drawString(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, "Restart"); + display.display(); + }); + + // Align text vertical/horizontal center + display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display.setFont(ArialMT_Plain_10); + display.drawString(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, "Ready for OTA:\n" + WiFi.localIP().toString()); + display.display(); +} + +void loop() { + ArduinoOTA.handle(); +} diff --git a/libraries/oled-ssd1306/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino b/libraries/oled-ssd1306/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino new file mode 100644 index 00000000..6d3854a5 --- /dev/null +++ b/libraries/oled-ssd1306/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino @@ -0,0 +1,187 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 by Daniel Eichhorn + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306.h" // alias for `#include "SSD1306Wire.h"` +// or #include "SH1106.h" alis for `#include "SH1106Wire.h"` +// For a connection via I2C using brzo_i2c (must be installed) include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// #include "SH1106Brzo.h" +// For a connection via SPI include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// #include "SH1106SPi.h" + +// Include custom images +#include "images.h" + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306 display(0x3c, 4, 15); +// SH1106 display(0x3c, D3, D5); + + +#define DEMO_DURATION 3000 +typedef void (*Demo)(void); + +int demoMode = 0; +int counter = 1; + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println(); + + + // Initialising the UI will init the display too. + display.init(); + + display.flipScreenVertically(); + display.setFont(ArialMT_Plain_10); + +} + +void drawFontFaceDemo() { + // Font Demo1 + // create more fonts at http://oleddisplay.squix.ch/ + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.setFont(ArialMT_Plain_10); + display.drawString(0, 0, "Hello world"); + display.setFont(ArialMT_Plain_16); + display.drawString(0, 10, "Hello world"); + display.setFont(ArialMT_Plain_24); + display.drawString(0, 26, "Hello world"); +} + +void drawTextFlowDemo() { + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawStringMaxWidth(0, 0, 128, + "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore." ); +} + +void drawTextAlignmentDemo() { + // Text alignment demo + display.setFont(ArialMT_Plain_10); + + // The coordinates define the left starting point of the text + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(0, 10, "Left aligned (0,10)"); + + // The coordinates define the center of the text + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(64, 22, "Center aligned (64,22)"); + + // The coordinates define the right end of the text + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(128, 33, "Right aligned (128,33)"); +} + +void drawRectDemo() { + // Draw a pixel at given position + for (int i = 0; i < 10; i++) { + display.setPixel(i, i); + display.setPixel(10 - i, i); + } + display.drawRect(12, 12, 20, 20); + + // Fill the rectangle + display.fillRect(14, 14, 17, 17); + + // Draw a line horizontally + display.drawHorizontalLine(0, 40, 20); + + // Draw a line horizontally + display.drawVerticalLine(40, 0, 20); +} + +void drawCircleDemo() { + for (int i=1; i < 8; i++) { + display.setColor(WHITE); + display.drawCircle(32, 32, i*3); + if (i % 2 == 0) { + display.setColor(BLACK); + } + display.fillCircle(96, 32, 32 - i* 3); + } +} + +void drawProgressBarDemo() { + int progress = (counter / 5) % 100; + // draw the progress bar + display.drawProgressBar(0, 32, 120, 10, progress); + + // draw the percentage as String + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(64, 15, String(progress) + "%"); +} + +void drawImageDemo() { + // see http://blog.squix.org/2015/05/esp8266-nodemcu-how-to-create-xbm.html + // on how to create xbm files + display.drawXbm(34, 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits); +} + +Demo demos[] = {drawFontFaceDemo, drawTextFlowDemo, drawTextAlignmentDemo, drawRectDemo, drawCircleDemo, drawProgressBarDemo, drawImageDemo}; +int demoLength = (sizeof(demos) / sizeof(Demo)); +long timeSinceLastModeSwitch = 0; + +void loop() { + // clear the display + display.clear(); + // draw the current demo method + demos[demoMode](); + + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(10, 128, String(millis())); + // write the buffer to the display + display.display(); + + if (millis() - timeSinceLastModeSwitch > DEMO_DURATION) { + demoMode = (demoMode + 1) % demoLength; + timeSinceLastModeSwitch = millis(); + } + counter++; + delay(10); +} diff --git a/libraries/oled-ssd1306/examples/SSD1306SimpleDemo/images.h b/libraries/oled-ssd1306/examples/SSD1306SimpleDemo/images.h new file mode 100644 index 00000000..9daf8c1a --- /dev/null +++ b/libraries/oled-ssd1306/examples/SSD1306SimpleDemo/images.h @@ -0,0 +1,28 @@ +#define WiFi_Logo_width 60 +#define WiFi_Logo_height 36 +const char WiFi_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; diff --git a/libraries/oled-ssd1306/examples/SSD1306UiDemo/SSD1306UiDemo.ino b/libraries/oled-ssd1306/examples/SSD1306UiDemo/SSD1306UiDemo.ino new file mode 100644 index 00000000..ada991ad --- /dev/null +++ b/libraries/oled-ssd1306/examples/SSD1306UiDemo/SSD1306UiDemo.ino @@ -0,0 +1,194 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 by Daniel Eichhorn + * Copyright (c) 2016 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + + // Include the correct display library + // For a connection via I2C using Wire include + #include // Only needed for Arduino 1.6.5 and earlier + #include "SSD1306.h" // alias for `#include "SSD1306Wire.h"` + // or #include "SH1106.h" alis for `#include "SH1106Wire.h"` + // For a connection via I2C using brzo_i2c (must be installed) include + // #include // Only needed for Arduino 1.6.5 and earlier + // #include "SSD1306Brzo.h" + // #include "SH1106Brzo.h" + // For a connection via SPI include + // #include // Only needed for Arduino 1.6.5 and earlier + // #include "SSD1306Spi.h" + // #include "SH1106SPi.h" + +// Include the UI lib +#include "OLEDDisplayUi.h" + +// Include custom images +#include "images.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306 display(0x3c, 4, 15); +// SH1106 display(0x3c, D3, D5); + +OLEDDisplayUi ui ( &display ); + +void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->setFont(ArialMT_Plain_10); + display->drawString(128, 0, String(millis())); +} + +void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // draw an xbm image. + // Please note that everything that should be transitioned + // needs to be drawn relative to x and y + + display->drawXbm(x + 34, y + 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits); +} + +void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // Demonstrates the 3 included default sizes. The fonts come from SSD1306Fonts.h file + // Besides the default fonts there will be a program to convert TrueType fonts into this format + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_10); + display->drawString(0 + x, 10 + y, "Arial 10"); + + display->setFont(ArialMT_Plain_16); + display->drawString(0 + x, 20 + y, "Arial 16"); + + display->setFont(ArialMT_Plain_24); + display->drawString(0 + x, 34 + y, "Arial 24"); +} + +void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // Text alignment demo + display->setFont(ArialMT_Plain_10); + + // The coordinates define the left starting point of the text + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->drawString(0 + x, 11 + y, "Left aligned (0,10)"); + + // The coordinates define the center of the text + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->drawString(64 + x, 22 + y, "Center aligned (64,22)"); + + // The coordinates define the right end of the text + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->drawString(128 + x, 33 + y, "Right aligned (128,33)"); +} + +void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // Demo for drawStringMaxWidth: + // with the third parameter you can define the width after which words will be wrapped. + // Currently only spaces and "-" are allowed for wrapping + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_10); + display->drawStringMaxWidth(0 + x, 10 + y, 128, "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore."); +} + +void drawFrame5(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + +} + +// This array keeps function pointers to all frames +// frames are the single views that slide in +FrameCallback frames[] = { drawFrame1, drawFrame2, drawFrame3, drawFrame4, drawFrame5 }; + +// how many frames are there? +int frameCount = 5; + +// Overlays are statically drawn on top of a frame eg. a clock +OverlayCallback overlays[] = { msOverlay }; +int overlaysCount = 1; + +void setup() { + //For Embeded OLED on Wifi kit 32 + pinMode(16,OUTPUT); + digitalWrite(16, LOW); // turn the LED on (HIGH is the voltage level) + delay(100); // wait for a second + digitalWrite(16, HIGH); // turn the LED off by making the voltage LOW + Serial.begin(115200); + Serial.println(); + Serial.println(); + + // The ESP is capable of rendering 60fps in 80Mhz mode + // but that won't give you much time for anything else + // run it in 160Mhz mode or just set it to 30 fps + ui.setTargetFPS(60); + + // Customize the active and inactive symbol + ui.setActiveSymbol(activeSymbol); + ui.setInactiveSymbol(inactiveSymbol); + + // You can change this to + // TOP, LEFT, BOTTOM, RIGHT + ui.setIndicatorPosition(BOTTOM); + + // Defines where the first frame is located in the bar. + ui.setIndicatorDirection(LEFT_RIGHT); + + // You can change the transition that is used + // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN + ui.setFrameAnimation(SLIDE_LEFT); + + // Add frames + ui.setFrames(frames, frameCount); + + // Add overlays + ui.setOverlays(overlays, overlaysCount); + + // Initialising the UI will init the display too. + ui.init(); + + display.flipScreenVertically(); + +} + + +void loop() { + int remainingTimeBudget = ui.update(); + + if (remainingTimeBudget > 0) { + // You can do some work here + // Don't do stuff if you are below your + // time budget. + delay(remainingTimeBudget); + } +} diff --git a/libraries/oled-ssd1306/examples/SSD1306UiDemo/images.h b/libraries/oled-ssd1306/examples/SSD1306UiDemo/images.h new file mode 100644 index 00000000..8b876a36 --- /dev/null +++ b/libraries/oled-ssd1306/examples/SSD1306UiDemo/images.h @@ -0,0 +1,50 @@ +#define WiFi_Logo_width 60 +#define WiFi_Logo_height 36 +const char WiFi_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + +const char activeSymbol[] PROGMEM = { + B00000000, + B00000000, + B00011000, + B00100100, + B01000010, + B01000010, + B00100100, + B00011000 +}; + +const char inactiveSymbol[] PROGMEM = { + B00000000, + B00000000, + B00000000, + B00000000, + B00011000, + B00011000, + B00000000, + B00000000 +}; diff --git a/libraries/oled-ssd1306/library.json b/libraries/oled-ssd1306/library.json new file mode 100644 index 00000000..862d1716 --- /dev/null +++ b/libraries/oled-ssd1306/library.json @@ -0,0 +1,28 @@ +{ + "name": "ESP8266_SSD1306", + "version": "3.2.7", + "keywords": "ssd1306, oled, display, i2c", + "description": "A I2C display driver for SSD1306 oled displays connected to an ESP8266 or ESP32", + "repository": + { + "type": "git", + "url": "https://github.com/squix78/esp8266-oled-ssd1306.git" + }, + "authors": + [ + { + "name": "Daniel Eichhorn", + "email": "squix78@gmail.com", + "url": "http://blog.squix.ch" + }, + { + "name": "Fabrice Weinberg", + "email": "fabrice@weinberg.me" + } + ], + "frameworks": "arduino", + "platforms": [ + "espressif8266", + "espressif32" + ] +} diff --git a/libraries/oled-ssd1306/library.properties b/libraries/oled-ssd1306/library.properties new file mode 100644 index 00000000..4f8de4cf --- /dev/null +++ b/libraries/oled-ssd1306/library.properties @@ -0,0 +1,9 @@ +name=ESP8266 and ESP32 Oled Driver for SSD1306 display +version=3.2.7 +author=Daniel Eichhorn, Fabrice Weinberg +maintainer=Daniel Eichhorn +sentence=A I2C display driver for SSD1306 oled displays connected to an ESP8266 or ESP32 +paragraph=A I2C display driver for SSD1306 oled displays connected to an ESP8266 or ESP32 +category=Display +url=https://github.com/squix78/esp8266-oled-ssd1306 +architectures=esp8266,esp32 diff --git a/libraries/oled-ssd1306/license b/libraries/oled-ssd1306/license new file mode 100644 index 00000000..706c10fe --- /dev/null +++ b/libraries/oled-ssd1306/license @@ -0,0 +1,24 @@ +The MIT License (MIT) + +Copyright (c) 2016 by Daniel Eichhorn +Copyright (c) 2016 by Fabrice Weinberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +See more at http://blog.squix.ch diff --git a/libraries/oled-ssd1306/resources/DemoFrame1.jpg b/libraries/oled-ssd1306/resources/DemoFrame1.jpg new file mode 100644 index 00000000..536b570d Binary files /dev/null and b/libraries/oled-ssd1306/resources/DemoFrame1.jpg differ diff --git a/libraries/oled-ssd1306/resources/DemoFrame2.jpg b/libraries/oled-ssd1306/resources/DemoFrame2.jpg new file mode 100644 index 00000000..8dccbcd0 Binary files /dev/null and b/libraries/oled-ssd1306/resources/DemoFrame2.jpg differ diff --git a/libraries/oled-ssd1306/resources/DemoFrame3.jpg b/libraries/oled-ssd1306/resources/DemoFrame3.jpg new file mode 100644 index 00000000..49e07ab5 Binary files /dev/null and b/libraries/oled-ssd1306/resources/DemoFrame3.jpg differ diff --git a/libraries/oled-ssd1306/resources/DemoFrame4.jpg b/libraries/oled-ssd1306/resources/DemoFrame4.jpg new file mode 100644 index 00000000..99cbe1bc Binary files /dev/null and b/libraries/oled-ssd1306/resources/DemoFrame4.jpg differ diff --git a/libraries/oled-ssd1306/resources/FontTool.png b/libraries/oled-ssd1306/resources/FontTool.png new file mode 100644 index 00000000..c7bb222b Binary files /dev/null and b/libraries/oled-ssd1306/resources/FontTool.png differ diff --git a/libraries/oled-ssd1306/resources/SPI_version.jpg b/libraries/oled-ssd1306/resources/SPI_version.jpg new file mode 100644 index 00000000..115c9f3b Binary files /dev/null and b/libraries/oled-ssd1306/resources/SPI_version.jpg differ diff --git a/libraries/oled-ssd1306/resources/xbmPreview.png b/libraries/oled-ssd1306/resources/xbmPreview.png new file mode 100644 index 00000000..70ea3a53 Binary files /dev/null and b/libraries/oled-ssd1306/resources/xbmPreview.png differ diff --git a/library.properties b/library.properties new file mode 100644 index 00000000..83cf0318 --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=ESP3D +version=2.0.0 +author=Luc Lebosse +maintainer=Luc Lebosse, +sentence=A 3D printer front end for ESP boards. +paragraph=This library implements a 3D printer front end. +category=Communication +url=https://github.com/luc-github/ESP3D +architectures=esp8266,esp32 diff --git a/esp3d/GenLinkedList.h b/src/GenLinkedList.h similarity index 76% rename from esp3d/GenLinkedList.h rename to src/GenLinkedList.h index f2051cec..ffebc821 100644 --- a/esp3d/GenLinkedList.h +++ b/src/GenLinkedList.h @@ -36,7 +36,7 @@ protected: // every time the list suffer changes bool isCached; - ListNode* getNode(int index); + ListNode* getNode (int index); public: GenLinkedList(); @@ -51,28 +51,28 @@ public: Unlink and link the GenLinkedList correcly; Increment _size */ - virtual bool add(int index, T); + virtual bool add (int index, T); /* Adds a T object in the end of the GenLinkedList; Increment _size; */ - virtual bool add(T); + virtual bool add (T); /* Adds a T object in the start of the GenLinkedList; Increment _size; */ - virtual bool unshift(T); + virtual bool unshift (T); /* Set the object at index, with T; Increment _size; */ - virtual bool set(int index, T); + virtual bool set (int index, T); /* Remove object at index; If index is not reachable, returns false; else, decrement _size */ - virtual T remove(int index); + virtual T remove (int index); /* Remove last object; */ @@ -86,7 +86,7 @@ public: Return Element if accessible, else, return false; */ - virtual T get(int index); + virtual T get (int index); /* Clear the entire array @@ -99,9 +99,9 @@ public: template GenLinkedList::GenLinkedList() { - root=NULL; - last=NULL; - _size=0; + root = NULL; + last = NULL; + _size = 0; lastNodeGot = root; lastIndexGot = 0; @@ -113,13 +113,13 @@ template GenLinkedList::~GenLinkedList() { ListNode* tmp; - while(root!=NULL) { - tmp=root; - root=root->next; + while (root != NULL) { + tmp = root; + root = root->next; delete tmp; } last = NULL; - _size=0; + _size = 0; isCached = false; } @@ -128,7 +128,7 @@ GenLinkedList::~GenLinkedList() */ template -ListNode* GenLinkedList::getNode(int index) +ListNode* GenLinkedList::getNode (int index) { int _pos = 0; @@ -136,19 +136,19 @@ ListNode* GenLinkedList::getNode(int index) // Check if the node trying to get is // immediately AFTER the previous got one - if(isCached && lastIndexGot <= index) { + if (isCached && lastIndexGot <= index) { _pos = lastIndexGot; current = lastNodeGot; } - while(_pos < index && current) { + while (_pos < index && current) { current = current->next; _pos++; } // Check if the object index got is the same as the required - if(_pos == index) { + if (_pos == index) { isCached = true; lastIndexGot = index; lastNodeGot = current; @@ -166,19 +166,19 @@ int GenLinkedList::size() } template -bool GenLinkedList::add(int index, T _t) +bool GenLinkedList::add (int index, T _t) { - if(index >= _size) { - return add(_t); + if (index >= _size) { + return add (_t); } - if(index == 0) { - return unshift(_t); + if (index == 0) { + return unshift (_t); } ListNode *tmp = new ListNode(), - *_prev = getNode(index-1); + *_prev = getNode (index - 1); tmp->data = _t; tmp->next = _prev->next; _prev->next = tmp; @@ -190,14 +190,14 @@ bool GenLinkedList::add(int index, T _t) } template -bool GenLinkedList::add(T _t) +bool GenLinkedList::add (T _t) { ListNode *tmp = new ListNode(); tmp->data = _t; tmp->next = NULL; - if(root) { + if (root) { // Already have elements inserted last->next = tmp; last = tmp; @@ -214,11 +214,11 @@ bool GenLinkedList::add(T _t) } template -bool GenLinkedList::unshift(T _t) +bool GenLinkedList::unshift (T _t) { - if(_size == 0) { - return add(_t); + if (_size == 0) { + return add (_t); } ListNode *tmp = new ListNode(); @@ -233,30 +233,30 @@ bool GenLinkedList::unshift(T _t) } template -bool GenLinkedList::set(int index, T _t) +bool GenLinkedList::set (int index, T _t) { // Check if index position is in bounds - if(index < 0 || index >= _size) { + if (index < 0 || index >= _size) { return false; } - getNode(index)->data = _t; + getNode (index)->data = _t; return true; } template T GenLinkedList::pop() { - if(_size <= 0) { + if (_size <= 0) { return T(); } isCached = false; - if(_size >= 2) { - ListNode *tmp = getNode(_size - 2); + if (_size >= 2) { + ListNode *tmp = getNode (_size - 2); T ret = tmp->next->data; - delete(tmp->next); + delete (tmp->next); tmp->next = NULL; last = tmp; _size--; @@ -264,7 +264,7 @@ T GenLinkedList::pop() } else { // Only one element left on the list T ret = root->data; - delete(root); + delete (root); root = NULL; last = NULL; _size = 0; @@ -275,14 +275,14 @@ T GenLinkedList::pop() template T GenLinkedList::shift() { - if(_size <= 0) { + if (_size <= 0) { return T(); } - if(_size > 1) { + if (_size > 1) { ListNode *_next = root->next; T ret = root->data; - delete(root); + delete (root); root = _next; _size --; isCached = false; @@ -296,25 +296,25 @@ T GenLinkedList::shift() } template -T GenLinkedList::remove(int index) +T GenLinkedList::remove (int index) { if (index < 0 || index >= _size) { return T(); } - if(index == 0) { + if (index == 0) { return shift(); } - if (index == _size-1) { + if (index == _size - 1) { return pop(); } - ListNode *tmp = getNode(index - 1); + ListNode *tmp = getNode (index - 1); ListNode *toDelete = tmp->next; T ret = toDelete->data; tmp->next = tmp->next->next; - delete(toDelete); + delete (toDelete); _size--; isCached = false; return ret; @@ -322,17 +322,17 @@ T GenLinkedList::remove(int index) template -T GenLinkedList::get(int index) +T GenLinkedList::get (int index) { - ListNode *tmp = getNode(index); + ListNode *tmp = getNode (index); - return (tmp ? tmp->data : T()); + return (tmp ? tmp->data : T() ); } template void GenLinkedList::clear() { - while(size() > 0) { + while (size() > 0) { shift(); } } diff --git a/src/asyncwebserver.cpp b/src/asyncwebserver.cpp new file mode 100644 index 00000000..19432026 --- /dev/null +++ b/src/asyncwebserver.cpp @@ -0,0 +1,1259 @@ +/* + asyncwebserver.cpp - ESP3D sync functions class + + 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 +*/ + +#include +#include "config.h" +#if defined(ASYNCWEBSERVER) + +#include "webinterface.h" +#include "wificonf.h" +#include +#include +#include +#include +#ifndef FS_NO_GLOBALS +#define FS_NO_GLOBALS +#endif +#include +#include +#ifdef ARDUINO_ARCH_ESP8266 +#include "ESP8266WiFi.h" +#include +#else //ESP32 +#include +#include +#include "SPIFFS.h" +#include "Update.h" +#endif + +#include "GenLinkedList.h" +#include "command.h" +#include "espcom.h" + +#ifdef SSDP_FEATURE + #ifdef ARDUINO_ARCH_ESP32 + #include + #else + #include + #endif +#endif + +//embedded response file if no files on SPIFFS +#include "nofile.h" + +extern bool deleteRecursive(String path); +extern void CloseSerialUpload (bool iserror, String & filename); +extern bool sendLine2Serial (String & line); +extern long id_connection; + +const uint8_t PAGE_404 [] PROGMEM = "\n\nRedirecting... \n\n\n
Unknown page : $QUERY$- you will be redirected...\n

\nif not redirected, click here\n

\n\n\n\n
\n\n\n\n"; +const uint8_t PAGE_CAPTIVE [] PROGMEM = "\n\nCaptive Portal \n\n\n
Captive Portal page : $QUERY$- you will be redirected...\n

\nif not redirected, click here\n

\n\n\n\n
\n\n\n\n"; +#define CONTENT_TYPE_HTML "text/html" + +//filter to intercept command line on root +bool filterOnRoot (AsyncWebServerRequest *request) +{ + if (request->hasArg ("forcefallback") ) { + String stmp = request->arg ("forcefallback"); + //to use all case + stmp.toLowerCase(); + if ( stmp == "yes") { + return false; + } + } + return true; +} + +void handle_login(AsyncWebServerRequest *request) +{ +#ifdef AUTHENTICATION_FEATURE +#else + AsyncWebServerResponse * response = request->beginResponse (200, "application/json", "{\"status\":\"Ok\",\"authentication_lvl\":\"admin\"}"); + response->addHeader("Cache-Control","no-cache"); + request->send(response); +#endif +} + + +#ifdef SSDP_FEATURE +void handle_SSDP (AsyncWebServerRequest *request) +{ + StreamString sschema ; + if (sschema.reserve (1024) ) { + String templ = "" + "" + "" + "1" + "0" + "" + "http://%s:%u/" + "" + "upnp:rootdevice" + "%s" + "/" + "%s" +#ifdef ARDUINO_ARCH_ESP32 + "ESP32" +#else + "ESP8266" +#endif + "ESP3D" +#ifdef ARDUINO_ARCH_ESP32 + "http://espressif.com/en/products/hardware/esp-wroom-32/overview" +#else + "http://espressif.com/en/products/esp8266" +#endif + "Espressif Systems" + "http://espressif.com" + "uuid:%s" + "" + "\r\n" + "\r\n"; + char uuid[37]; + String sip = WiFi.localIP().toString(); +#ifdef ARDUINO_ARCH_ESP8266 + uint32_t chipId = ESP.getChipId(); +#else + uint32_t chipId = (uint16_t) (ESP.getEfuseMac() >> 32); +#endif + sprintf (uuid, "38323636-4558-4dda-9188-cda0e6%02x%02x%02x", + (uint16_t) ( (chipId >> 16) & 0xff), + (uint16_t) ( (chipId >> 8) & 0xff), + (uint16_t) chipId & 0xff ); + String friendlyName; + if (!CONFIG::read_string (EP_HOSTNAME, friendlyName, MAX_HOSTNAME_LENGTH) ) { + friendlyName = wifi_config.get_default_hostname(); + } + String serialNumber = String (chipId); + sschema.printf (templ.c_str(), + sip.c_str(), + wifi_config.iweb_port, + friendlyName.c_str(), + serialNumber.c_str(), + uuid); + request->send (200, "text/xml", (String) sschema); + } else { + request->send (500); + } +} +#endif + +//SPIFFS +//SPIFFS files list and file commands +void handleFileList (AsyncWebServerRequest *request) +{ + level_authenticate_type auth_level = web_interface->is_authenticated(); + if (auth_level == LEVEL_GUEST) { + web_interface->_upload_status = UPLOAD_STATUS_NONE; + request->send (401, "text/plain", "Authentication failed!\n"); + return; + } + String path ; + String status = "Ok"; + if ( (web_interface->_upload_status == UPLOAD_STATUS_FAILED) || (web_interface->_upload_status == UPLOAD_STATUS_CANCELLED) ) { + status = "Upload failed"; + } + //be sure root is correct according authentication + if (auth_level == LEVEL_ADMIN) { + path = "/"; + } else { + path = "/user"; + } + //get current path + if (request->hasArg ("path") ) { + path += request->arg ("path") ; + } + //to have a clean path + path.trim(); + path.replace ("//", "/"); + if (path[path.length() - 1] != '/') { + path += "/"; + } + //check if query need some action + if (request->hasArg ("action") ) { + //delete a file + if (request->arg ("action") == "delete" && request->hasArg ("filename") ) { + String filename; + String shortname = request->arg ("filename"); + shortname.replace ("/", ""); + filename = path + request->arg ("filename"); + filename.replace ("//", "/"); + if (!SPIFFS.exists (filename) ) { + status = shortname + F (" does not exists!"); + } else { + if (SPIFFS.remove (filename) ) { + status = shortname + F (" deleted"); + //what happen if no "/." and no other subfiles ? +#ifdef ARDUINO_ARCH_ESP8266 + FS_DIR dir = SPIFFS.openDir (path); + if (!dir.next() ) { +#else + String ptmp = path; + if ( (path != "/") && (path[path.length() - 1] = '/') ) { + ptmp = path.substring (0, path.length() - 1); + } + FS_FILE dir = SPIFFS.open (ptmp); + FS_FILE dircontent = dir.openNextFile(); + if (!dircontent) { +#endif + //keep directory alive even empty + FS_FILE r = SPIFFS.open (path + "/.", SPIFFS_FILE_WRITE); + if (r) { + r.close(); + } + } + } else { + status = F ("Cannot deleted ") ; + status += shortname ; + } + } + } + //delete a directory + if (request->arg ("action") == "deletedir" && request->hasArg ("filename") ) { + String filename; + String shortname = request->arg ("filename"); + shortname.replace ("/", ""); + filename = path + request->arg ("filename"); + filename += "/"; + filename.replace ("//", "/"); + if (filename != "/") { + bool delete_error = false; +#ifdef ARDUINO_ARCH_ESP8266 + FS_DIR dir = SPIFFS.openDir (path + shortname); + { + while (dir.next() ) { +#else + FS_FILE dir = SPIFFS.open (path + shortname); + { + FS_FILE file2deleted = dir.openNextFile(); + while (file2deleted) { +#endif +#ifdef ARDUINO_ARCH_ESP8266 + String fullpath = dir.fileName(); +#else + String fullpath = file2deleted.name(); +#endif + if (!SPIFFS.remove (fullpath) ) { + delete_error = true; + status = F ("Cannot deleted ") ; + status += fullpath; + } +#ifdef ARDUINO_ARCH_ESP32 + file2deleted = dir.openNextFile(); +#endif + } + } + if (!delete_error) { + status = shortname ; + status += " deleted"; + } + } + } + //create a directory + if (request->arg ("action") == "createdir" && request->hasArg ("filename") ) { + String filename; + filename = path + request->arg ("filename") + "/."; + String shortname = request->arg ("filename"); + shortname.replace ("/", ""); + filename.replace ("//", "/"); + if (SPIFFS.exists (filename) ) { + status = shortname + F (" already exists!"); + } else { + FS_FILE r = SPIFFS.open (filename, SPIFFS_FILE_WRITE); + if (!r) { + status = F ("Cannot create "); + status += shortname ; + } else { + r.close(); + status = shortname + F (" created"); + } + } + } + } + String jsonfile = "{"; +#ifdef ARDUINO_ARCH_ESP8266 + FS_DIR dir = SPIFFS.openDir (path); +#else + String ptmp = path; + if ( (path != "/") && (path[path.length() - 1] = '/') ) { + ptmp = path.substring (0, path.length() - 1); + } + FS_FILE dir = SPIFFS.open (ptmp); +#endif + jsonfile += "\"files\":["; + bool firstentry = true; + String subdirlist = ""; +#ifdef ARDUINO_ARCH_ESP8266 + while (dir.next() ) { + String filename = dir.fileName(); +#else + File fileparsed = dir.openNextFile(); + while (fileparsed) { + String filename = fileparsed.name(); +#endif + String size = ""; + bool addtolist = true; + //remove path from name + filename = filename.substring (path.length(), filename.length() ); + //check if file or subfile + if (filename.indexOf ("/") > -1) { + //Do not rely on "/." to define directory as SPIFFS upload won't create it but directly files + //and no need to overload SPIFFS if not necessary to create "/." if no need + //it will reduce SPIFFS available space so limit it to creation + filename = filename.substring (0, filename.indexOf ("/") ); + String tag = "*"; + tag += filename + "*"; + if (subdirlist.indexOf (tag) > -1 || filename.length() == 0) { //already in list + addtolist = false; //no need to add + } else { + size = "-1"; //it is subfile so display only directory, size will be -1 to describe it is directory + if (subdirlist.length() == 0) { + subdirlist += "*"; + } + subdirlist += filename + "*"; //add to list + } + } else { + //do not add "." file + if (! ( (filename == ".") || (filename == "") ) ) { +#ifdef ARDUINO_ARCH_ESP8266 + size = CONFIG::formatBytes (dir.fileSize() ); +#else + size = CONFIG::formatBytes (fileparsed.size() ); +#endif + + } else { + addtolist = false; + } + } + if (addtolist) { + if (!firstentry) { + jsonfile += ","; + } else { + firstentry = false; + } + jsonfile += "{"; + jsonfile += "\"name\":\""; + jsonfile += filename; + jsonfile += "\",\"size\":\""; + jsonfile += size; + jsonfile += "\""; + jsonfile += "}"; + } +#ifdef ARDUINO_ARCH_ESP32 + fileparsed = dir.openNextFile(); +#endif + } + jsonfile += "],"; + jsonfile += "\"path\":\"" + path + "\","; + jsonfile += "\"status\":\"" + status + "\","; + size_t totalBytes; + size_t usedBytes; +#ifdef ARDUINO_ARCH_ESP8266 + fs::FSInfo info; + SPIFFS.info (info); + totalBytes = info.totalBytes; + usedBytes = info.usedBytes; +#else + totalBytes = SPIFFS.totalBytes(); + usedBytes = SPIFFS.usedBytes(); +#endif + jsonfile += "\"total\":\"" + CONFIG::formatBytes (totalBytes) + "\","; + jsonfile += "\"used\":\"" + CONFIG::formatBytes (usedBytes) + "\","; + jsonfile.concat (F ("\"occupation\":\"") ); + jsonfile += CONFIG::intTostr (100 * usedBytes / totalBytes); + jsonfile += "\""; + jsonfile += "}"; + path = ""; + AsyncResponseStream *response = request->beginResponseStream ("application/json"); + response->addHeader ("Cache-Control", "no-cache"); + response->print (jsonfile.c_str() ); + request->send (response); + web_interface->_upload_status = UPLOAD_STATUS_NONE; +} + +//SPIFFS files uploader handle +void SPIFFSFileupload (AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) +{ + LOG ("Uploading: ") + LOG (filename) + LOG ("\n") + //get authentication status + level_authenticate_type auth_level= web_interface->is_authenticated(); + //Guest cannot upload - only admin + if (auth_level == LEVEL_GUEST) { + web_interface->_upload_status = UPLOAD_STATUS_CANCELLED; + ESPCOM::println (F ("Upload rejected"), PRINTER_PIPE); + LOG ("Upload rejected\r\n"); + request->client()->abort(); + return; + } + String upload_filename = filename; + if (upload_filename[0] != '/') { + upload_filename = "/" + filename; + LOG ("Fix :") + LOG (upload_filename) + LOG ("\n") + } + if(auth_level != LEVEL_ADMIN) { + String filename = upload_filename; + upload_filename = "/user" + filename; + } + //Upload start + //************** + if (!index) { + LOG ("Upload start: ") + LOG ("filename") + LOG ("\n") + LOG (upload_filename) + if (SPIFFS.exists (upload_filename) ) { + SPIFFS.remove (upload_filename); + } + if (request->_tempFile) { + request->_tempFile.close(); + } + request->_tempFile = SPIFFS.open (upload_filename, SPIFFS_FILE_WRITE); + if (!request->_tempFile) { + LOG ("Error") + request->client()->abort(); + web_interface->_upload_status = UPLOAD_STATUS_FAILED; + } else { + web_interface->_upload_status = UPLOAD_STATUS_ONGOING; + } + + } + //Upload write + //************** + if ( request->_tempFile) { + if ( ( web_interface->_upload_status = UPLOAD_STATUS_ONGOING) && len) { + request->_tempFile.write (data, len); + LOG ("Write file\n") + } + } + //Upload end + //************** + if (final) { + request->_tempFile.flush(); + request->_tempFile.close(); + request->_tempFile = SPIFFS.open (upload_filename, SPIFFS_FILE_READ); + uint32_t filesize = request->_tempFile.size(); + request->_tempFile.close(); + String sizeargname = filename + "S"; + if (request->hasArg (sizeargname.c_str()) ) { + if (request->arg (sizeargname.c_str()) != String(filesize)) { + web_interface->_upload_status = UPLOAD_STATUS_FAILED; + SPIFFS.remove (upload_filename); + } + } + LOG ("Close file\n") + if (web_interface->_upload_status == UPLOAD_STATUS_ONGOING) { + web_interface->_upload_status = UPLOAD_STATUS_SUCCESSFUL; + } + } +} + + +//FW update using Web interface +#ifdef WEB_UPDATE_FEATURE +void handleUpdate (AsyncWebServerRequest *request) +{ + level_authenticate_type auth_level = web_interface->is_authenticated(); + if (auth_level != LEVEL_ADMIN) { + web_interface->_upload_status = UPLOAD_STATUS_NONE; + request->send (403, "text/plain", "Not allowed, log in first!\n"); + return; + } + String jsonfile = "{\"status\":\"" ; + jsonfile += CONFIG::intTostr (web_interface->_upload_status); + jsonfile += "\"}"; + //send status + AsyncResponseStream *response = request->beginResponseStream ("application/json"); + response->addHeader ("Cache-Control", "no-cache"); + response->print (jsonfile.c_str() ); + request->send (response); + //if success restart + if (web_interface->_upload_status == UPLOAD_STATUS_SUCCESSFUL) { + web_interface->restartmodule = true; + } else { + web_interface->_upload_status = UPLOAD_STATUS_NONE; + } +} +void WebUpdateUpload (AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) +{ + static size_t last_upload_update; + static size_t totalSize; + static uint32_t maxSketchSpace ; + //only admin can update FW + if (web_interface->is_authenticated() != LEVEL_ADMIN) { + web_interface->_upload_status = UPLOAD_STATUS_CANCELLED; + request->client()->abort(); + ESPCOM::println (F ("Update rejected"), PRINTER_PIPE); + LOG ("Update failed\r\n"); + return; + } + //Upload start + //************** + if (!index) { + ESPCOM::println (F ("Update Firmware"), PRINTER_PIPE); + web_interface->_upload_status = UPLOAD_STATUS_ONGOING; +#ifdef ARDUINO_ARCH_ESP8266 + Update.runAsync (true); + WiFiUDP::stopAll(); +#endif +#ifdef ARDUINO_ARCH_ESP8266 + maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; +#else +//Not sure can do OTA on 2Mb board + maxSketchSpace = (ESP.getFlashChipSize() > 0x20000) ? 0x140000 : 0x140000 / 2; +#endif + last_upload_update = 0; + totalSize = 0; + if (!Update.begin (maxSketchSpace) ) { //start with max available size + web_interface->_upload_status = UPLOAD_STATUS_CANCELLED; + ESPCOM::println (F ("Update Cancelled"), PRINTER_PIPE); + request->client()->abort(); + return; + } else { + if ( ( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER) ) { + ESPCOM::println (F ("Update 0%%"), PRINTER_PIPE); + } else { + ESPCOM::println (F ("Update 0%"), PRINTER_PIPE); + } + } + } + //Upload write + //************** + if (web_interface->_upload_status == UPLOAD_STATUS_ONGOING) { + //we do not know the total file size yet but we know the available space so let's use it + totalSize += len; + if ( ( (100 * totalSize) / maxSketchSpace) != last_upload_update) { + last_upload_update = (100 * totalSize) / maxSketchSpace; + String s = "Update "; + s+= String(last_upload_update); + + if ( ( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER) ) { + s+="%%"; + } else { + s+="%"; + } + ESPCOM::println (s, PRINTER_PIPE); + } + if (Update.write (data, len) != len) { + web_interface->_upload_status = UPLOAD_STATUS_CANCELLED; + ESPCOM::println(F("Update Error"), PRINTER_PIPE); + Update.end(); + request->client()->abort(); + } + } + //Upload end + //************** + if (final) { + String sizeargname = filename + "S"; + if (request->hasArg (sizeargname.c_str()) ) { + ESPCOM::println (F ("Check integrity"), PRINTER_PIPE); + if (request->arg (sizeargname.c_str()) != String(totalSize)) { + web_interface->_upload_status = UPLOAD_STATUS_FAILED; + ESPCOM::println (F ("Update Error"), PRINTER_PIPE); + Update.end(); + request->client()->abort(); + } + } + if (Update.end (true) ) { //true to set the size to the current progress + //Update is done + if ( ( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER) ) { + ESPCOM::println (F ("Update 100%%"), PRINTER_PIPE); + } else { + ESPCOM::println (F ("Update 100%"), PRINTER_PIPE); + } + web_interface->_upload_status = UPLOAD_STATUS_SUCCESSFUL; + } + } +} +#endif + +//Not found Page handler ////////////////////////////////////////////////////////// +void handle_not_found (AsyncWebServerRequest *request) +{ + //if we are here it means no index.html + if (request->url() == "/") { + AsyncWebServerResponse * response = request->beginResponse_P (200, CONTENT_TYPE_HTML, PAGE_NOFILES, PAGE_NOFILES_SIZE); + response->addHeader ("Content-Encoding", "gzip"); + request->send (response); + } else { + String path = F ("/404.htm"); + String pathWithGz = path + F (".gz"); + if (SPIFFS.exists (pathWithGz) || SPIFFS.exists (path) ) { + request->send (SPIFFS, path); + } else { + //if not template use default page + String contentpage = FPSTR (PAGE_404); + String stmp; + if (WiFi.getMode() == WIFI_STA ) { + stmp = WiFi.localIP().toString(); + } else { + stmp = WiFi.softAPIP().toString(); + } + //Web address = ip + port + String KEY_IP = F ("$WEB_ADDRESS$"); + String KEY_QUERY = F ("$QUERY$"); + if (wifi_config.iweb_port != 80) { + stmp += ":"; + stmp += CONFIG::intTostr (wifi_config.iweb_port); + } + contentpage.replace (KEY_IP, stmp); + contentpage.replace (KEY_QUERY, request->url() ); + request->send (404, CONTENT_TYPE_HTML, contentpage.c_str() ); + } + } +} + +//Handle web command query and send answer////////////////////////////// +void handle_web_command (AsyncWebServerRequest *request) +{ + //to save time if already disconnected + if (request->hasArg ("PAGEID") ) { + if (request->arg ("PAGEID").length() > 0 ) { + if (request->arg ("PAGEID").toInt() != id_connection) { + request->send (200, "text/plain", "Invalid command"); + return; + } + } + } + level_authenticate_type auth_level = web_interface->is_authenticated(); + LOG (" Web command\r\n") +#ifdef DEBUG_ESP3D + int nb = request->args(); + for (int i = 0 ; i < nb; i++) { + LOG (request->argName (i) ) + LOG (":") + LOG (request->arg (i) ) + LOG ("\r\n") + } +#endif + String cmd = ""; + if (request->hasArg ("plain") || request->hasArg ("commandText") ) { + if (request->hasArg ("plain") ) { + cmd = request->arg ("plain"); + } else { + cmd = request->arg ("commandText"); + } + LOG ("Web Command:") + LOG (cmd) + LOG ("\r\n") + } else { + LOG ("invalid argument\r\n") + request->send (200, "text/plain", "Invalid command"); + return; + } + //if it is for ESP module [ESPXXX] + cmd.trim(); + int ESPpos = cmd.indexOf ("[ESP"); + if (ESPpos > -1) { + //is there the second part? + int ESPpos2 = cmd.indexOf ("]", ESPpos); + if (ESPpos2 > -1) { + //Split in command and parameters + String cmd_part1 = cmd.substring (ESPpos + 4, ESPpos2); + String cmd_part2 = ""; + //only [ESP800] is allowed login free if authentication is enabled + if ( (auth_level == LEVEL_GUEST) && (cmd_part1.toInt() != 800) ) { + request->send (401, "text/plain", "Authentication failed!\n"); + return; + } + //is there space for parameters? + if (ESPpos2 < cmd.length() ) { + cmd_part2 = cmd.substring (ESPpos2 + 1); + } + //if command is a valid number then execute command + if (cmd_part1.toInt() != 0) { + AsyncResponseStream *response = request->beginResponseStream ("text/html"); + response->addHeader ("Cache-Control", "no-cache"); + COMMAND::execute_command (cmd_part1.toInt(), cmd_part2, WEB_PIPE, auth_level, response); + request->send (response); + } + //if not is not a valid [ESPXXX] command + } + } else { + if (auth_level == LEVEL_GUEST) { + request->send (401, "text/plain", "Authentication failed!\n"); + return; + } + //send command to serial as no need to transfer ESP command + //to avoid any pollution if Uploading file to SDCard + if ( (web_interface->blockserial) == false) { + //block every query + web_interface->blockserial = true; + LOG ("Block Serial\r\n") + //empty the serial buffer and incoming data + LOG ("Start PurgeSerial\r\n") + ESPCOM::processFromSerial (true); + LOG ("End PurgeSerial\r\n") + LOG ("Start PurgeSerial\r\n") + ESPCOM::processFromSerial (true); + LOG ("End PurgeSerial\r\n") + //send command + LOG ("Send Command\r\n") + ESPCOM::println (cmd, DEFAULT_PRINTER_PIPE); + CONFIG::wait (1); + AsyncWebServerResponse *response = request->beginChunkedResponse ("text/plain", [] (uint8_t *buffer, size_t maxLen, size_t index) -> size_t { + static bool finish_check; + static String current_line; + static int multiple_counter = 0; + static uint8_t *active_serial_buffer = NULL; + static size_t active_serial_buffer_size = 0; + uint32_t timeout = millis(); + size_t count = 0; + LOG ("Entering\n") + //this is the start + if (!index) + { + //we are not done + finish_check = false; + //no error / multiple heat + multiple_counter = 0; + //if somehow the buffer is not empty - clean it + if (active_serial_buffer != NULL) { + delete active_serial_buffer; + active_serial_buffer = NULL; + } + active_serial_buffer_size = 0; + current_line = ""; + } + //current_line is not empty because previous buffer was full + if (current_line.length() > 0 ) + { + //does it have a end already ? + if (current_line[current_line.length() - 1] == '\n') { + LOG (current_line.c_str() ) + //if yes fill the buffer + String tmp = ""; + for (int p = 0 ; p < current_line.length() ; p++) { + CONFIG::wdtFeed(); + //Just in case line is too long + if (count < maxLen) { + buffer[count] = current_line[p]; + count++; + } //too long let's copy to buffer what's left for next time + else { + tmp += current_line[p]; + } + } + //this will be sent next time + current_line = tmp; + } + } + LOG (" Max Len Size ") + LOG (String (maxLen) ) + LOG (" Index ") + LOG (String (index) ) + LOG (" initial count ") + LOG (String (count) ) + LOG (" buffer ") + LOG (String (active_serial_buffer_size) ) + LOG (" line ") + LOG (String (current_line.length() ) ) + LOG ("\n") + int packet_size = (maxLen >= 1460) ? 1200 : maxLen; + //now check if serial has data if have space in send buffer + while (!finish_check && (count < maxLen) ) + { + CONFIG::wdtFeed(); + size_t len = ESPCOM::available(DEFAULT_PRINTER_PIPE); + LOG ("Input Size ") + LOG (String (len) ) + LOG ("\n") + if (len > 0) { + //prepare serial buffer + uint8_t * tmp_buf = new uint8_t[active_serial_buffer_size + len]; + //copy current buffer in new buffer + if ( (active_serial_buffer_size > 0) && (active_serial_buffer != NULL) ) { + for (int p = 0; p < active_serial_buffer_size; p++) { + tmp_buf[p] = active_serial_buffer[p]; + } + delete active_serial_buffer; + } + //new sized buffer + active_serial_buffer = tmp_buf; + //read full buffer instead of read char by char + //so it give time to refill when processing + ESPCOM::readBytes (DEFAULT_PRINTER_PIPE, &active_serial_buffer[active_serial_buffer_size], len); + //new size of current buffer + active_serial_buffer_size += len; + } + LOG ("buffer Size ") + LOG (String (active_serial_buffer_size) ) + LOG ("\n") + //if buffer is not empty let's process it + if (active_serial_buffer_size > 0) { + for (int p = 0; p < active_serial_buffer_size ; p++) { + //reset timeout + timeout = millis(); + //feed WDT + CONFIG::wdtFeed(); + //be sure there is some space + if (count < maxLen) { + //read next char + uint8_t c = active_serial_buffer[p]; + //if it is end line + if ( (c == 13) || (c == 10) ) { + //it is an ok or a wait so this is the end + LOG ("line ") + LOG (current_line.c_str() ) + if ( (current_line == "ok") || (current_line == "wait") || ( ( (CONFIG::GetFirmwareTarget() == REPETIER) || (CONFIG::GetFirmwareTarget() == REPETIER4DV) ) && ( (current_line.indexOf ("busy:") > -1) || (current_line.startsWith ( "ok ") ) ) ) ) { + LOG ("ignore ") + LOG (current_line.c_str() ) + current_line = ""; + //check we have something before we leave and it is not + //an ack for the command + if ( (index == 0) && (count == 0) ) { + multiple_counter ++ ; + } else { + finish_check = true; + } + //we do not ignore it + } else { + if (current_line.length() > 0) { + current_line += "\n"; + //do we add current line to buffer or wait for next callback ? + if ( (count + current_line.length() ) < maxLen) { + LOG ("line added\n") + //have space so copy to buffer + for (int pp = 0 ; pp < current_line.length() ; pp++) { + buffer[count] = current_line[pp]; + count++; + } + //CONFIG::wait(1); + timeout = millis(); + if (COMMAND::check_command (current_line, NO_PIPE, false, false) ) { + multiple_counter ++ ; + } + //reset line + current_line = ""; + //no space return and add next time + } else { + //we should never reach here - but better prevent + if (p < active_serial_buffer_size) { + uint8_t * tmp_buf = new uint8_t[active_serial_buffer_size - p]; + //copy old one to new one + for (int pp = 0; pp < active_serial_buffer_size - p; pp++) { + tmp_buf[pp] = active_serial_buffer[p + pp]; + } + //delete old one + if (active_serial_buffer != NULL) { + delete active_serial_buffer; + } + //now new is the active one + active_serial_buffer = tmp_buf; + //reset size + active_serial_buffer_size = active_serial_buffer_size - p; + } + return count; + } + } + } + } else { + current_line += char (c); + //case of long line that won't fit + if (current_line.length() >= maxLen) { + //we push out what we have and what we can + //no need tp check if it is command as it is too long + for (int pp = 0 ; pp < current_line.length() ; pp++) { + CONFIG::wdtFeed(); + if (count < maxLen) { + buffer[count] = current_line[pp]; + count++; + } else { //put what is left for next time + //remove part already sent + String tmp = current_line.substring (pp); + current_line = tmp; + //save left buffer + if (p < active_serial_buffer_size) { + uint8_t * tmp_buf = new uint8_t[active_serial_buffer_size - p]; + //copy old one to new one + for (int pp = 0; pp < active_serial_buffer_size - p; pp++) { + tmp_buf[pp] = active_serial_buffer[p + pp]; + } + //delete old one + if (active_serial_buffer != NULL) { + delete active_serial_buffer; + } + //now new is the active one + active_serial_buffer = tmp_buf; + //reset size + active_serial_buffer_size = active_serial_buffer_size - p; + } + return count; + } + } + } + } + //no space return and add next time + } else { + //we should never reach here - but better prevent + //save unprocessed buffer + //create a resized buffer + uint8_t * tmp_buf = new uint8_t[active_serial_buffer_size - p]; + //copy old one to new one + for (int pp = 0; pp < active_serial_buffer_size - p; pp++) { + tmp_buf[pp] = active_serial_buffer[p + pp]; + } + //delete old one + if (active_serial_buffer != NULL) { + delete active_serial_buffer; + } + //now new is the active one + active_serial_buffer = tmp_buf; + //reset size + active_serial_buffer_size = active_serial_buffer_size - p; + return count; + } + }//end processing buffer Serial + active_serial_buffer_size = 0; + if (active_serial_buffer != NULL) { + delete active_serial_buffer; + } + active_serial_buffer = NULL; + //we chop to fill packect size not maxLen + if (count >= packet_size) { + //buffer is empty so time to clean + return count; + } + timeout = millis(); + } //end of processing serial buffer + //we got several ok / wait /busy or temperature, so we should stop to avoid a dead loop + if (multiple_counter > 5) { + LOG ("Multiple counter reached\n\r") + finish_check = true; + } + //no input during 1 s so consider we are done and we missed the end flag + if (millis() - timeout > 1000) { + finish_check = true; + LOG ("time out\r\n") + } + } // we are done for this call : buffer is full or everything is finished + //if we are done + if (finish_check) + { + //do some cleaning if needed + active_serial_buffer_size = 0; + if (active_serial_buffer != NULL) { + delete active_serial_buffer; + } + active_serial_buffer = NULL; + } + return count; + }); + response->addHeader ("Cache-Control", "no-cache"); + request->send (response); + LOG ("Start PurgeSerial\r\n") + ESPCOM::processFromSerial (true); + LOG ("End PurgeSerial\r\n") + web_interface->blockserial = false; + LOG ("Release Serial\r\n") + } else { + request->send (409, "text/plain", "Serial is busy, retry later!"); + } + } +} + +//Handle web command query and sent ack or fail instead of answer/////// +void handle_web_command_silent (AsyncWebServerRequest *request) +{ + //to save time if already disconnected + if (request->hasArg ("PAGEID") ) { + if (request->arg ("PAGEID").length() > 0 ) { + if (request->arg ("PAGEID").toInt() != id_connection) { + request->send (200, "text/plain", "Invalid command"); + return; + } + } + } + level_authenticate_type auth_level = web_interface->is_authenticated(); + if (auth_level == LEVEL_GUEST) { + request->send (401, "text/plain", "Authentication failed!\n"); + return; + } + LOG (String (request->args() ) ) + LOG (" Web silent command\r\n") +#ifdef DEBUG_ESP3D + int nb = request->args(); + for (int i = 0 ; i < nb; i++) { + LOG (request->argName (i) ) + LOG (":") + LOG (request->arg (i) ) + LOG ("\r\n") + } +#endif + String cmd = ""; + //int count ; + if (request->hasArg ("plain") || request->hasArg ("commandText") ) { + if (request->hasArg ("plain") ) { + cmd = request->arg ("plain"); + } else { + cmd = request->arg ("commandText"); + } + LOG ("Web Command:") + LOG (cmd) + LOG ("\r\n") + } else { + LOG ("invalid argument\r\n") + request->send (200, "text/plain", "Invalid command"); + return; + } + //if it is for ESP module [ESPXXX] + cmd.trim(); + int ESPpos = cmd.indexOf ("[ESP"); + if (ESPpos > -1) { + //is there the second part? + int ESPpos2 = cmd.indexOf ("]", ESPpos); + if (ESPpos2 > -1) { + //Split in command and parameters + String cmd_part1 = cmd.substring (ESPpos + 4, ESPpos2); + String cmd_part2 = ""; + //is there space for parameters? + if (ESPpos2 < cmd.length() ) { + cmd_part2 = cmd.substring (ESPpos2 + 1); + } + //if command is a valid number then execute command + if (cmd_part1.toInt() != 0) { + if (COMMAND::execute_command (cmd_part1.toInt(), cmd_part2, NO_PIPE, auth_level) ) { + request->send (200, "text/plain", "ok"); + } else { + request->send (500, "text/plain", "error"); + } + + } + //if not is not a valid [ESPXXX] command + } + } else { + //send command to serial as no need to transfer ESP command + //to avoid any pollution if Uploading file to SDCard + if ( (web_interface->blockserial) == false) { + LOG ("Send Command\r\n") + //send command + ESPCOM::println (cmd, DEFAULT_PRINTER_PIPE); + + request->send (200, "text/plain", "ok"); + } else { + request->send (200, "text/plain", "Serial is busy, retry later!"); + } + } +} + +//serial SD files list////////////////////////////////////////////////// +void handle_serial_SDFileList (AsyncWebServerRequest *request) +{ + //this is only for admin and user + if (web_interface->is_authenticated() == LEVEL_GUEST) { + web_interface->_upload_status = UPLOAD_STATUS_NONE; + request->send (401, "application/json", "{\"status\":\"Authentication failed!\"}"); + return; + } + LOG ("serial SD upload done\r\n") + String sstatus = "Ok"; + if ( (web_interface->_upload_status == UPLOAD_STATUS_FAILED) || (web_interface->_upload_status == UPLOAD_STATUS_CANCELLED) ) { + sstatus = "Upload failed"; + web_interface->_upload_status = UPLOAD_STATUS_NONE; + } + String jsonfile = "{\"status\":\"" + sstatus + "\"}"; + AsyncResponseStream *response = request->beginResponseStream ("application/json"); + response->addHeader ("Cache-Control", "no-cache"); + response->print (jsonfile.c_str() ); + request->send (response); + web_interface->blockserial = false; + web_interface->_upload_status = UPLOAD_STATUS_NONE; +} + +//SD file upload by serial +#define NB_RETRY 5 +#define MAX_RESEND_BUFFER 128 +#define SERIAL_CHECK_TIMEOUT 2000 +void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) +{ + LOG ("Uploading: ") + LOG (filename) + LOG ("\n") + static String current_line; + static bool is_comment = false; + static String current_filename; + String response; + //Guest cannot upload - only admin and user + if (web_interface->is_authenticated() == LEVEL_GUEST) { + web_interface->_upload_status = UPLOAD_STATUS_CANCELLED; + ESPCOM::println (F ("SD upload rejected"), PRINTER_PIPE); + LOG ("SD upload rejected\r\n"); + request->client()->abort(); + return; + } + //Upload start + //************** + if (!index) { + LOG ("Starting\n") + //need to lock serial out to avoid garbage in file + (web_interface->blockserial) = true; + current_line = ""; + //init flags + is_comment = false; + current_filename = filename; + web_interface->_upload_status = UPLOAD_STATUS_ONGOING; + ESPCOM::println (F ("Uploading..."), PRINTER_PIPE); + ESPCOM::flush (DEFAULT_PRINTER_PIPE); + LOG ("Clear Serial\r\n"); + if(ESPCOM::available(DEFAULT_PRINTER_PIPE)) { + ESPCOM::bridge(); + CONFIG::wait(1); + } + //command to pritnter to start print + String command = "M28 " + filename; + LOG (command); + LOG ("\r\n"); + ESPCOM::println (command, DEFAULT_PRINTER_PIPE); + ESPCOM::flush (DEFAULT_PRINTER_PIPE); + CONFIG::wait (500); + uint32_t timeout = millis(); + bool done = false; + while (!done) { //time out is 2000ms + CONFIG::wdtFeed(); + //if there is something in serial buffer + size_t len = ESPCOM::available(DEFAULT_PRINTER_PIPE); + //get size of buffer + if (len > 0) { + CONFIG::wdtFeed(); + uint8_t sbuf[len + 1]; + //read buffer + ESPCOM::readBytes (DEFAULT_PRINTER_PIPE, sbuf, len); + //convert buffer to zero end array + sbuf[len] = '\0'; + //use string because easier to handle + response = (const char*) sbuf; + LOG (response); + //if there is a wait it means purge is done + if (response.indexOf ("wait") > -1) { + LOG ("Exit start writing\r\n"); + done = true; + break; + } + //it is first command if it is failed no need to continue + //and resend command won't help + if (response.indexOf ("Resend") > -1 || response.indexOf ("failed") > -1) { + web_interface->blockserial = false; + LOG ("Error start writing\r\n"); + web_interface->_upload_status = UPLOAD_STATUS_FAILED; + request->client()->abort(); + return; + } + } + if ( (millis() - timeout) > SERIAL_CHECK_TIMEOUT) { + done = true; + } + } + } + //Upload write + //************** + if ( ( web_interface->_upload_status = UPLOAD_STATUS_ONGOING) && len) { + LOG ("Writing to serial\n") + for (int pos = 0; pos < len; pos++) { //parse full post data + //feed watchdog + CONFIG::wdtFeed(); + //it is a comment + if (data[pos] == ';') { + LOG ("Comment\n") + is_comment = true; + } + //it is an end line + else if ( (data[pos] == 13) || (data[pos] == 10) ) { + is_comment = false; + //does line fit the buffer ? + if (current_line.length() < 126) { + //do we have something in buffer ? + if (current_line.length() > 0 ) { + current_line += "\r\n"; + if (!sendLine2Serial (current_line) ) { + LOG ("Error over buffer\n") + CloseSerialUpload (true, current_filename); + request->client()->abort(); + return; + } + //reset line + current_line = ""; + //if comment line then reset + } else { + LOG ("Empy line\n") + } + } else { + //error buffer overload + LOG ("Error over buffer\n") + CloseSerialUpload (true, current_filename); + request->client()->abort(); + return; + } + } else if (!is_comment) { + if (current_line.length() < 126) { + current_line += char (data[pos]); //copy current char to buffer to send/resend + } else { + LOG ("Error over buffer\n") + CloseSerialUpload (true, current_filename); + request->client()->abort(); + return; + } + } + } + LOG ("Parsing Done\n") + } else { + LOG ("Nothing to write\n") + } + + //Upload end + //************** + if (final) { + LOG ("Final is reached\n") + //if last part does not have '\n' + if (current_line.length() > 0) { + current_line += "\r\n"; + if (!sendLine2Serial (current_line) ) { + LOG ("Error sending buffer\n") + CloseSerialUpload (true, current_filename); + request->client()->abort(); + return; + } + } + LOG ("Upload finished "); + CloseSerialUpload (false, current_filename); + } + LOG ("Exit fn\n") +} + + +//on event connect function +void handle_onevent_connect(AsyncEventSourceClient *client) +{ + if (!client->lastId()){ + //Init active ID + id_connection++; + client->send(String(id_connection).c_str(), "InitID", id_connection, 1000); + //Dispatch who is active ID + web_interface->web_events.send( String(id_connection).c_str(),"ActiveID"); + } +} + +void handle_Websocket_Event(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ + //Handle WebSocket event +} + +#endif diff --git a/src/asyncwebserver.h b/src/asyncwebserver.h new file mode 100644 index 00000000..6a7c7dcb --- /dev/null +++ b/src/asyncwebserver.h @@ -0,0 +1,48 @@ +/* + asyncwebserver.h - ESP3D sync functions 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 +*/ + +#ifndef ASYNCWEBSERVER_H +#define ASYNCWEBSERVER_H +#include "config.h" +class AsyncWebServerRequest; + +extern bool filterOnRoot (AsyncWebServerRequest *request); +extern void handle_login(AsyncWebServerRequest *request); +extern void SPIFFSFileupload (AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final); +extern void handleFileList (AsyncWebServerRequest *request); +extern void handle_not_found (AsyncWebServerRequest *request); +extern void handle_web_command (AsyncWebServerRequest *request); +extern void handle_web_command_silent (AsyncWebServerRequest *request); +extern void handle_serial_SDFileList (AsyncWebServerRequest *request); +extern void SDFile_serial_upload (AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final); +extern void handle_Websocket_Event(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len); +extern void handle_onevent_connect(AsyncEventSourceClient *client); + +#ifdef SSDP_FEATURE +extern void handle_SSDP (AsyncWebServerRequest *request); +#endif + +#ifdef WEB_UPDATE_FEATURE +extern void handleUpdate (AsyncWebServerRequest *request); +extern void WebUpdateUpload (AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final); +#endif + + +#endif diff --git a/src/command.cpp b/src/command.cpp new file mode 100644 index 00000000..236cca67 --- /dev/null +++ b/src/command.cpp @@ -0,0 +1,1718 @@ +/* + command.cpp - ESP3D configuration class + + 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 +*/ +#include "config.h" +#include "command.h" +#include "wificonf.h" +#include "webinterface.h" +#ifndef FS_NO_GLOBALS +#define FS_NO_GLOBALS +#endif +#include +#if defined(ARDUINO_ARCH_ESP32) +#include "SPIFFS.h" +#define MAX_GPIO 16 +#else +#define MAX_GPIO 37 +#endif +#ifdef TIMESTAMP_FEATURE +#include +#endif +#ifdef ESP_OLED_FEATURE +#include "esp_oled.h" +#endif + +#ifdef DHT_FEATURE +#include "DHTesp.h" +extern DHTesp dht; +#endif +String COMMAND::buffer_serial; +String COMMAND::buffer_tcp; + +#define ERROR_CMD_MSG (output == WEB_PIPE)?F("Error: Wrong Command"):F("Cmd Error") +#define INCORRECT_CMD_MSG (output == WEB_PIPE)?F("Error: Incorrect Command"):F("Incorrect Cmd") +#define OK_CMD_MSG (output == WEB_PIPE)?F("ok"):F("Cmd Ok") + +String COMMAND::get_param (String & cmd_params, const char * id, bool withspace) +{ + static String parameter; + String sid = id; + int start; + int end = -1; + parameter = ""; + //if no id it means it is first part of cmd + if (strlen (id) == 0) { + start = 0; + } + //else find id position + else { + start = cmd_params.indexOf (id); + } + //if no id found and not first part leave + if (start == -1 ) { + return parameter; + } + //password and SSID can have space so handle it + //if no space expected use space as delimiter + if (!withspace) { + end = cmd_params.indexOf (" ", start); + } +#ifdef AUTHENTICATION_FEATURE + //if space expected only one parameter but additional password may be present + else if (sid != " pwd=") { + end = cmd_params.indexOf (" pwd=", start); + } +#endif + //if no end found - take all + if (end == -1) { + end = cmd_params.length(); + } + //extract parameter + parameter = cmd_params.substring (start + strlen (id), end); + //be sure no extra space + parameter.trim(); + return parameter; +} +#ifdef AUTHENTICATION_FEATURE +//check admin password +bool COMMAND::isadmin (String & cmd_params) +{ + String adminpassword; + String sadminPassword; + if (!CONFIG::read_string (EP_ADMIN_PWD, sadminPassword, MAX_LOCAL_PASSWORD_LENGTH) ) { + LOG ("ERROR getting admin\r\n") + sadminPassword = FPSTR (DEFAULT_ADMIN_PWD); + } + adminpassword = get_param (cmd_params, "pwd=", true); + if (!sadminPassword.equals (adminpassword) ) { + LOG("Not identified from command line\r\n") + return false; + } else { + return true; + } +} +//check user password - admin password is also valid +bool COMMAND::isuser (String & cmd_params) +{ + String userpassword; + String suserPassword; + if (!CONFIG::read_string (EP_USER_PWD, suserPassword, MAX_LOCAL_PASSWORD_LENGTH) ) { + LOG ("ERROR getting user\r\n") + suserPassword = FPSTR (DEFAULT_USER_PWD); + } + userpassword = get_param (cmd_params, "pwd=", true); + //it is not user password + if (!suserPassword.equals (userpassword) ) { + //check admin password + return COMMAND::isadmin (cmd_params); + } else { + return true; + } +} +#endif +bool COMMAND::execute_command (int cmd, String cmd_params, tpipe output, level_authenticate_type auth_level, ESPResponseStream *espresponse) +{ + bool response = true; + level_authenticate_type auth_type = auth_level; +#ifdef AUTHENTICATION_FEATURE + + if (isadmin(cmd_params)) { + auth_type = LEVEL_ADMIN; + LOG("you are Admin\r\n"); + } + if (isuser (cmd_params) && (auth_type != LEVEL_ADMIN) ) { + auth_type = LEVEL_USER; + LOG("you are User\r\n"); + } +#ifdef DEBUG_ESP3D + if ( auth_type == LEVEL_ADMIN) + { + LOG("admin identified\r\n"); + } + else { + if( auth_type == LEVEL_USER) + { + LOG("user identified\r\n"); + } + else + { + LOG("guest identified\r\n"); + } + } +#endif +#endif + //manage parameters + byte mode = 254; + String parameter; + LOG ("Execute Command\r\n") + switch (cmd) { + //STA SSID + //[ESP100][pwd=] + case 100: + parameter = get_param (cmd_params, "", true); + if (!CONFIG::isSSIDValid (parameter.c_str() ) ) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + } +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + } else +#endif + if (!CONFIG::write_string (EP_STA_SSID, parameter.c_str() ) ) { + ESPCOM::println (ERROR_CMD_MSG, output, espresponse); + response = false; + } else { + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + break; + //STA Password + //[ESP101][pwd=] + case 101: + parameter = get_param (cmd_params, "", true); + if (!CONFIG::isPasswordValid (parameter.c_str() ) ) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } else +#endif + if (!CONFIG::write_string (EP_STA_PASSWORD, parameter.c_str() ) ) { + ESPCOM::println (ERROR_CMD_MSG, output, espresponse); + response = false; + } else { + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + break; + //Hostname + //[ESP102][pwd=] + case 102: + parameter = get_param (cmd_params, "", true); + if (!CONFIG::isHostnameValid (parameter.c_str() ) ) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } else +#endif + if (!CONFIG::write_string (EP_HOSTNAME, parameter.c_str() ) ) { + ESPCOM::println (ERROR_CMD_MSG, output, espresponse); + response = false; + } else { + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + break; + //Wifi mode (STA/AP) + //[ESP103][pwd=] + case 103: + parameter = get_param (cmd_params, "", true); + if (parameter == "STA") { + mode = CLIENT_MODE; + } else if (parameter == "AP") { + mode = AP_MODE; + } else { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } + if ( (mode == CLIENT_MODE) || (mode == AP_MODE) ) { +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } else +#endif + if (!CONFIG::write_byte (EP_WIFI_MODE, mode) ) { + ESPCOM::println (ERROR_CMD_MSG, output, espresponse); + response = false; + } else { + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + } + break; + //STA IP mode (DHCP/STATIC) + //[ESP104][pwd=] + case 104: + parameter = get_param (cmd_params, "", true); + if (parameter == "STATIC") { + mode = STATIC_IP_MODE; + } else if (parameter == "DHCP") { + mode = DHCP_MODE; + } else { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } + if ( (mode == STATIC_IP_MODE) || (mode == DHCP_MODE) ) { +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } else +#endif + if (!CONFIG::write_byte (EP_STA_IP_MODE, mode) ) { + ESPCOM::println (ERROR_CMD_MSG, output, espresponse); + response = false; + } else { + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + } + break; + //AP SSID + //[ESP105][pwd=] + case 105: + parameter = get_param (cmd_params, "", true); + if (!CONFIG::isSSIDValid (parameter.c_str() ) ) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } else +#endif + if (!CONFIG::write_string (EP_AP_SSID, parameter.c_str() ) ) { + ESPCOM::println (ERROR_CMD_MSG, output, espresponse); + response = false; + } else { + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + break; + //AP Password + //[ESP106][pwd=] + case 106: + parameter = get_param (cmd_params, "", true); + if (!CONFIG::isPasswordValid (parameter.c_str() ) ) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } else +#endif + if (!CONFIG::write_string (EP_AP_PASSWORD, parameter.c_str() ) ) { + ESPCOM::println (ERROR_CMD_MSG, output, espresponse); + response = false; + } else { + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + break; + //AP IP mode (DHCP/STATIC) + //[ESP107][pwd=] + case 107: + parameter = get_param (cmd_params, "", true); + if (parameter == "STATIC") { + mode = STATIC_IP_MODE; + } else if (parameter == "DHCP") { + mode = DHCP_MODE; + } else { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } + if ( (mode == STATIC_IP_MODE) || (mode == DHCP_MODE) ) { +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } else +#endif + if (!CONFIG::write_byte (EP_AP_IP_MODE, mode) ) { + ESPCOM::println (ERROR_CMD_MSG, output, espresponse); + response = false; + } else { + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + } + break; + // Set wifi on/off + //[ESP110][pwd=] + case 110: + parameter = get_param (cmd_params, "", true); + if (parameter == "on") { + mode = 1; + } else if (parameter == "off") { + mode = 0; + } else if (parameter == "restart") { + mode = 2; + } else { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } + if (response) { +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } else +#endif + if (mode == 0) { + if ((WiFi.getMode() != WIFI_OFF) || wifi_config.WiFi_on) { + //disable wifi + ESPCOM::println ("Disabling Wifi", output, espresponse); +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::display_signal(-1); + OLED_DISPLAY::setCursor(0, 0); + ESPCOM::print("", OLED_PIPE); + OLED_DISPLAY::setCursor(0, 16); + ESPCOM::print("", OLED_PIPE); + OLED_DISPLAY::setCursor(0, 48); + ESPCOM::print("Wifi disabled", OLED_PIPE); +#endif + WiFi.disconnect(true); + WiFi.enableSTA (false); + WiFi.enableAP (false); + WiFi.mode (WIFI_OFF); + wifi_config.WiFi_on = false; + wifi_config.Disable_servers(); + return response; + } else { + ESPCOM::println ("Wifi already off", output, espresponse); + } + } else if (mode == 1) { //restart device is the best way to start everything clean + if ((WiFi.getMode() == WIFI_OFF) || !wifi_config.WiFi_on) { + ESPCOM::println ("Enabling Wifi", output, espresponse); + web_interface->restartmodule = true; + } else { + ESPCOM::println ("Wifi already on", output, espresponse); + } + } else { //restart wifi and restart is the best way to start everything clean + ESPCOM::println ("Enabling Wifi", output, espresponse); + web_interface->restartmodule = true; + } + } + break; + //Get current IP + //[ESP111]
+ case 111: { + String currentIP ; + if (WiFi.getMode() == WIFI_STA) { + currentIP = WiFi.localIP().toString(); + } else { + currentIP = WiFi.softAPIP().toString(); + } + ESPCOM::print (cmd_params, output, espresponse); + ESPCOM::println (currentIP, output, espresponse); + LOG (cmd_params) + LOG (currentIP) + LOG ("\r\n") + } + break; + //Get hostname + //[ESP112]
+ case 112: { + String shost ; + if (!CONFIG::read_string (EP_HOSTNAME, shost, MAX_HOSTNAME_LENGTH) ) { + shost = wifi_config.get_default_hostname(); + } + ESPCOM::print (cmd_params, output, espresponse); + ESPCOM::println (shost, output, espresponse); + LOG (cmd_params) + LOG (shost) + LOG ("\r\n") + } + break; +#if defined(TIMESTAMP_FEATURE) + //restart time client + case 114: { + CONFIG::init_time_client(); + LOG ("restart time client\r\n") + } + break; + //get time client + case 115: { + struct tm tmstruct; + time_t now; + String stmp = ""; + time(&now); + localtime_r(&now, &tmstruct); + stmp = String((tmstruct.tm_year)+1900) + "-"; + if (((tmstruct.tm_mon)+1) < 10) stmp +="0"; + stmp += String(( tmstruct.tm_mon)+1) + "-"; + if (tmstruct.tm_mday < 10) stmp +="0"; + stmp += String(tmstruct.tm_mday) + " "; + if (tmstruct.tm_hour < 10) stmp +="0"; + stmp += String(tmstruct.tm_hour) + ":"; + if (tmstruct.tm_min < 10) stmp +="0"; + stmp += String(tmstruct.tm_min) + ":"; + if (tmstruct.tm_sec < 10) stmp +="0"; + stmp += String(tmstruct.tm_sec); + ESPCOM::println(stmp.c_str(), output); + } + break; +#endif + +#ifdef DIRECT_PIN_FEATURE + //Get/Set pin value + //[ESP201]P V [PULLUP=YES RAW=YES]pwd= + case 201: + parameter = get_param (cmd_params, "", true); +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } else +#endif + { + //check if have pin + parameter = get_param (cmd_params, "P", false); + LOG ("Pin:") + LOG (parameter) + LOG ("\r\n") + if (parameter == "") { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } else { + int pin = parameter.toInt(); + //check pin is valid and not serial used pins + if ( (pin >= 0) && (pin <= 16) && ! ( (pin == 1) || (pin == 3) ) ) { + //check if is set or get + parameter = get_param (cmd_params, "V", false); + //it is a get + if (parameter == "") { + //this is to not set pin mode + parameter = get_param (cmd_params, "RAW=", false); + if (parameter != "YES") { + parameter = get_param (cmd_params, "PULLUP=", false); + if (parameter == "YES") { + //GPIO16 is different than others + if (pin < MAX_GPIO) { + LOG ("Set as input pull up\r\n") + pinMode (pin, INPUT_PULLUP); + } +#ifdef ARDUINO_ARCH_ESP8266 + else { + LOG ("Set as input pull down 16\r\n") + pinMode (pin, INPUT_PULLDOWN_16); + } +#endif + } else { + LOG ("Set as input\r\n") + pinMode (pin, INPUT); + } + } + int value = digitalRead (pin); + LOG ("Read:"); + LOG (String (value).c_str() ) + LOG ("\n"); + ESPCOM::println (String (value).c_str(), output, espresponse); + } else { + //it is a set + int value = parameter.toInt(); + //verify it is a 0 or a 1 + if ( (value == 0) || (value == 1) ) { + pinMode (pin, OUTPUT); + LOG ("Set:") + LOG (String ( (value == 0) ? LOW : HIGH) ) + LOG ("\r\n") + digitalWrite (pin, (value == 0) ? LOW : HIGH); + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } else { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } + } + } else { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } + } + } + break; +#endif + + +#ifdef ESP_OLED_FEATURE + //Output to oled + //[ESP210] + case 210: { + parameter = get_param (cmd_params, "C=", false); + int c = parameter.toInt(); + parameter = get_param (cmd_params, "L=", false); + int l = parameter.toInt(); + parameter = get_param (cmd_params, "T=", true); + OLED_DISPLAY::setCursor(c, l); + ESPCOM::print(parameter.c_str(), OLED_PIPE); + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + break; + //Output to oled line 1 + //[ESP211] + case 211: { + parameter = get_param (cmd_params, "", true); + OLED_DISPLAY::setCursor(0, 0); + ESPCOM::print(parameter.c_str(), OLED_PIPE); + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + break; + //Output to oled line 2 + //[ESP212] + case 212: { + parameter = get_param (cmd_params, "", true); + OLED_DISPLAY::setCursor(0, 16); + ESPCOM::print(parameter.c_str(), OLED_PIPE); + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + break; + //Output to oled line 3 + //[ESP213] + case 213: { + parameter = get_param (cmd_params, "", true); + OLED_DISPLAY::setCursor(0, 32); + ESPCOM::print(parameter.c_str(), OLED_PIPE); + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + break; + //Output to oled line 4 + //[ESP214] + case 214: { + parameter = get_param (cmd_params, "", true); + OLED_DISPLAY::setCursor(0, 48); + ESPCOM::print(parameter.c_str(), OLED_PIPE); + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + break; +#endif + //Get full EEPROM settings content + //[ESP400] + case 400: { + char sbuf[MAX_DATA_LENGTH + 1]; + uint8_t ipbuf[4]; + byte bbuf = 0; + int ibuf = 0; + parameter = get_param (cmd_params, "", true); + //Start JSON + ESPCOM::println (F ("{\"EEPROM\":["), output, espresponse); + if (cmd_params == "network" || cmd_params == "") { + + //1- Baud Rate + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_BAUD_RATE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"I\",\"V\":\""), output, espresponse); + if (!CONFIG::read_buffer (EP_BAUD_RATE, (byte *) &ibuf, INTEGER_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (ibuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Baud Rate\",\"O\":[{\"9600\":\"9600\"},{\"19200\":\"19200\"},{\"38400\":\"38400\"},{\"57600\":\"57600\"},{\"115200\":\"115200\"},{\"230400\":\"230400\"},{\"250000\":\"250000\"}]}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //2-Sleep Mode + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_SLEEP_MODE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"B\",\"V\":\""), output, espresponse); + if (!CONFIG::read_byte (EP_SLEEP_MODE, &bbuf ) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (bbuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Sleep Mode\",\"O\":[{\"None\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (WIFI_NONE_SLEEP), output, espresponse); + ESPCOM::print (F ("\"},{\"Light\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (WIFI_LIGHT_SLEEP), output, espresponse); + ESPCOM::print (F ("\"},{\"Modem\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (WIFI_MODEM_SLEEP), output, espresponse); + ESPCOM::print (F ("\"}]}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //3-Web Port + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_WEB_PORT), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"I\",\"V\":\""), output, espresponse); + if (!CONFIG::read_buffer (EP_WEB_PORT, (byte *) &ibuf, INTEGER_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (ibuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Web Port\",\"S\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (DEFAULT_MAX_WEB_PORT), output, espresponse); + ESPCOM::print (F ("\",\"M\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (DEFAULT_MIN_WEB_PORT), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //4-Data Port + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_DATA_PORT), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"I\",\"V\":\""), output, espresponse); + if (!CONFIG::read_buffer (EP_DATA_PORT, (byte *) &ibuf, INTEGER_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (ibuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Data Port\",\"S\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (DEFAULT_MAX_DATA_PORT), output, espresponse); + ESPCOM::print (F ("\",\"M\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (DEFAULT_MIN_DATA_PORT), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); +#ifdef AUTHENTICATION_FEATURE + //5-Admin password + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_ADMIN_PWD), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"S\",\"V\":\""), output, espresponse); + if (!CONFIG::read_string (EP_ADMIN_PWD, sbuf, MAX_LOCAL_PASSWORD_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ("********", output, espresponse); + } + ESPCOM::print (F ("\",\"S\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MAX_LOCAL_PASSWORD_LENGTH), output, espresponse); + ESPCOM::print (F ("\",\"H\":\"Admin Password\",\"M\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MIN_LOCAL_PASSWORD_LENGTH), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //6-User password + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_USER_PWD), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"S\",\"V\":\""), output, espresponse); + if (!CONFIG::read_string (EP_USER_PWD, sbuf, MAX_LOCAL_PASSWORD_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ("********", output, espresponse); + } + ESPCOM::print (F ("\",\"S\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MAX_LOCAL_PASSWORD_LENGTH), output, espresponse); + ESPCOM::print (F ("\",\"H\":\"User Password\",\"M\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MIN_LOCAL_PASSWORD_LENGTH), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); +#endif + //7-Hostname + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_HOSTNAME), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"S\",\"V\":\""), output, espresponse); + if (!CONFIG::read_string (EP_HOSTNAME, sbuf, MAX_HOSTNAME_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print (sbuf, output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Hostname\" ,\"S\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MAX_HOSTNAME_LENGTH), output, espresponse); + ESPCOM::print (F ("\", \"M\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MIN_HOSTNAME_LENGTH), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //8-wifi mode + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_WIFI_MODE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"B\",\"V\":\""), output, espresponse); + if (!CONFIG::read_byte (EP_WIFI_MODE, &bbuf ) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (bbuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Wifi mode\",\"O\":[{\"AP\":\"1\"},{\"STA\":\"2\"}]}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //9-STA SSID + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_STA_SSID), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"S\",\"V\":\""), output, espresponse); + if (!CONFIG::read_string (EP_STA_SSID, sbuf, MAX_SSID_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print (sbuf, output, espresponse); + } + ESPCOM::print (F ("\",\"S\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MAX_SSID_LENGTH), output, espresponse); + ESPCOM::print (F ("\",\"H\":\"Station SSID\",\"M\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MIN_SSID_LENGTH), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //10-STA password + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_STA_PASSWORD), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"S\",\"V\":\""), output, espresponse); + if (!CONFIG::read_string (EP_STA_PASSWORD, sbuf, MAX_PASSWORD_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ("********", output, espresponse); + } + ESPCOM::print (F ("\",\"S\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MAX_PASSWORD_LENGTH), output, espresponse); + ESPCOM::print (F ("\",\"H\":\"Station Password\",\"M\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MIN_PASSWORD_LENGTH), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //11-Station Network Mode + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_STA_PHY_MODE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"B\",\"V\":\""), output, espresponse); + if (!CONFIG::read_byte (EP_STA_PHY_MODE, &bbuf ) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (bbuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Station Network Mode\",\"O\":[{\"11b\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (WIFI_PHY_MODE_11B), output, espresponse); + ESPCOM::print (F ("\"},{\"11g\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (WIFI_PHY_MODE_11G), output, espresponse); + ESPCOM::print (F ("\"},{\"11n\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (WIFI_PHY_MODE_11N), output, espresponse); + ESPCOM::print (F ("\"}]}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //12-STA IP mode + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_STA_IP_MODE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"B\",\"V\":\""), output, espresponse); + if (!CONFIG::read_byte (EP_STA_IP_MODE, &bbuf ) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (bbuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Station IP Mode\",\"O\":[{\"DHCP\":\"1\"},{\"Static\":\"2\"}]}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //13-STA static IP + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_STA_IP_VALUE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"A\",\"V\":\""), output, espresponse); + if (!CONFIG::read_buffer (EP_STA_IP_VALUE, (byte *) ipbuf, IP_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print (IPAddress (ipbuf).toString().c_str(), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Station Static IP\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //14-STA static Mask + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_STA_MASK_VALUE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"A\",\"V\":\""), output, espresponse); + if (!CONFIG::read_buffer (EP_STA_MASK_VALUE, (byte *) ipbuf, IP_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print (IPAddress (ipbuf).toString().c_str(), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Station Static Mask\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //15-STA static Gateway + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_STA_GATEWAY_VALUE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"A\",\"V\":\""), output, espresponse); + if (!CONFIG::read_buffer (EP_STA_GATEWAY_VALUE, (byte *) ipbuf, IP_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print (IPAddress (ipbuf).toString().c_str(), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Station Static Gateway\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //16-AP SSID + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_AP_SSID), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"S\",\"V\":\""), output, espresponse); + if (!CONFIG::read_string (EP_AP_SSID, sbuf, MAX_SSID_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print (sbuf, output, espresponse); + } + ESPCOM::print (F ("\",\"S\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MAX_SSID_LENGTH), output, espresponse); + ESPCOM::print (F ("\",\"H\":\"AP SSID\",\"M\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MIN_SSID_LENGTH), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //17-AP password + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_AP_PASSWORD), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"S\",\"V\":\""), output, espresponse); + if (!CONFIG::read_string (EP_AP_PASSWORD, sbuf, MAX_PASSWORD_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ("********", output, espresponse); + } + ESPCOM::print (F ("\",\"S\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MAX_PASSWORD_LENGTH), output, espresponse); + ESPCOM::print (F ("\",\"H\":\"AP Password\",\"M\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MIN_PASSWORD_LENGTH), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //18 - AP Network Mode + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_AP_PHY_MODE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"B\",\"V\":\""), output, espresponse); + if (!CONFIG::read_byte (EP_AP_PHY_MODE, &bbuf ) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (bbuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"AP Network Mode\",\"O\":[{\"11b\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (WIFI_PHY_MODE_11B), output, espresponse); + ESPCOM::print (F ("\"},{\"11g\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (WIFI_PHY_MODE_11G), output, espresponse); + ESPCOM::print (F ("\"}]}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //19-AP SSID visibility + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_SSID_VISIBLE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"B\",\"V\":\""), output, espresponse); + if (!CONFIG::read_byte (EP_SSID_VISIBLE, &bbuf ) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (bbuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"SSID Visible\",\"O\":[{\"No\":\"0\"},{\"Yes\":\"1\"}]}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //20-AP Channel + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_CHANNEL), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"B\",\"V\":\""), output, espresponse); + if (!CONFIG::read_byte (EP_CHANNEL, &bbuf ) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (bbuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"AP Channel\",\"O\":["), output, espresponse); + for (int i = 1; i < 12 ; i++) { + ESPCOM::print (F ("{\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (i), output, espresponse); + ESPCOM::print (F ("\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (i), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); + if (i < 11) { + ESPCOM::print (F (","), output, espresponse); + } + } + ESPCOM::print (F ("]}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //21-AP Authentication + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_AUTH_TYPE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"B\",\"V\":\""), output, espresponse); + if (!CONFIG::read_byte (EP_AUTH_TYPE, &bbuf ) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (bbuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Authentication\",\"O\":[{\"Open\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (AUTH_OPEN), output, espresponse); + ESPCOM::print (F ("\"},{\"WPA\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (AUTH_WPA_PSK), output, espresponse); + ESPCOM::print (F ("\"},{\"WPA2\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (AUTH_WPA2_PSK), output, espresponse); + ESPCOM::print (F ("\"},{\"WPA/WPA2\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (AUTH_WPA_WPA2_PSK), output, espresponse); + ESPCOM::print (F ("\"}]}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //22-AP IP mode + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_AP_IP_MODE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"B\",\"V\":\""), output, espresponse); + if (!CONFIG::read_byte (EP_AP_IP_MODE, &bbuf ) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (bbuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"AP IP Mode\",\"O\":[{\"DHCP\":\"1\"},{\"Static\":\"2\"}]}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //23-AP static IP + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_AP_IP_VALUE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"A\",\"V\":\""), output, espresponse); + if (!CONFIG::read_buffer (EP_AP_IP_VALUE, (byte *) ipbuf, IP_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print (IPAddress (ipbuf).toString().c_str(), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"AP Static IP\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //24-AP static Mask + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_AP_MASK_VALUE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"A\",\"V\":\""), output, espresponse); + if (!CONFIG::read_buffer (EP_AP_MASK_VALUE, (byte *) ipbuf, IP_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print (IPAddress (ipbuf).toString().c_str(), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"AP Static Mask\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //25-AP static Gateway + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_AP_GATEWAY_VALUE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"A\",\"V\":\""), output, espresponse); + if (!CONFIG::read_buffer (EP_AP_GATEWAY_VALUE, (byte *) ipbuf, IP_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print (IPAddress (ipbuf).toString().c_str(), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"AP Static Gateway\"}"), output, espresponse); +#if defined(TIMESTAMP_FEATURE) + ESPCOM::println (F (","), output, espresponse); + + //26-Time zone + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_TIMEZONE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"B\",\"V\":\""), output, espresponse); + if (!CONFIG::read_byte (EP_TIMEZONE, &bbuf ) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr ( (int8_t) bbuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Time Zone\",\"O\":["), output, espresponse); + for (int i = -12; i <= 12 ; i++) { + ESPCOM::print (F ("{\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (i), output, espresponse); + ESPCOM::print (F ("\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (i), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); + if (i < 12) { + ESPCOM::print (F (","), output, espresponse); + } + } + ESPCOM::print (F ("]}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //27- DST + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_TIME_ISDST), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"B\",\"V\":\""), output, espresponse); + if (!CONFIG::read_byte (EP_TIME_ISDST, &bbuf ) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (bbuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Day Saving Time\",\"O\":[{\"No\":\"0\"},{\"Yes\":\"1\"}]}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //28- Time Server1 + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_TIME_SERVER1), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"S\",\"V\":\""), output, espresponse); + if (!CONFIG::read_string (EP_TIME_SERVER1, sbuf, MAX_DATA_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print (sbuf, output, espresponse); + } + ESPCOM::print (F ("\",\"S\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MAX_DATA_LENGTH), output, espresponse); + ESPCOM::print (F ("\",\"H\":\"Time Server 1\",\"M\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MIN_DATA_LENGTH), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //29- Time Server2 + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_TIME_SERVER2), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"S\",\"V\":\""), output, espresponse); + if (!CONFIG::read_string (EP_TIME_SERVER2, sbuf, MAX_DATA_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print (sbuf, output, espresponse); + } + ESPCOM::print (F ("\",\"S\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MAX_DATA_LENGTH), output, espresponse); + ESPCOM::print (F ("\",\"H\":\"Time Server 2\",\"M\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MIN_DATA_LENGTH), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //30- Time Server3 + ESPCOM::print (F ("{\"F\":\"network\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_TIME_SERVER3), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"S\",\"V\":\""), output, espresponse); + if (!CONFIG::read_string (EP_TIME_SERVER3, sbuf, MAX_DATA_LENGTH) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print (sbuf, output, espresponse); + } + ESPCOM::print (F ("\",\"S\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MAX_DATA_LENGTH), output, espresponse); + ESPCOM::print (F ("\",\"H\":\"Time Server 3\",\"M\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MIN_DATA_LENGTH), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); +#endif + } + + if (cmd_params == "printer" || cmd_params == "") { + if (cmd_params == "") { + ESPCOM::println (F (","), output, espresponse); + } + //Target FW + ESPCOM::print (F ("{\"F\":\"printer\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_TARGET_FW), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"B\",\"V\":\""), output, espresponse); + if (!CONFIG::read_byte (EP_TARGET_FW, &bbuf ) ) { + ESPCOM::print ("Unknown", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (bbuf), output, espresponse); + } + ESPCOM::print (F ("\",\"H\":\"Target FW\",\"O\":[{\"Repetier\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (REPETIER), output, espresponse); + ESPCOM::print (F ("\"},{\"Repetier for Davinci\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (REPETIER4DV), output, espresponse); + ESPCOM::print (F ("\"},{\"Marlin\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MARLIN), output, espresponse); + ESPCOM::print (F ("\"},{\"Marlin Kimbra\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (MARLINKIMBRA), output, espresponse); + ESPCOM::print (F ("\"},{\"Smoothieware\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (SMOOTHIEWARE), output, espresponse); + ESPCOM::print (F ("\"},{\"Grbl\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (GRBL), output, espresponse); + ESPCOM::print (F ("\"},{\"Unknown\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (UNKNOWN_FW), output, espresponse); + ESPCOM::print (F ("\"}]}"), output, espresponse); + ESPCOM::println (F (","), output, espresponse); + + //Output flag + ESPCOM::print (F ("{\"F\":\"printer\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_OUTPUT_FLAG), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"F\",\"V\":\""), output, espresponse); + if (!CONFIG::read_byte (EP_OUTPUT_FLAG, &bbuf ) ) { + ESPCOM::print ("???", output, espresponse); + } else { + ESPCOM::print ( (const char *) CONFIG::intTostr (bbuf), output, espresponse); + } + String s = "\",\"H\":\"Output msg\",\"O\":[{\"M117\":\""; + s+= CONFIG::intTostr(FLAG_BLOCK_M117); + s+= "\"}"; +#ifdef ESP_OLED_FEATURE + s+=",{\"Oled\":\""; + s+= CONFIG::intTostr(FLAG_BLOCK_OLED); + s+="\"}"; +#endif + s+=",{\"Serial\":\""; + s+= CONFIG::intTostr(FLAG_BLOCK_SERIAL); + s+="\"}"; +#ifdef WS_DATA_FEATURE + s+=",{\"Web Socket\":\""; + s+= CONFIG::intTostr(FLAG_BLOCK_WSOCKET); + s+="\"}"; +#endif +#ifdef TCP_IP_DATA_FEATURE + s+=",{\"TCP\":\""; + s+= CONFIG::intTostr(FLAG_BLOCK_TCP); + s+="\"}"; +#endif + s+= "]}"; + ESPCOM::print (s, output, espresponse); + +#ifdef DHT_FEATURE + + //DHT type + ESPCOM::println (F (","), output, espresponse); + ESPCOM::print (F ("{\"F\":\"printer\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_DHT_TYPE), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"B\",\"V\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (CONFIG::DHT_type), output, espresponse); + ESPCOM::print (F ("\",\"H\":\"DHT Type\",\"O\":[{\"None\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (255), output, espresponse); + ESPCOM::print (F ("\"},{\"DHT11\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (DHTesp::DHT11), output, espresponse); + ESPCOM::print (F ("\"},{\"DHT22\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (DHTesp::DHT22), output, espresponse); + ESPCOM::print (F ("\"},{\"AM2302\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (DHTesp::RHT03), output, espresponse); + ESPCOM::print (F ("\"},{\"RHT03\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (DHTesp::AM2302), output, espresponse); + ESPCOM::print (F ("\"}]}"), output, espresponse); + + //DHT interval + ESPCOM::println (F (","), output, espresponse); + ESPCOM::print (F ("{\"F\":\"printer\",\"P\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (EP_DHT_INTERVAL), output, espresponse); + ESPCOM::print (F ("\",\"T\":\"I\",\"V\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (CONFIG::DHT_interval), output, espresponse); + ESPCOM::print (F ("\",\"H\":\"DHT check (seconds)\",\"S\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (DEFAULT_MAX_WEB_PORT), output, espresponse); + ESPCOM::print (F ("\",\"M\":\""), output, espresponse); + ESPCOM::print ( (const char *) CONFIG::intTostr (0), output, espresponse); + ESPCOM::print (F ("\"}"), output, espresponse); +#endif + + } + + //end JSON + ESPCOM::println (F ("\n]}"), output, espresponse); + } + break; + + //Set EEPROM setting + //[ESP401]P= T= V= pwd= + case 401: { + //check validity of parameters + String spos = get_param (cmd_params, "P=", false); + String styp = get_param (cmd_params, "T=", false); + String sval = get_param (cmd_params, "V=", true); + sval.trim(); + int pos = spos.toInt(); + if ( (pos == 0 && spos != "0") || (pos > LAST_EEPROM_ADDRESS || pos < 0) ) { + response = false; + } + if (! (styp == "B" || styp == "S" || styp == "A" || styp == "I" || styp == "F") ) { + response = false; + } + if (sval.length() == 0) { + response = false; + } + + +#ifdef AUTHENTICATION_FEATURE + if (response) { + //check authentication + level_authenticate_type auth_need = LEVEL_ADMIN; + for (int i = 0; i < AUTH_ENTRY_NB; i++) { + if (Setting[i][0] == pos ) { + auth_need = (level_authenticate_type) (Setting[i][1]); + i = AUTH_ENTRY_NB; + } + } + if ( (auth_need == LEVEL_ADMIN && auth_type == LEVEL_USER) || (auth_type == LEVEL_GUEST) ) { + response = false; + } + } +#endif + if (response) { + if ((styp == "B") || (styp == "F")){ + byte bbuf = sval.toInt(); + if (!CONFIG::write_byte (pos, bbuf) ) { + response = false; + } else { + //dynamique refresh is better than restart the board + if (pos == EP_OUTPUT_FLAG){ + CONFIG::output_flag = bbuf; + } + if (pos == EP_TARGET_FW) { + CONFIG::InitFirmwareTarget(); + } + +#ifdef DHT_FEATURE + if (pos == EP_DHT_TYPE) { + CONFIG::DHT_type = bbuf; + CONFIG::InitDHT(true); + } +#endif +#if defined(TIMESTAMP_FEATURE) + if ( (pos == EP_TIMEZONE) || (pos == EP_TIME_ISDST) || (pos == EP_TIME_SERVER1) || (pos == EP_TIME_SERVER2) || (pos == EP_TIME_SERVER3) ) { + CONFIG::init_time_client(); + } +#endif + } + } + if (styp == "I") { + int ibuf = sval.toInt(); + if (!CONFIG::write_buffer (pos, (const byte *) &ibuf, INTEGER_LENGTH) ) { + response = false; + } else { +#ifdef DHT_FEATURE + if (pos == EP_DHT_INTERVAL) { + CONFIG::DHT_interval = ibuf; + } +#endif + } + } + if (styp == "S") { + if (!CONFIG::write_string (pos, sval.c_str() ) ) { + response = false; + } + } + if (styp == "A") { + byte ipbuf[4]; + if (CONFIG::split_ip (sval.c_str(), ipbuf) < 4) { + response = false; + } else if (!CONFIG::write_buffer (pos, ipbuf, IP_LENGTH) ) { + response = false; + } + } + } + if (!response) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + } else { + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } + + } + break; + + //Get available AP list (limited to 30) + //output is JSON or plain text according parameter + //[ESP410] + case 410: { + parameter = get_param (cmd_params, "", true); + bool plain = (parameter == "plain"); + +#if defined(ASYNCWEBSERVER) + if (!plain) { + ESPCOM::print (F ("{\"AP_LIST\":["), output, espresponse); + } + int n = WiFi.scanComplete(); + if (n == -2) { + WiFi.scanNetworks (ESP_USE_ASYNC); + } else if (n) { +#else + int n = WiFi.scanNetworks (); + if (!plain) { + ESPCOM::print (F ("{\"AP_LIST\":["), output, espresponse); + } +#endif + + for (int i = 0; i < n; ++i) { + if (i > 0) { + if (!plain) { + ESPCOM::print (F (","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + } + if (!plain) { + ESPCOM::print (F ("{\"SSID\":\""), output, espresponse); + } + ESPCOM::print (WiFi.SSID (i).c_str(), output, espresponse); + if (!plain) { + ESPCOM::print (F ("\",\"SIGNAL\":\""), output, espresponse); + } else { + ESPCOM::print (F ("\t"), output, espresponse); + } + ESPCOM::print (CONFIG::intTostr (wifi_config.getSignal (WiFi.RSSI (i) ) ), output, espresponse);; + //ESPCOM::print(F("%"), output, espresponse); + if (!plain) { + ESPCOM::print (F ("\",\"IS_PROTECTED\":\""), output, espresponse); + } + if (WiFi.encryptionType (i) == ENC_TYPE_NONE) { + if (!plain) { + ESPCOM::print (F ("0"), output, espresponse); + } else { + ESPCOM::print (F ("\tOpen"), output, espresponse); + } + } else { + if (!plain) { + ESPCOM::print (F ("1"), output, espresponse); + } else { + ESPCOM::print (F ("\tSecure"), output, espresponse); + } + } + if (!plain) { + ESPCOM::print (F ("\"}"), output, espresponse); + } + } + WiFi.scanDelete(); +#if defined(ASYNCWEBSERVER) + if (WiFi.scanComplete() == -2) { + WiFi.scanNetworks (ESP_USE_ASYNC); + } + } +#endif + if (!plain) { + ESPCOM::print (F ("]}"), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + } + break; + //Get ESP current status in plain or JSON + //[ESP420] + case 420: { + parameter = get_param (cmd_params, "", true); + CONFIG::print_config (output, (parameter == "plain"), espresponse); + } + break; + //Set ESP mode + //cmd is RESET, SAFEMODE, RESTART + //[ESP444]pwd= + case 444: + parameter = get_param (cmd_params, "", true); +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } else +#endif + { + if (parameter == "RESET") { + CONFIG::reset_config(); + ESPCOM::println (F ("Reset done - restart needed"), output, espresponse); + } else if (parameter == "SAFEMODE") { + wifi_config.Safe_Setup(); + ESPCOM::println (F ("Set Safe Mode - restart needed"), output, espresponse); + } else if (parameter == "RESTART") { + ESPCOM::println (F ("Restart started"), output, espresponse); + web_interface->restartmodule = true; + } else { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } + } + break; +#ifdef AUTHENTICATION_FEATURE + //Change / Reset user password + //[ESP555]pwd= + case 555: { + if (auth_type == LEVEL_ADMIN) { + parameter = get_param (cmd_params, "", true); + if (parameter.length() == 0) { + if (CONFIG::write_string (EP_USER_PWD, FPSTR (DEFAULT_USER_PWD) ) ) { + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } else { + ESPCOM::println (ERROR_CMD_MSG, output, espresponse); + response = false; + } + } else { + if (CONFIG::isLocalPasswordValid (parameter.c_str() ) ) { + if (CONFIG::write_string (EP_USER_PWD, parameter.c_str() ) ) { + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } else { + ESPCOM::println (ERROR_CMD_MSG, output, espresponse); + response = false; + } + } else { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } + } + } else { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } + break; + } +#endif + //[ESP700] + case 700: { //read local file + //be sure serial is locked + if ( (web_interface->blockserial) ) { + break; + } + cmd_params.trim() ; + if ( (cmd_params.length() > 0) && (cmd_params[0] != '/') ) { + cmd_params = "/" + cmd_params; + } + FS_FILE currentfile = SPIFFS.open (cmd_params, SPIFFS_FILE_READ); + if (currentfile) {//if file open success + //flush to be sure send buffer is empty + ESPCOM::flush (DEFAULT_PRINTER_PIPE); + //until no line in file + while (currentfile.available()) { + String currentline = currentfile.readStringUntil('\n'); + currentline.replace("\n",""); + currentline.replace("\r",""); + if (currentline.length() > 0) { + int ESPpos = currentline.indexOf ("[ESP"); + if (ESPpos > -1) { + //is there the second part? + int ESPpos2 = currentline.indexOf ("]", ESPpos); + if (ESPpos2 > -1) { + //Split in command and parameters + String cmd_part1 = currentline.substring (ESPpos + 4, ESPpos2); + String cmd_part2 = ""; + //is there space for parameters? + if (ESPpos2 < currentline.length() ) { + cmd_part2 = currentline.substring (ESPpos2 + 1); + } + //if command is a valid number then execute command + if(cmd_part1.toInt()!=0) { + execute_command(cmd_part1.toInt(),cmd_part2,NO_PIPE, auth_type, espresponse); + } + //if not is not a valid [ESPXXX] command ignore it + } + } else { + //send line to serial + ESPCOM::println (currentline, DEFAULT_PRINTER_PIPE); + CONFIG::wait (1); + //flush to be sure send buffer is empty + ESPCOM::flush (DEFAULT_PRINTER_PIPE); + } + CONFIG::wait (1); + } + } + currentfile.close(); + ESPCOM::println (OK_CMD_MSG, output, espresponse); + } else { + ESPCOM::println (ERROR_CMD_MSG, output, espresponse); + response = false; + } + + break; + } + //Format SPIFFS + //[ESP710]FORMAT pwd= + case 710: + parameter = get_param (cmd_params, "", true); +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } else +#endif + { + if (parameter == "FORMAT") { + ESPCOM::print (F ("Formating"), output, espresponse); + SPIFFS.format(); + ESPCOM::println (F ("...Done"), output, espresponse); + } else { + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } + } + break; + //SPIFFS total size and used size + //[ESP720]
+ case 720: + ESPCOM::print (cmd_params, output, espresponse); +#ifdef ARDUINO_ARCH_ESP8266 + fs::FSInfo info; + SPIFFS.info (info); + ESPCOM::print ("SPIFFS Total:", output, espresponse); + ESPCOM::print (CONFIG::formatBytes (info.totalBytes).c_str(), output, espresponse); + ESPCOM::print (" Used:", output, espresponse); + ESPCOM::println (CONFIG::formatBytes (info.usedBytes).c_str(), output, espresponse); +#else + ESPCOM::print ("SPIFFS Total:", output, espresponse); + ESPCOM::print (CONFIG::formatBytes (SPIFFS.totalBytes() ).c_str(), output, espresponse); + ESPCOM::print (" Used:", output, espresponse); + ESPCOM::println (CONFIG::formatBytes (SPIFFS.usedBytes() ).c_str(), output, espresponse); +#endif + break; + //get fw version firmare target and fw version + //[ESP800]
+ case 800: { + byte sd_dir = 0; + String shost ; + if (!CONFIG::read_string (EP_HOSTNAME, shost, MAX_HOSTNAME_LENGTH) ) { + shost = wifi_config.get_default_hostname(); + } + ESPCOM::print (cmd_params, output, espresponse); + ESPCOM::print (F ("FW version:"), output, espresponse); + ESPCOM::print (FW_VERSION, output, espresponse); + ESPCOM::print (F (" # FW target:"), output, espresponse); + ESPCOM::print (CONFIG::GetFirmwareTargetShortName(), output, espresponse); + ESPCOM::print (F (" # FW HW:"), output, espresponse); + if (CONFIG::is_direct_sd) { + ESPCOM::print (F ("Direct SD"), output, espresponse); + } else { + ESPCOM::print (F ("Serial SD"), output, espresponse); + } + ESPCOM::print (F (" # primary sd:"), output, espresponse); + if (!CONFIG::read_byte (EP_PRIMARY_SD, &sd_dir ) ) { + sd_dir = DEFAULT_PRIMARY_SD; + } + if (sd_dir == SD_DIRECTORY) { + ESPCOM::print (F ("/sd/"), output, espresponse); + } else if (sd_dir == EXT_DIRECTORY) { + ESPCOM::print (F ("/ext/"), output, espresponse); + } else { + ESPCOM::print (F ("none"), output, espresponse); + } + ESPCOM::print (F (" # secondary sd:"), output, espresponse); + if (!CONFIG::read_byte (EP_SECONDARY_SD, &sd_dir ) ) { + sd_dir = DEFAULT_SECONDARY_SD; + } + if (sd_dir == SD_DIRECTORY) { + ESPCOM::print (F ("/sd/"), output, espresponse); + } else if (sd_dir == EXT_DIRECTORY) { + ESPCOM::print (F ("/ext/"), output, espresponse); + } else { + ESPCOM::print (F ("none"), output, espresponse); + } + ESPCOM::print (F (" # authentication:"), output, espresponse); +#ifdef AUTHENTICATION_FEATURE + ESPCOM::print (F ("yes"), output, espresponse); +#else + ESPCOM::print (F ("no"), output, espresponse); +#endif + ESPCOM::print (F (" # webcommunication:"), output, espresponse); +#if defined (ASYNCWEBSERVER) + ESPCOM::print (F ("Async"), output, espresponse); +#else + ESPCOM::print (F ("Sync:"), output, espresponse); + String sp = String(wifi_config.iweb_port+1); + ESPCOM::print (sp.c_str(), output, espresponse); +#endif + + ESPCOM::print (F (" # hostname:"), output, espresponse); + ESPCOM::print (shost, output, espresponse); + if (WiFi.getMode() == WIFI_AP) ESPCOM::print (F("(AP mode)"), output, espresponse); + + ESPCOM::println ("", output, espresponse); + } + break; + //get fw target + //[ESP801]
+ case 801: + ESPCOM::print (cmd_params, output, espresponse); + ESPCOM::println (CONFIG::GetFirmwareTargetShortName(), output, espresponse); + break; + //clear status/error/info list + case 802: + if (CONFIG::check_update_presence( ) ) { + ESPCOM::println ("yes", output, espresponse); + } else { + ESPCOM::println ("no", output, espresponse); + } + break; + case 810: + web_interface->blockserial = false; + break; + + default: + ESPCOM::println (INCORRECT_CMD_MSG, output, espresponse); + response = false; + } + return response; +} + +bool COMMAND::check_command (String buffer, tpipe output, bool handlelockserial, bool executecmd) +{ + String buffer2; + LOG ("Check Command:") + LOG (buffer) + LOG ("\r\n") + bool is_temp = false; + if ( (buffer.indexOf ("T:") > -1 ) || (buffer.indexOf ("B:") > -1 ) ) { + is_temp = true; + } + if ( ( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER) ) { + //save time no need to continue + if ( (buffer.indexOf ("busy:") > -1) || (buffer.startsWith ("wait") ) ) { + return false; + } + if (buffer.startsWith ("ok") ) { + return false; + } + } else if (buffer.startsWith ("ok") && buffer.length() < 4) { + return false; + } + +#ifdef SERIAL_COMMAND_FEATURE + if (executecmd) { + String ESP_Command; + int ESPpos = buffer.indexOf ("[ESP"); + if (ESPpos > -1) { + //is there the second part? + int ESPpos2 = buffer.indexOf ("]", ESPpos); + if (ESPpos2 > -1) { + //Split in command and parameters + String cmd_part1 = buffer.substring (ESPpos + 4, ESPpos2); + String cmd_part2 = ""; + //is there space for parameters? + if (ESPpos2 < buffer.length() ) { + cmd_part2 = buffer.substring (ESPpos2 + 1); + } + //if command is a valid number then execute command + if (cmd_part1.toInt() != 0) { + execute_command (cmd_part1.toInt(), cmd_part2, output); + } + //if not is not a valid [ESPXXX] command + } + } + } +#endif + + return is_temp; +} + +//read a buffer in an array +void COMMAND::read_buffer_serial (uint8_t *b, size_t len) +{ + for (long i = 0; i < len; i++) { + read_buffer_serial (b[i]); + //*b++; + } +} + +#ifdef TCP_IP_DATA_FEATURE +//read buffer as char +void COMMAND::read_buffer_tcp (uint8_t b) +{ + static bool previous_was_char = false; + static bool iscomment = false; +//to ensure it is continuous string, no char separated by binaries + if (!previous_was_char) { + buffer_tcp = ""; + iscomment = false; + } +//is comment ? + if (char (b) == ';') { + iscomment = true; + } +//it is a char so add it to buffer + if (isPrintable (b) ) { + previous_was_char = true; + //add char if not a comment + if (!iscomment) { + buffer_tcp += char (b); + } + } else { + previous_was_char = false; //next call will reset the buffer + } +//this is not printable but end of command check if need to handle it + if (b == 13 || b == 10) { + //reset comment flag + iscomment = false; + //Minimum is something like M10 so 3 char + if (buffer_tcp.length() > 3) { + check_command (buffer_tcp, TCP_PIPE); + } + } +} +#endif +//read buffer as char +void COMMAND::read_buffer_serial (uint8_t b) +{ + static bool previous_was_char = false; + static bool iscomment = false; +//to ensure it is continuous string, no char separated by binaries + if (!previous_was_char) { + buffer_serial = ""; + iscomment = false; + } +//is comment ? + if (char (b) == ';') { + iscomment = true; + } +//it is a char so add it to buffer + if (isPrintable (b) ) { + previous_was_char = true; + if (!iscomment) { + buffer_serial += char (b); + } + } else { + previous_was_char = false; //next call will reset the buffer + } +//this is not printable but end of command check if need to handle it + if (b == 13 || b == 10) { + //reset comment flag + iscomment = false; + //Minimum is something like M10 so 3 char + if (buffer_serial.length() > 3) { + check_command (buffer_serial, DEFAULT_PRINTER_PIPE); + } + } +} diff --git a/esp3d/command.h b/src/command.h similarity index 61% rename from esp3d/command.h rename to src/command.h index 2c055648..ec5ac311 100644 --- a/esp3d/command.h +++ b/src/command.h @@ -21,23 +21,24 @@ #ifndef COMMAND_h #define COMMAND_h #include -#include "bridge.h" +#include "espcom.h" + class COMMAND { public: static String buffer_serial; static String buffer_tcp; - static void read_buffer_serial(uint8_t *b, size_t len); - static void read_buffer_serial(uint8_t b); + static void read_buffer_serial (uint8_t *b, size_t len); + static void read_buffer_serial (uint8_t b); #ifdef TCP_IP_DATA_FEATURE - static void read_buffer_tcp(uint8_t b); + static void read_buffer_tcp (uint8_t b); #endif - static bool check_command(String buffer, tpipe output, bool handlelockserial = true); - 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); + static bool check_command (String buffer, tpipe output, bool handlelockserial = true, bool executecmd = true); + static bool execute_command (int cmd, String cmd_params, tpipe output, level_authenticate_type auth_level = LEVEL_GUEST, ESPResponseStream *espresponse = NULL); + 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 diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 00000000..6eb6f2c0 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,1927 @@ +/* + config.cpp- ESP3D configuration class + + 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 +*/ +#include "config.h" +#include +#ifndef FS_NO_GLOBALS +#define FS_NO_GLOBALS +#endif +#include +#include +#include "wificonf.h" +#ifdef ARDUINO_ARCH_ESP8266 +extern "C" { +#include "user_interface.h" +} +#else +#include "Update.h" +#include "esp_wifi.h" +#endif +#include "espcom.h" +#ifdef TIMESTAMP_FEATURE +#include +#endif + +#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_SERIAL_1 +HardwareSerial Serial (1); +#endif +#ifdef USE_SERIAL_2 +HardwareSerial Serial2 (2); +#endif +#endif + +#ifdef DHT_FEATURE +#include "DHTesp.h" +extern DHTesp dht; +#endif + +uint8_t CONFIG::FirmwareTarget = UNKNOWN_FW; +byte CONFIG::output_flag = DEFAULT_OUTPUT_FLAG; + +#ifdef DHT_FEATURE +byte CONFIG::DHT_type = DEFAULT_DHT_TYPE; +int CONFIG::DHT_interval = DEFAULT_DHT_INTERVAL; +#endif + +//Watchdog feeder +void CONFIG::wdtFeed() +{ +#ifdef ARDUINO_ARCH_ESP8266 + ESP.wdtFeed(); +#else + void esp_task_wdt_feed(); +#endif +} + +void CONFIG::wait (uint32_t milliseconds) +{ +#if defined(ASYNCWEBSERVER) + uint32_t timeout = millis(); + while ( (millis() - timeout) < milliseconds) { + wdtFeed(); + } +#else + delay(milliseconds); +#endif +} + +bool CONFIG::SetFirmwareTarget (uint8_t fw) +{ + if ( fw <= MAX_FW_ID) { + FirmwareTarget = fw; + return true; + } else { + return false; + } +} +uint8_t CONFIG::GetFirmwareTarget() +{ + return FirmwareTarget; +} +const char* CONFIG::GetFirmwareTargetName() +{ + static String response; + if ( CONFIG::FirmwareTarget == REPETIER4DV) { + response = F ("Repetier for Davinci"); + } else if ( CONFIG::FirmwareTarget == REPETIER) { + response = F ("Repetier"); + } else if ( CONFIG::FirmwareTarget == MARLIN) { + response = F ("Marlin"); + } else if ( CONFIG::FirmwareTarget == MARLINKIMBRA) { + response = F ("MarlinKimbra"); + } else if ( CONFIG::FirmwareTarget == SMOOTHIEWARE) { + response = F ("Smoothieware"); + } else if ( CONFIG::FirmwareTarget == GRBL) { + response = F ("Grbl"); + } else { + response = F ("???"); + } + return response.c_str(); +} + +const char* CONFIG::GetFirmwareTargetShortName() +{ + static String response; + if ( CONFIG::FirmwareTarget == REPETIER4DV) { + response = F ("repetier4davinci"); + } else if ( CONFIG::FirmwareTarget == REPETIER) { + response = F ("repetier"); + } else if ( CONFIG::FirmwareTarget == MARLIN) { + response = F ("marlin"); + } else if ( CONFIG::FirmwareTarget == MARLINKIMBRA) { + response = F ("marlinkimbra"); + } else if ( CONFIG::FirmwareTarget == SMOOTHIEWARE) { + response = F ("smoothieware"); + } else if ( CONFIG::FirmwareTarget == GRBL) { + response = F ("grbl"); + } else { + response = F ("???"); + } + return response.c_str(); +} + +void CONFIG::InitFirmwareTarget() +{ + uint8_t b = UNKNOWN_FW; + if (!CONFIG::read_byte (EP_TARGET_FW, &b ) ) { + b = UNKNOWN_FW; + } + if (!SetFirmwareTarget (b) ) { + SetFirmwareTarget (UNKNOWN_FW) ; + } +} +void CONFIG::InitOutput(){ + byte bflag = 0; + if (!CONFIG::read_byte (EP_OUTPUT_FLAG, &bflag ) ) { + bflag = 0; + } + CONFIG::output_flag = bflag; +} + +bool CONFIG::is_locked(byte flag){ + return ((CONFIG::output_flag & flag) == flag); +} + +void CONFIG::InitDirectSD() +{ + + CONFIG::is_direct_sd = false; + +} + + +bool CONFIG::InitBaudrate(long value) +{ + long baud_rate = 0; + if (value > 0)baud_rate = value; + else { + if ( !CONFIG::read_buffer (EP_BAUD_RATE, (byte *) &baud_rate, INTEGER_LENGTH) ) { + return false; + } + } + if ( ! (baud_rate == 9600 || baud_rate == 19200 || baud_rate == 38400 || baud_rate == 57600 || baud_rate == 115200 || baud_rate == 230400 || baud_rate == 250000) ) { + return false; + } + + //setup serial + //TODO define baudrate for each Serial +#ifdef USE_SERIAL_0 + if (Serial.baudRate() != baud_rate) { + Serial.begin (baud_rate); + } +#endif +#ifdef USE_SERIAL_1 + if (Serial1.baudRate() != baud_rate) { + Serial1.begin (baud_rate); + } +#endif +#ifdef USE_SERIAL_2 + if (Serial2.baudRate() != baud_rate) { + Serial2.begin (baud_rate); + } +#endif + +//only Serial for ESP8266 +#ifdef ARDUINO_ARCH_ESP8266 + Serial.setRxBufferSize (SERIAL_RX_BUFFER_SIZE); +#endif + + wifi_config.baud_rate = baud_rate; + delay (1000); + return true; +} + +bool CONFIG::InitExternalPorts() +{ + if (!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) ) { + return false; + } + if (wifi_config.iweb_port < DEFAULT_MIN_WEB_PORT || wifi_config.iweb_port > DEFAULT_MAX_WEB_PORT || wifi_config.idata_port < DEFAULT_MIN_DATA_PORT || wifi_config.idata_port > DEFAULT_MAX_DATA_PORT) { + return false; + } + return true; +} + +//warning if using from async function with async param +//restart will work but reason will be wrong +//better to use "web_interface->restartmodule = true;" instead +void CONFIG::esp_restart (bool async) +{ + LOG ("Restarting\r\n") + ESPCOM::flush (DEFAULT_PRINTER_PIPE); + if (!async) { + delay (100); + } +#ifdef ARDUINO_ARCH_ESP8266 + //ESP8266 has only serial + Serial.swap(); +#endif + ESP.restart(); + while (1) { + if (!async) { + delay (1); + } + }; +} +#ifdef DHT_FEATURE +void CONFIG::InitDHT(bool refresh) { + if (!refresh) { + byte bflag = DEFAULT_DHT_TYPE; + int ibuf = DEFAULT_DHT_INTERVAL; + if (!CONFIG::read_byte (EP_DHT_TYPE, &bflag ) ) { + bflag = DEFAULT_DHT_TYPE; + } + CONFIG::DHT_type = bflag; + if (!CONFIG::read_buffer (EP_DHT_INTERVAL, (byte *) &ibuf, INTEGER_LENGTH) ) { + ibuf = DEFAULT_DHT_INTERVAL; + } + CONFIG::DHT_interval = ibuf; + } + if (CONFIG::DHT_type != 255) dht.setup(ESP_DHT_PIN,(DHTesp::DHT_MODEL_t)CONFIG::DHT_type); // Connect DHT sensor to GPIO ESP_DHT_PIN +} +#endif +void CONFIG::InitPins() +{ +#ifdef RECOVERY_FEATURE + pinMode (RESET_CONFIG_PIN, INPUT); +#endif + +#ifdef DHT_FEATURE + CONFIG::InitDHT(); +#endif +} + +#if defined(TIMESTAMP_FEATURE) +void CONFIG::init_time_client() +{ + String s1, s2, s3; + int8_t t1; + byte d1; + if (!CONFIG::read_string (EP_TIME_SERVER1, s1, MAX_DATA_LENGTH) ) { + s1 = FPSTR (DEFAULT_TIME_SERVER1); + } + if (!CONFIG::read_string (EP_TIME_SERVER2, s2, MAX_DATA_LENGTH) ) { + s2 = FPSTR (DEFAULT_TIME_SERVER2); + } + if (!CONFIG::read_string (EP_TIME_SERVER3, s3, MAX_DATA_LENGTH) ) { + s3 = FPSTR (DEFAULT_TIME_SERVER3); + } + if (!CONFIG::read_byte (EP_TIMEZONE, (byte *) &t1 ) ) { + t1 = DEFAULT_TIME_ZONE; + } + if (!CONFIG::read_byte (EP_TIME_ISDST, &d1 ) ) { + d1 = DEFAULT_TIME_DST; + } + configTime (3600 * (t1), d1 * 3600, s1.c_str(), s2.c_str(), s3.c_str() ); + time_t now = time(nullptr); + if (WiFi.getMode() == WIFI_STA) { + int nb = 0; + while ((now < 8 * 3600 * 2) && (nb < 6)) { + wait(500); + nb++; + now = time(nullptr); + } + } +} +#endif + +bool CONFIG::is_direct_sd = false; + + +bool CONFIG::isHostnameValid (const char * hostname) +{ + //limited size + char c; + if (strlen (hostname) > MAX_HOSTNAME_LENGTH || strlen (hostname) < MIN_HOSTNAME_LENGTH) { + return false; + } + //only letter and digit + for (int i = 0; i < strlen (hostname); i++) { + c = hostname[i]; + if (! (isdigit (c) || isalpha (c) || c == '_') ) { + return false; + } + if (c == ' ') { + return false; + } + } + return true; +} + +bool CONFIG::isSSIDValid (const char * ssid) +{ + //limited size + //char c; + if (strlen (ssid) > MAX_SSID_LENGTH || strlen (ssid) < MIN_SSID_LENGTH) { + return false; + } + //only printable + for (int i = 0; i < strlen (ssid); i++) { + if (!isPrintable (ssid[i]) ) { + return false; + } + } + return true; +} + +bool CONFIG::isPasswordValid (const char * password) +{ + //limited size + if (strlen (password) > MAX_PASSWORD_LENGTH) { + return false; + } +#if MIN_PASSWORD_LENGTH > 0 + if (strlen (password) < MIN_PASSWORD_LENGTH) { + ) return false; + } +#endif + //no space allowed + for (int i = 0; i < strlen (password); i++) + if (password[i] == ' ') { + return false; + } + return true; +} + +bool CONFIG::isLocalPasswordValid (const char * password) +{ + char c; + //limited size + if ( (strlen (password) > MAX_LOCAL_PASSWORD_LENGTH) || (strlen (password) < MIN_LOCAL_PASSWORD_LENGTH) ) { + return false; + } + //no space allowed + for (int i = 0; i < strlen (password); i++) { + c = password[i]; + if (c == ' ') { + return false; + } + } + return true; +} + +bool CONFIG::isIPValid (const char * IP) +{ + //limited size + int internalcount = 0; + int dotcount = 0; + bool previouswasdot = false; + char c; + + if (strlen (IP) > 15 || strlen (IP) == 0) { + return false; + } + //cannot start with . + if (IP[0] == '.') { + return false; + } + //only letter and digit + for (int i = 0; i < strlen (IP); i++) { + c = IP[i]; + if (isdigit (c) ) { + //only 3 digit at once + internalcount++; + previouswasdot = false; + if (internalcount > 3) { + return false; + } + } else if (c == '.') { + //cannot have 2 dots side by side + if (previouswasdot) { + return false; + } + previouswasdot = true; + internalcount = 0; + dotcount++; + }//if not a dot neither a digit it is wrong + else { + return false; + } + } + //if not 3 dots then it is wrong + if (dotcount != 3) { + return false; + } + //cannot have the last dot as last char + if (IP[strlen (IP) - 1] == '.') { + return false; + } + return true; +} + +char * CONFIG::intTostr (int value) +{ + static char result [12]; + sprintf (result, "%d", value); + return result; +} + +String CONFIG::formatBytes (uint32_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"; + } +} + +//helper to convert string to IP +//do not use IPAddress.fromString() because lack of check point and error result +//return number of parts +byte CONFIG::split_ip (const char * ptr, byte * part) +{ + if (strlen (ptr) > 15 || strlen (ptr) < 7) { + part[0] = 0; + part[1] = 0; + part[2] = 0; + part[3] = 0; + return 0; + } + + char pstart [16]; + char * ptr2; + strcpy (pstart, ptr); + ptr2 = pstart; + byte i = strlen (pstart); + byte pos = 0; + for (byte j = 0; j < i; j++) { + if (pstart[j] == '.') { + if (pos == 4) { + part[0] = 0; + part[1] = 0; + part[2] = 0; + part[3] = 0; + return 0; + } + pstart[j] = 0x0; + part[pos] = atoi (ptr2); + pos++; + ptr2 = &pstart[j + 1]; + } + } + part[pos] = atoi (ptr2); + return pos + 1; +} + +//just simple helper to convert mac address to string +char * CONFIG::mac2str (uint8_t mac [WL_MAC_ADDR_LENGTH]) +{ + static char macstr [18]; + if (0 > sprintf (macstr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]) ) { + strcpy (macstr, "00:00:00:00:00:00"); + } + return macstr; +} + + +//read a string +//a string is multibyte + \0, this is won't work if 1 char is multibyte like chinese char +bool CONFIG::read_string (int pos, char byte_buffer[], int size_max) +{ + //check if parameters are acceptable + if (size_max == 0 || pos + size_max + 1 > EEPROM_SIZE || byte_buffer == NULL) { + LOG ("Error read string\r\n") + return false; + } + EEPROM.begin (EEPROM_SIZE); + byte b = 13; // non zero for the while loop below + int i = 0; + + //read until max size is reached or \0 is found + while (i < size_max && b != 0) { + b = EEPROM.read (pos + i); + byte_buffer[i] = b; + i++; + } + + // Be sure there is a 0 at the end. + if (b != 0) { + byte_buffer[i - 1] = 0x00; + } + EEPROM.end(); + + return true; +} + +bool CONFIG::read_string (int pos, String & sbuffer, int size_max) +{ + //check if parameters are acceptable + if (size_max == 0 || pos + size_max + 1 > EEPROM_SIZE ) { + LOG ("Error read string\r\n") + return false; + } + byte b = 13; // non zero for the while loop below + int i = 0; + sbuffer = ""; + + EEPROM.begin (EEPROM_SIZE); + //read until max size is reached or \0 is found + while (i < size_max && b != 0) { + b = EEPROM.read (pos + i); + if (b != 0) { + sbuffer += char (b); + } + i++; + } + EEPROM.end(); + + return true; +} + +//read a buffer of size_buffer +bool CONFIG::read_buffer (int pos, byte byte_buffer[], int size_buffer) +{ + //check if parameters are acceptable + if (size_buffer == 0 || pos + size_buffer > EEPROM_SIZE || byte_buffer == NULL) { + LOG ("Error read buffer\r\n") + return false; + } + int i = 0; + EEPROM.begin (EEPROM_SIZE); + //read until max size is reached + while (i < size_buffer ) { + byte_buffer[i] = EEPROM.read (pos + i); + i++; + } + EEPROM.end(); + return true; +} + +//read a flag / byte +bool CONFIG::read_byte (int pos, byte * value) +{ + //check if parameters are acceptable + if (pos + 1 > EEPROM_SIZE) { + LOG ("Error read byte\r\n") + return false; + } + EEPROM.begin (EEPROM_SIZE); + value[0] = EEPROM.read (pos); + EEPROM.end(); + return true; +} + +bool CONFIG::write_string (int pos, const __FlashStringHelper *str) +{ + String stmp = str; + return write_string (pos, stmp.c_str() ); +} + +bool CONFIG::check_update_presence( ) +{ + bool result = false; + if (CONFIG::is_direct_sd) { + if (!CONFIG::InitBaudrate()) return false; + CONFIG::InitFirmwareTarget(); + delay (500); + String cmd = "M20"; + //By default M20 should be applied + //if (CONFIG::FirmwareTarget == UNKNOWN_FW) return false; + if (CONFIG::FirmwareTarget == SMOOTHIEWARE) { + byte sd_dir = 0; + if (!CONFIG::read_byte (EP_PRIMARY_SD, &sd_dir ) ) { + sd_dir = DEFAULT_PRIMARY_SD; + } + if (sd_dir == SD_DIRECTORY) { + cmd = "ls /sd"; + } else if (sd_dir == EXT_DIRECTORY) { + cmd = "ls /ext"; + } else { + return false; + } + } + String tmp; + + int count ; + //send command to serial as no need to transfer ESP command + //to avoid any pollution if Uploading file to SDCard + //block every query + //empty the serial buffer and incoming data + if (ESPCOM::processFromSerial() ) { + delay (1); + } + //Send command + ESPCOM::println (cmd, DEFAULT_PRINTER_PIPE); + count = 0; + String current_buffer; + String current_line; + //int pos; + int temp_counter = 0; + + //pickup the list + while (count < MAX_TRY) { + //give some time between each buffer + if (ESPCOM::available(DEFAULT_PRINTER_PIPE)) { + count = 0; + size_t len = ESPCOM::available(DEFAULT_PRINTER_PIPE); + uint8_t sbuf[len + 1]; + //read buffer + ESPCOM::readBytes (DEFAULT_PRINTER_PIPE, sbuf, len); + //change buffer as string + sbuf[len] = '\0'; + //add buffer to current one if any + current_buffer += (char * ) sbuf; + while (current_buffer.indexOf ("\n") != -1) { + //remove the possible "\r" + current_buffer.replace ("\r", ""); + //pos = current_buffer.indexOf("\n"); + //get line + current_line = current_buffer.substring (0, current_buffer.indexOf ("\n") ); + //if line is command ack - just exit so save the time out period + if ( (current_line == "ok") || (current_line == "wait") ) { + count = MAX_TRY; + break; + } + //check line + //save time no need to continue + if (current_line.indexOf ("busy:") > -1 || current_line.indexOf ("T:") > -1 || current_line.indexOf ("B:") > -1) { + temp_counter++; + } else { + + } + if (temp_counter > 5) { + break; + } + //current remove line from buffer + tmp = current_buffer.substring (current_buffer.indexOf ("\n") + 1, current_buffer.length() ); + current_buffer = tmp; + delay (0); + } + delay (0); + } else { + delay (1); + } + //it is sending too many temp status should be heating so let's exit the loop + if (temp_counter > 5) { + count = MAX_TRY; + } + count++; + } + if (ESPCOM::processFromSerial() ) { + delay (1); + } + } + return result; +} + + +//write a string (array of byte with a 0x00 at the end) +bool CONFIG::write_string (int pos, const char * byte_buffer) +{ + int size_buffer; + int maxsize = EEPROM_SIZE; + size_buffer = strlen (byte_buffer); + //check if parameters are acceptable + switch (pos) { + case EP_ADMIN_PWD: + case EP_USER_PWD: + maxsize = MAX_LOCAL_PASSWORD_LENGTH; + break; + case EP_AP_SSID: + case EP_STA_SSID: + maxsize = MAX_SSID_LENGTH; + break; + case EP_AP_PASSWORD: + case EP_STA_PASSWORD: + maxsize = MAX_PASSWORD_LENGTH; + break; + case EP_HOSTNAME: + maxsize = MAX_HOSTNAME_LENGTH; + break; + case EP_TIME_SERVER1: + case EP_TIME_SERVER2: + case EP_TIME_SERVER3: + maxsize = MAX_DATA_LENGTH; + break; + default: + maxsize = EEPROM_SIZE; + break; + } + if ( (size_buffer == 0 ) || pos + size_buffer + 1 > EEPROM_SIZE || size_buffer > maxsize || byte_buffer == NULL) { + LOG ("Error write string\r\n") + return false; + } + //copy the value(s) + EEPROM.begin (EEPROM_SIZE); + for (int i = 0; i < size_buffer; i++) { + EEPROM.write (pos + i, byte_buffer[i]); + } + + //0 terminal + EEPROM.write (pos + size_buffer, 0x00); + EEPROM.commit(); + EEPROM.end(); + return true; +} + +//write a buffer +bool CONFIG::write_buffer (int pos, const byte * byte_buffer, int size_buffer) +{ + //check if parameters are acceptable + if (size_buffer == 0 || pos + size_buffer > EEPROM_SIZE || byte_buffer == NULL) { + LOG ("Error write buffer\r\n") + return false; + } + EEPROM.begin (EEPROM_SIZE); + //copy the value(s) + for (int i = 0; i < size_buffer; i++) { + EEPROM.write (pos + i, byte_buffer[i]); + } + EEPROM.commit(); + EEPROM.end(); + return true; +} + +//read a flag / byte +bool CONFIG::write_byte (int pos, const byte value) +{ + //check if parameters are acceptable + if (pos + 1 > EEPROM_SIZE) { + LOG ("Error write byte\r\n") + return false; + } + EEPROM.begin (EEPROM_SIZE); + EEPROM.write (pos, value); + EEPROM.commit(); + EEPROM.end(); + return true; +} + +bool CONFIG::reset_config() +{ + if (!CONFIG::write_byte (EP_WIFI_MODE, DEFAULT_WIFI_MODE) ) { + return false; + } + if (!CONFIG::write_buffer (EP_BAUD_RATE, (const byte *) &DEFAULT_BAUD_RATE, INTEGER_LENGTH) ) { + return false; + } + if (!CONFIG::write_string (EP_AP_SSID, FPSTR (DEFAULT_AP_SSID) ) ) { + return false; + } + if (!CONFIG::write_string (EP_AP_PASSWORD, FPSTR (DEFAULT_AP_PASSWORD) ) ) { + return false; + } + if (!CONFIG::write_string (EP_STA_SSID, FPSTR (DEFAULT_STA_SSID) ) ) { + return false; + } + if (!CONFIG::write_string (EP_STA_PASSWORD, FPSTR (DEFAULT_STA_PASSWORD) ) ) { + return false; + } + if (!CONFIG::write_byte (EP_AP_IP_MODE, DEFAULT_AP_IP_MODE) ) { + return false; + } + if (!CONFIG::write_byte (EP_STA_IP_MODE, DEFAULT_STA_IP_MODE) ) { + return false; + } + if (!CONFIG::write_buffer (EP_STA_IP_VALUE, DEFAULT_IP_VALUE, IP_LENGTH) ) { + return false; + } + if (!CONFIG::write_buffer (EP_STA_MASK_VALUE, DEFAULT_MASK_VALUE, IP_LENGTH) ) { + return false; + } + if (!CONFIG::write_buffer (EP_STA_GATEWAY_VALUE, DEFAULT_GATEWAY_VALUE, IP_LENGTH) ) { + return false; + } + if (!CONFIG::write_byte (EP_STA_PHY_MODE, DEFAULT_PHY_MODE) ) { + return false; + } + if (!CONFIG::write_buffer (EP_AP_IP_VALUE, DEFAULT_IP_VALUE, IP_LENGTH) ) { + return false; + } + if (!CONFIG::write_buffer (EP_AP_MASK_VALUE, DEFAULT_MASK_VALUE, IP_LENGTH) ) { + return false; + } + if (!CONFIG::write_buffer (EP_AP_GATEWAY_VALUE, DEFAULT_GATEWAY_VALUE, IP_LENGTH) ) { + return false; + } + if (!CONFIG::write_byte (EP_AP_PHY_MODE, DEFAULT_PHY_MODE) ) { + return false; + } + if (!CONFIG::write_byte (EP_SLEEP_MODE, DEFAULT_SLEEP_MODE) ) { + return false; + } + if (!CONFIG::write_byte (EP_CHANNEL, DEFAULT_CHANNEL) ) { + return false; + } + if (!CONFIG::write_byte (EP_AUTH_TYPE, DEFAULT_AUTH_TYPE) ) { + return false; + } + if (!CONFIG::write_byte (EP_SSID_VISIBLE, DEFAULT_SSID_VISIBLE) ) { + return false; + } + if (!CONFIG::write_buffer (EP_WEB_PORT, (const byte *) &DEFAULT_WEB_PORT, INTEGER_LENGTH) ) { + return false; + } + if (!CONFIG::write_buffer (EP_DATA_PORT, (const byte *) &DEFAULT_DATA_PORT, INTEGER_LENGTH) ) { + return false; + } + + if (!CONFIG::write_string (EP_HOSTNAME, wifi_config.get_default_hostname() ) ) { + return false; + } + + if (!CONFIG::write_string (EP_ADMIN_PWD, FPSTR (DEFAULT_ADMIN_PWD) ) ) { + return false; + } + if (!CONFIG::write_string (EP_USER_PWD, FPSTR (DEFAULT_USER_PWD) ) ) { + return false; + } + + if (!CONFIG::write_byte (EP_TARGET_FW, UNKNOWN_FW) ) { + return false; + } +#if defined(TIMESTAMP_FEATURE) + if (!CONFIG::write_byte (EP_TIMEZONE, DEFAULT_TIME_ZONE) ) { + return false; + } + + if (!CONFIG::write_byte (EP_TIME_ISDST, DEFAULT_TIME_DST) ) { + return false; + } + + if (!CONFIG::write_string (EP_TIME_SERVER1, FPSTR (DEFAULT_TIME_SERVER1) ) ) { + return false; + } + + if (!CONFIG::write_string (EP_TIME_SERVER2, FPSTR (DEFAULT_TIME_SERVER2) ) ) { + return false; + } + + if (!CONFIG::write_string (EP_TIME_SERVER3, FPSTR (DEFAULT_TIME_SERVER3) ) ) { + return false; + } +#endif + + if (!CONFIG::write_byte (EP_OUTPUT_FLAG, DEFAULT_OUTPUT_FLAG) ) { + return false; + } +#ifdef DHT_FEATURE + if (!CONFIG::write_buffer (EP_DHT_INTERVAL, (const byte *) &DEFAULT_DHT_INTERVAL, INTEGER_LENGTH) ) { + return false; + } + + if (!CONFIG::write_byte (EP_DHT_TYPE, DEFAULT_DHT_TYPE) ) { + return false; + } +#endif + return true; +} + +void CONFIG::print_config (tpipe output, bool plaintext, ESPResponseStream *espresponse) +{ + if (!plaintext) { + ESPCOM::print (F ("{\"chip_id\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Chip ID: "), output, espresponse); + } +#ifdef ARDUINO_ARCH_ESP8266 + ESPCOM::print (String (ESP.getChipId() ).c_str(), output, espresponse); +#else + ESPCOM::print (String ( (uint16_t) (ESP.getEfuseMac() >> 32) ).c_str(), output, espresponse); +#endif + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"cpu\":\""), output, espresponse); + } else { + ESPCOM::print (F ("CPU Frequency: "), output, espresponse); + } + ESPCOM::print (String (ESP.getCpuFreqMHz() ).c_str(), output, espresponse); + if (plaintext) { + ESPCOM::print (F ("Mhz"), output, espresponse); + } + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } +#ifdef ARDUINO_ARCH_ESP32 + if (!plaintext) { + ESPCOM::print (F ("\"cpu_temp\":\""), output, espresponse); + } else { + ESPCOM::print (F ("CPU Temperature: "), output, espresponse); + } + ESPCOM::print (String (temperatureRead(), 1).c_str(), output, espresponse); + if (plaintext) { + ESPCOM::print (F ("C"), output, espresponse); + } + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } +#endif + if (!plaintext) { + ESPCOM::print (F ("\"freemem\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Free memory: "), output, espresponse); + } + ESPCOM::print (formatBytes (ESP.getFreeHeap() ).c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\""), output, espresponse); + } + ESPCOM::print (F ("SDK"), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\":\""), output, espresponse); + } else { + ESPCOM::print (F (": "), output, espresponse); + } + ESPCOM::print (ESP.getSdkVersion(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"flash_size\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Flash Size: "), output, espresponse); + } + ESPCOM::print (formatBytes (ESP.getFlashChipSize() ).c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } +#ifdef ARDUINO_ARCH_ESP8266 + if (!plaintext) { + ESPCOM::print (F ("\"update_size\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Available Size for update: "), output, espresponse); + } + uint32_t flashsize = ESP.getFlashChipSize(); + fs::FSInfo info; + SPIFFS.info (info); + //if higher than 1MB take out SPIFFS + if (flashsize > 1024 * 1024) { + flashsize = (1024 * 1024)-ESP.getSketchSize()-1024; + } + else { + flashsize = flashsize - ESP.getSketchSize()-info.totalBytes-1024; + } + ESPCOM::print(formatBytes(flashsize).c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + if (flashsize > ( ESP.getSketchSize())) { + ESPCOM::println(F("(Ok)"), output, espresponse); + } else { + ESPCOM::println(F ("(Not enough)"), output, espresponse); + } + } + + if (!plaintext) { + ESPCOM::print (F ("\"spiffs_size\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Available Size for SPIFFS: "), output, espresponse); + } + ESPCOM::print (formatBytes (info.totalBytes).c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } +#else + if (!plaintext) { + ESPCOM::print (F ("\"update_size\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Available Size for update: "), output, espresponse); + } + uint32_t flashsize = ESP.getFlashChipSize(); + //Not OTA on 2Mb board per spec + if (flashsize > 0x20000) { + flashsize = 0x140000; + } else { + flashsize = 0x0; + } + ESPCOM::print (formatBytes (flashsize).c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + if (flashsize > 0x0) { + ESPCOM::println (F ("(Ok)"), output, espresponse); + } else { + ESPCOM::print (F ("(Not enough)"), output, espresponse); + } + } + if (!plaintext) { + ESPCOM::print (F ("\"spiffs_size\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Available Size for SPIFFS: "), output, espresponse); + } + ESPCOM::print (formatBytes (SPIFFS.totalBytes() ).c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } +#endif + if (!plaintext) { + ESPCOM::print (F ("\"baud_rate\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Baud rate: "), output, espresponse); + } + uint32_t br = ESPCOM::baudRate(DEFAULT_PRINTER_PIPE); + ESPCOM::print (String (br).c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"sleep_mode\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Sleep mode: "), output, espresponse); + } +#ifdef ARDUINO_ARCH_ESP32 + wifi_ps_type_t ps_type; + esp_wifi_get_ps (&ps_type); +#else + WiFiSleepType_t ps_type; + ps_type = WiFi.getSleepMode(); +#endif + if (ps_type == WIFI_NONE_SLEEP) { + ESPCOM::print (F ("None"), output, espresponse); + } else if (ps_type == WIFI_LIGHT_SLEEP) { + ESPCOM::print (F ("Light"), output, espresponse); + } else if (ps_type == WIFI_MODEM_SLEEP) { + ESPCOM::print (F ("Modem"), output, espresponse); + } else { + ESPCOM::print (F ("???"), output, espresponse); + } + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"channel\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Channel: "), output, espresponse); + } + ESPCOM::print (String (WiFi.channel() ).c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } +#ifdef ARDUINO_ARCH_ESP32 + uint8_t PhyMode; + if (WiFi.getMode() == WIFI_STA) { + esp_wifi_get_protocol (ESP_IF_WIFI_STA, &PhyMode); + } else { + esp_wifi_get_protocol (ESP_IF_WIFI_AP, &PhyMode); + } +#else + WiFiPhyMode_t PhyMode = WiFi.getPhyMode(); +#endif + if (!plaintext) { + ESPCOM::print (F ("\"phy_mode\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Phy Mode: "), output, espresponse); + } + if (PhyMode == (WIFI_PHY_MODE_11G) ) { + ESPCOM::print (F ("11g"), output, espresponse); + } else if (PhyMode == (WIFI_PHY_MODE_11B) ) { + ESPCOM::print (F ("11b"), output, espresponse); + } else if (PhyMode == (WIFI_PHY_MODE_11N) ) { + ESPCOM::print (F ("11n"), output, espresponse); + } else { + ESPCOM::print (F ("???"), output, espresponse); + } + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"web_port\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Web port: "), output, espresponse); + } + ESPCOM::print (String (wifi_config.iweb_port).c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"data_port\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Data port: "), output, espresponse); + } +#ifdef TCP_IP_DATA_FEATURE + ESPCOM::print (String (wifi_config.idata_port).c_str(), output, espresponse); +#else + ESPCOM::print (F ("Disabled"), output, espresponse); +#endif + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (WiFi.getMode() == WIFI_STA || WiFi.getMode() == WIFI_AP_STA) { + if (!plaintext) { + ESPCOM::print (F ("\"hostname\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Hostname: "), output, espresponse); + } +#ifdef ARDUINO_ARCH_ESP32 + ESPCOM::print (WiFi.getHostname(), output, espresponse); +#else + ESPCOM::print (WiFi.hostname().c_str(), output, espresponse); +#endif + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + } + + if (!plaintext) { + ESPCOM::print (F ("\"active_mode\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Active Mode: "), output, espresponse); + } + if (WiFi.getMode() == WIFI_STA) { + ESPCOM::print (F ("STA ("), output, espresponse); + ESPCOM::print (WiFi.macAddress().c_str(), output, espresponse); + ESPCOM::print (F (")"), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + if (WiFi.isConnected() ) { + if (!plaintext) { + ESPCOM::print (F ("\"connected_ssid\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Connected to: "), output, espresponse); + } + ESPCOM::print (WiFi.SSID().c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + if (!plaintext) { + ESPCOM::print (F ("\"connected_signal\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Signal: "), output, espresponse); + } + ESPCOM::print (String (wifi_config.getSignal (WiFi.RSSI() ) ).c_str(), output, espresponse); + ESPCOM::print (F ("%"), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + } else { + if (!plaintext) { + ESPCOM::print (F ("\"connection_status\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Connection Status: "), output, espresponse); + } + ESPCOM::print (F ("Connection Status: "), output, espresponse); + if (WiFi.status() == WL_DISCONNECTED) { + ESPCOM::print (F ("Disconnected"), output, espresponse); + } else if (WiFi.status() == WL_CONNECTION_LOST) { + ESPCOM::print (F ("Connection lost"), output, espresponse); + } else if (WiFi.status() == WL_CONNECT_FAILED) { + ESPCOM::print (F ("Connection failed"), output, espresponse); + } else if (WiFi.status() == WL_NO_SSID_AVAIL) { + ESPCOM::print (F ("No connection"), output, espresponse); + } else if (WiFi.status() == WL_IDLE_STATUS ) { + ESPCOM::print (F ("Idle"), output, espresponse); + } else { + ESPCOM::print (F ("Unknown"), output, espresponse); + } + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + } + if (!plaintext) { + ESPCOM::print (F ("\"ip_mode\":\""), output, espresponse); + } else { + ESPCOM::print (F ("IP Mode: "), output, espresponse); + } +#ifdef ARDUINO_ARCH_ESP32 + tcpip_adapter_dhcp_status_t dhcp_status; + tcpip_adapter_dhcpc_get_status (TCPIP_ADAPTER_IF_STA, &dhcp_status); + if (dhcp_status == TCPIP_ADAPTER_DHCP_STARTED) +#else + if (wifi_station_dhcpc_status() == DHCP_STARTED) +#endif + { + ESPCOM::print (F ("DHCP"), output, espresponse); + } else { + ESPCOM::print (F ("Static"), output, espresponse); + } + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"ip\":\""), output, espresponse); + } else { + ESPCOM::print (F ("IP: "), output, espresponse); + } + ESPCOM::print (WiFi.localIP().toString().c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"gw\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Gateway: "), output, espresponse); + } + ESPCOM::print (WiFi.gatewayIP().toString().c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"msk\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Mask: "), output, espresponse); + } + ESPCOM::print (WiFi.subnetMask().toString().c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"dns\":\""), output, espresponse); + } else { + ESPCOM::print (F ("DNS: "), output, espresponse); + } + ESPCOM::print (WiFi.dnsIP().toString().c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"disabled_mode\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Disabled Mode: "), output, espresponse); + } + ESPCOM::print (F ("AP ("), output, espresponse); + ESPCOM::print (WiFi.softAPmacAddress().c_str(), output, espresponse); + ESPCOM::print (F (")"), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + } else if (WiFi.getMode() == WIFI_AP) { + ESPCOM::print (F ("AP ("), output, espresponse); + ESPCOM::print (WiFi.softAPmacAddress().c_str(), output, espresponse); + ESPCOM::print (F (")"), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + //get current config +#ifdef ARDUINO_ARCH_ESP32 + wifi_ap_config_t apconfig; + wifi_config_t conf; + esp_wifi_get_config (ESP_IF_WIFI_AP, &conf); + apconfig.ssid_hidden = conf.ap.ssid_hidden; + apconfig.authmode = conf.ap.authmode; + apconfig.max_connection = conf.ap.max_connection; +#else + struct softap_config apconfig; + wifi_softap_get_config (&apconfig); +#endif + if (!plaintext) { + ESPCOM::print (F ("\"ap_ssid\":\""), output, espresponse); + } else { + ESPCOM::print (F ("SSID: "), output, espresponse); + } +#ifdef ARDUINO_ARCH_ESP32 + ESPCOM::print ( (const char*) conf.ap.ssid, output, espresponse); +#else + ESPCOM::print ( (const char*) apconfig.ssid, output, espresponse); +#endif + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"ssid_visible\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Visible: "), output, espresponse); + } + ESPCOM::print ( (apconfig.ssid_hidden == 0) ? F ("Yes") : F ("No"), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"ssid_authentication\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Authentication: "), output, espresponse); + } + if (apconfig.authmode == AUTH_OPEN) { + ESPCOM::print (F ("None"), output, espresponse); + } else if (apconfig.authmode == AUTH_WEP) { + ESPCOM::print (F ("WEP"), output, espresponse); + } else if (apconfig.authmode == AUTH_WPA_PSK) { + ESPCOM::print (F ("WPA"), output, espresponse); + } else if (apconfig.authmode == AUTH_WPA2_PSK) { + ESPCOM::print (F ("WPA2"), output, espresponse); + } else { + ESPCOM::print (F ("WPA/WPA2"), output, espresponse); + } + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"ssid_max_connections\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Max Connections: "), output, espresponse); + } + ESPCOM::print (String (apconfig.max_connection).c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"ssid_dhcp\":\""), output, espresponse); + } else { + ESPCOM::print (F ("DHCP Server: "), output, espresponse); + } +#ifdef ARDUINO_ARCH_ESP32 + tcpip_adapter_dhcp_status_t dhcp_status; + tcpip_adapter_dhcps_get_status (TCPIP_ADAPTER_IF_AP, &dhcp_status); + if (dhcp_status == TCPIP_ADAPTER_DHCP_STARTED) +#else + if (wifi_softap_dhcps_status() == DHCP_STARTED) +#endif + { + ESPCOM::print (F ("Started"), output, espresponse); + } else { + ESPCOM::print (F ("Stopped"), output, espresponse); + } + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"ip\":\""), output, espresponse); + } else { + ESPCOM::print (F ("IP: "), output, espresponse); + } + ESPCOM::print (WiFi.softAPIP().toString().c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } +#ifdef ARDUINO_ARCH_ESP32 + tcpip_adapter_ip_info_t ip; + tcpip_adapter_get_ip_info (TCPIP_ADAPTER_IF_AP, &ip); +#else + struct ip_info ip; + wifi_get_ip_info (SOFTAP_IF, &ip); +#endif + if (!plaintext) { + ESPCOM::print (F ("\"gw\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Gateway: "), output, espresponse); + } + ESPCOM::print (IPAddress (ip.gw.addr).toString().c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) { + ESPCOM::print (F ("\"msk\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Mask: "), output, espresponse); + } + ESPCOM::print (IPAddress (ip.netmask.addr).toString().c_str(), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + + if (!plaintext) { + ESPCOM::print (F ("\"connected_clients\":["), output, espresponse); + } else { + ESPCOM::print (F ("Connected clients: "), output, espresponse); + } + int client_counter = 0; +#ifdef ARDUINO_ARCH_ESP32 + wifi_sta_list_t station; + tcpip_adapter_sta_list_t tcpip_sta_list; + esp_wifi_ap_get_sta_list (&station); + tcpip_adapter_get_sta_list (&station, &tcpip_sta_list); +#else + struct station_info * station; + station = wifi_softap_get_station_info(); +#endif + String stmp = ""; +#ifdef ARDUINO_ARCH_ESP32 + for (int i = 0; i < station.num; i++) { +#else + while (station) { +#endif + if (stmp.length() > 0) { + if (!plaintext) { + stmp += F (","); + } else { + stmp += F ("\n"); + } + + } + if (!plaintext) { + stmp += F ("{\"bssid\":\""); + } + //BSSID +#ifdef ARDUINO_ARCH_ESP32 + stmp += CONFIG::mac2str (tcpip_sta_list.sta[i].mac); +#else + stmp += CONFIG::mac2str (station->bssid); +#endif + if (!plaintext) { + stmp += F ("\",\"ip\":\""); + } else { + stmp += F (" "); + } + //IP +#ifdef ARDUINO_ARCH_ESP32 + stmp += IPAddress (tcpip_sta_list.sta[i].ip.addr).toString().c_str(); +#else + stmp += IPAddress ( (const uint8_t *) &station->ip).toString().c_str(); +#endif + if (!plaintext) { + stmp += F ("\"}"); + } + //increment counter + client_counter++; +#ifdef ARDUINO_ARCH_ESP32 + } +#else + //go next record + station = STAILQ_NEXT (station, next); + } + wifi_softap_free_station_info(); +#endif + if (!plaintext) { + ESPCOM::print (stmp.c_str(), output, espresponse); + ESPCOM::print (F ("],"), output, espresponse); + } else { + //display number of client + ESPCOM::println (String (client_counter).c_str(), output, espresponse); + //display list if any + if (stmp.length() > 0) { + ESPCOM::println (stmp.c_str(), output, espresponse); + } + } + + if (!plaintext) { + ESPCOM::print (F ("\"disabled_mode\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Disabled Mode: "), output, espresponse); + } + ESPCOM::print (F ("STA ("), output, espresponse); + ESPCOM::print (WiFi.macAddress().c_str(), output, espresponse); + ESPCOM::print (F (") is disabled"), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + + } else if (WiFi.getMode() == WIFI_AP_STA) + { + ESPCOM::print (F ("Mixed"), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + if (!plaintext) { + ESPCOM::print (F ("\"active_mode\":\""), output, espresponse); + } else { + ESPCOM::print (F ("Active Mode: "), output, espresponse); + } + ESPCOM::print (F ("AP ("), output, espresponse); + ESPCOM::print (WiFi.softAPmacAddress().c_str(), output, espresponse); + ESPCOM::println (F (")"), output, espresponse); + ESPCOM::print (F ("STA ("), output, espresponse); + ESPCOM::print (WiFi.macAddress().c_str(), output, espresponse); + ESPCOM::print (F (")"), output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + } else + { + ESPCOM::print ("Wifi Off", output, espresponse); + if (!plaintext) { + ESPCOM::print (F ("\","), output, espresponse); + } else { + ESPCOM::print (F ("\n"), output, espresponse); + } + } + + if (!plaintext) + { + ESPCOM::print (F ("\"captive_portal\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("Captive portal: "), output, espresponse); + } +#ifdef CAPTIVE_PORTAL_FEATURE + ESPCOM::print (F ("Enabled"), output, espresponse); +#else + ESPCOM::print (F ("Disabled"), output, espresponse); +#endif + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) + { + ESPCOM::print (F ("\"ssdp\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("SSDP: "), output, espresponse); + } +#ifdef SSDP_FEATURE + ESPCOM::print (F ("Enabled"), output, espresponse); +#else + ESPCOM::print (F ("Disabled"), output, espresponse); +#endif + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) + { + ESPCOM::print (F ("\"netbios\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("NetBios: "), output, espresponse); + } +#ifdef NETBIOS_FEATURE + ESPCOM::print (F ("Enabled"), output, espresponse); +#else + ESPCOM::print (F ("Disabled"), output, espresponse); +#endif + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) + { + ESPCOM::print (F ("\"mdns\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("mDNS: "), output, espresponse); + } +#ifdef MDNS_FEATURE + ESPCOM::print (F ("Enabled"), output, espresponse); +#else + ESPCOM::print (F ("Disabled"), output, espresponse); +#endif + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) + { + ESPCOM::print (F ("\"web_update\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("Web Update: "), output, espresponse); + } +#ifdef WEB_UPDATE_FEATURE + ESPCOM::print (F ("Enabled"), output, espresponse); +#else + ESPCOM::print (F ("Disabled"), output, espresponse); +#endif + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) + { + ESPCOM::print (F ("\"pin recovery\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("Pin Recovery: "), output, espresponse); + } +#ifdef RECOVERY_FEATURE + ESPCOM::print (F ("Enabled"), output, espresponse); +#else + ESPCOM::print (F ("Disabled"), output, espresponse); +#endif + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) + { + ESPCOM::print (F ("\"autentication\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("Authentication: "), output, espresponse); + } +#ifdef AUTHENTICATION_FEATURE + ESPCOM::print (F ("Enabled"), output, espresponse); +#else + ESPCOM::print (F ("Disabled"), output, espresponse); +#endif + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } + + if (!plaintext) + { + ESPCOM::print (F ("\"target_fw\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("Target Firmware: "), output, espresponse); + } + ESPCOM::print (CONFIG::GetFirmwareTargetName(), output, espresponse); + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } + //flag M117 + if (!plaintext) + { + ESPCOM::print (F ("\"M117_output\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("M117 output: "), output, espresponse); + } + if (!CONFIG::is_locked(FLAG_BLOCK_M117)) + { + ESPCOM::print (F ("Enabled"), output, espresponse); + } else + { + ESPCOM::print (F ("Disabled"), output, espresponse); + } + + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } + + //Flag Oled +#ifdef ESP_OLED_FEATURE + if (!plaintext) + { + ESPCOM::print (F ("\"Oled_output\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("Oled output: "), output, espresponse); + } + if (!CONFIG::is_locked(FLAG_BLOCK_OLED)) + { + ESPCOM::print (F ("Enabled"), output, espresponse); + } else + { + ESPCOM::print (F ("Disabled"), output, espresponse); + } + + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } +#endif + + //flag serial + if (!plaintext) + { + ESPCOM::print (F ("\"Serial_output\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("Serial output: "), output, espresponse); + } + if (!CONFIG::is_locked(FLAG_BLOCK_SERIAL)) + { + ESPCOM::print (F ("Enabled"), output, espresponse); + } else + { + ESPCOM::print (F ("Disabled"), output, espresponse); + } + + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } + +#ifdef WS_DATA_FEATURE + //flag websocket + if (!plaintext) + { + ESPCOM::print (F ("\"Websocket_output\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("Web socket output: "), output, espresponse); + } + if (!CONFIG::is_locked(FLAG_BLOCK_WSOCKET)) + { + ESPCOM::print (F ("Enabled"), output, espresponse); + } else + { + ESPCOM::print (F ("Disabled"), output, espresponse); + } + + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } +#endif +#ifdef TCP_IP_DATA_FEATURE + //flag tcp + if (!plaintext) + { + ESPCOM::print (F ("\"TCP_output\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("TCP output: "), output, espresponse); + } + if (!CONFIG::is_locked(FLAG_BLOCK_TCP)) + { + ESPCOM::print (F ("Enabled"), output, espresponse); + } else + { + ESPCOM::print (F ("Disabled"), output, espresponse); + } + + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } +#endif +#ifdef DEBUG_ESP3D + if (!plaintext) + { + ESPCOM::print (F ("\"debug\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("Debug: "), output, espresponse); + } + ESPCOM::print (F ("Debug Enabled :"), output, espresponse); +#ifdef DEBUG_OUTPUT_SPIFFS + ESPCOM::print (F ("SPIFFS"), output, espresponse); +#endif +#ifdef DEBUG_OUTPUT_SD + ESPCOM::print (F ("SD"), output, espresponse); +#endif +#ifdef DEBUG_OUTPUT_SERIAL + ESPCOM::print (F ("serial"), output, espresponse); +#endif +#ifdef DEBUG_OUTPUT_TCP + ESPCOM::print (F ("TCP"), output, espresponse); +#endif + if (!plaintext) + { + ESPCOM::print (F ("\","), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } +#endif + if (!plaintext) + { + ESPCOM::print (F ("\"fw\":\""), output, espresponse); + } else + { + ESPCOM::print (F ("FW version: "), output, espresponse); + } + ESPCOM::print (FW_VERSION, output, espresponse); + #ifdef ARDUINO_ARCH_ESP8266 + ESPCOM::print (" ESP8266/8586", output, espresponse); + #else + ESPCOM::print (" ESP32", output, espresponse); + #endif + if (!plaintext) + { + ESPCOM::print (F ("\"}"), output, espresponse); + } else + { + ESPCOM::print (F ("\n"), output, espresponse); + } +} diff --git a/esp3d/config.h b/src/config.h similarity index 58% rename from esp3d/config.h rename to src/config.h index ec02dc4b..6f984759 100644 --- a/esp3d/config.h +++ b/src/config.h @@ -18,18 +18,144 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -//definition +//version and sources location +#define FW_VERSION "2.0.0.c14" +#define REPOSITORY "https://github.com/luc-github/ESP3D" + +//Customize ESP3D //////////////////////////////////////////////////////////////////////// +#define ESP8266_MODEL_NAME "ESP8266" +#define ESP8266_MODEL_URL "http://espressif.com/en/products/esp8266/" +#define ESP32_MODEL_NAME "ESP32" +#define ESP32_MODEL_URL "https://www.espressif.com/en/products/hardware/esp-wroom-32/overview" +#define ESP_MODEL_NUMBER "ESP3D 2.0" +#define ESP_MANUFACTURER_NAME "Espressif Systems" +#define ESP_MANUFACTURER_URL "http://espressif.com" +//default name if no mac address is valid +#define ESP_DEFAULT_NAME "MYESP" +//if commented name will follow mac address 3 last digits +//like ESP_XXXXXX (eg:ESP_028E41) to avoid overlap if several ESP3D +#define ESP_HOST_NAME ESP_DEFAULT_NAME + + +//FEATURES - comment to disable ////////////////////////////////////////////////////////// + +//Do we use async webserver or not +//#define ASYNCWEBSERVER + +//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 + +//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 + +//NETBIOS_FEATURE: this feature is a discovery protocol, supported on Windows out of the box +#define NETBIOS_FEATURE + +//CAPTIVE_PORTAL_FEATURE: In SoftAP redirect all unknow call to main page +#define CAPTIVE_PORTAL_FEATURE + +//WEB_UPDATE_FEATURE: allow to flash fw using web UI +#define WEB_UPDATE_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 + +//DIRECT_PIN_FEATURE: allow to access pin using ESP201 command +#define DIRECT_PIN_FEATURE + +//ESP_OLED_FEATURE: allow oled screen output +#define ESP_OLED_FEATURE + +//DHT_FEATURE: send update of temperature / humidity based on DHT 11/22 +#define DHT_FEATURE + +//AUTHENTICATION_FEATURE: protect pages by login password +//#define AUTHENTICATION_FEATURE + +//WS_DATA_FEATURE: allow to connect serial from Websocket +#define WS_DATA_FEATURE + +//TIMESTAMP_FEATURE: Time stamp feature on direct SD files +#define TIMESTAMP_FEATURE + +//Extra features ///////////////////////////////////////////////////////////////////////// + +//Serial rx buffer size is 256 but can be extended +#define SERIAL_RX_BUFFER_SIZE 512 + +//which serial ESP use to communicate to printer (ESP32 has 3 serials available) +#define USE_SERIAL_0 +//#define USE_SERIAL_1 +//#define USE_SERIAL_2 + +//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_SERIAL +//#define DEBUG_OUTPUT_TCP + +//Pins Definition //////////////////////////////////////////////////////////////////////// +#ifdef RECOVERY_FEATURE +//pin used to reset setting +#define RESET_CONFIG_PIN 2 +#endif + +#ifdef DHT_FEATURE +#define ESP_DHT_PIN 2 +#endif + +//Pins where the screen is connected +#ifdef ESP_OLED_FEATURE +#define OLED_PIN_A 4 +#define OLED_PIN_B 15 +#define OLED_ADDR 0x3c +#define HELTEC_EMBEDDED_PIN 16 //0 to disable +#define OLED_FLIP_VERTICALY 1 //0 to disable +#endif + + +//Supported FW ///////////////////////////////////////////////////////////// #define UNKNOWN_FW 0 #define REPETIER4DV 1 #define MARLIN 2 #define MARLINKIMBRA 3 #define SMOOTHIEWARE 4 #define REPETIER 5 +#define GRBL 6 +#define MAX_FW_ID 6 + +//Do not Edit after this line ////////////////////////////////////////////// + +//Sanity check +#ifndef SDCARD_FEATURE +#ifdef TIMESTAMP_FEATURE +#undef TIMESTAMP_FEATURE +#endif +#endif + +#if defined(ASYNCWEBSERVER) +#define ESP_USE_ASYNC true +#else +#define ESP_USE_ASYNC false +#endif + +//number of clients allowed to use data port at once +#define MAX_SRV_CLIENTS 1 #ifdef ARDUINO_ARCH_ESP32 #include "FS.h" #include "SPIFFS.h" +using fs::File; #define WIFI_NONE_SLEEP WIFI_PS_NONE +#define WIFI_LIGHT_SLEEP WIFI_PS_MIN_MODEM #define WIFI_MODEM_SLEEP WIFI_PS_MAX_MODEM #define WIFI_PHY_MODE_11B WIFI_PROTOCOL_11B #define WIFI_PHY_MODE_11G WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G @@ -42,111 +168,28 @@ #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 +#define WIFI_EVENT_STAMODE_CONNECTED SYSTEM_EVENT_STA_CONNECTED +#define WIFI_EVENT_STAMODE_DISCONNECTED SYSTEM_EVENT_STA_DISCONNECTED +#define WIFI_EVENT_STAMODE_GOT_IP SYSTEM_EVENT_STA_GOT_IP +#define WIFI_EVENT_SOFTAPMODE_STACONNECTED SYSTEM_EVENT_AP_STACONNECTED #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 - -//comment to disable -//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 - -//NETBIOS_FEATURE: this feature is a discovery protocol, supported on Windows out of the box -#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 - -//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 - -#ifdef RECOVERY_FEATURE -//pin used to reset setting -#define RESET_CONFIG_PIN 2 -#endif - -//DIRECT_PIN_FEATURE: allow to access pin using ESP201 command -#define DIRECT_PIN_FEATURE - -//INFO_MSG_FEATURE: catch the Info msg and filter it to specific table -#define INFO_MSG_FEATURE - -//ERROR_MSG_FEATURE: catch the error msg and filter it to specific table -#define ERROR_MSG_FEATURE - -//STATUS_MSG_FEATURE: catch the status msg and filter it to specific table -#define STATUS_MSG_FEATURE - -//Serial rx buffer size is 256 but can be extended -#define SERIAL_RX_BUFFER_SIZE 512 - -#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_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) -/* -#ifndef FS_NO_GLOBALS -#define FS_NO_GLOBALS -#endif -#include -#define DEBUG_ESP3D(string) { FS_FILE logfile = SPIFFS.open("/log.txt", "a+");logfile.print(string);logfile.close();} -*/ +//WEBHOST_SDCARD_FEATURE : to use SDCard to host webpages +//NOT YET IMPLEMENTED!!! Keep it as TODO +//#define WEBHOST_SDCARD_FEATURE #ifdef DEBUG_ESP3D #ifdef DEBUG_OUTPUT_SPIFFS @@ -157,13 +200,15 @@ #define DEBUG_PIPE NO_PIPE #define LOG(string) { FS_FILE logfile = SPIFFS.open("/log.txt", "a+");logfile.print(string);logfile.close();} #endif + + #ifdef DEBUG_OUTPUT_SERIAL -#define LOG(string) {ESP_SERIAL_OUT.print(string);} #define DEBUG_PIPE SERIAL_PIPE +#define LOG(string) {Serial.print(string);} #endif #ifdef DEBUG_OUTPUT_TCP -#include "bridge.h" -#define LOG(string) {BRIDGE::send2TCP(string);} +#include "espcom.h" +#define LOG(string) {ESPCOM::send2TCP(string, true);} #define DEBUG_PIPE TCP_PIPE #endif #else @@ -171,6 +216,8 @@ #define DEBUG_PIPE NO_PIPE #endif +#define NOLOG(string) {} + #ifndef CONFIG_h #define CONFIG_h @@ -180,23 +227,38 @@ extern "C" { #include "user_interface.h" } #else -//Nothing here + #endif #include "wificonf.h" -//version and sources location -#define FW_VERSION "1.0" -#define REPOSITORY "https://github.com/luc-github/ESP3D" + +typedef enum { + UPLOAD_STATUS_NONE = 0, + UPLOAD_STATUS_FAILED = 1, + UPLOAD_STATUS_CANCELLED = 2, + UPLOAD_STATUS_SUCCESSFUL = 3, + UPLOAD_STATUS_ONGOING = 4 +} upload_status_type; typedef enum { NO_PIPE = 0, SERIAL_PIPE = 2, SERIAL1_PIPE = 3, + SERIAL2_PIPE = 4, #ifdef TCP_IP_DATA_FEATURE - TCP_PIPE = 4, + TCP_PIPE = 5, #endif - WEB_PIPE = 5 +#ifdef WS_DATA_FEATURE + WS_PIPE = 6, +#endif +#ifdef ESP_OLED_FEATURE + OLED_PIPE = 7, +#endif + WEB_PIPE = 8, + PRINTER_PIPE = 9 } tpipe; +#define DEFAULT_PRINTER_PIPE SERIAL_PIPE + typedef enum { LEVEL_GUEST = 0, LEVEL_USER = 1, @@ -232,11 +294,11 @@ typedef enum { #define EP_SSID_VISIBLE 120 //1 byte = flag #define EP_WEB_PORT 121 //4 bytes = int #define EP_DATA_PORT 125 //4 bytes = int -#define EP_REFRESH_PAGE_TIME 129 //1 bytes = flag +#define EP_OUTPUT_FLAG 129 //1 bytes = flag #define EP_HOSTNAME 130//33 bytes 32+1 = string ; warning does not support multibyte char like chinese -#define EP_XY_FEEDRATE 164//4 bytes = int -#define EP_Z_FEEDRATE 168//4 bytes = int -#define EP_E_FEEDRATE 172//4 bytes = int +#define EP_DHT_INTERVAL 164//4 bytes = int +#define EP_FREE_INT2 168//4 bytes = int +#define EP_FREE_INT3 172//4 bytes = int #define EP_ADMIN_PWD 176//21 bytes 20+1 = string ; warning does not support multibyte char like chinese #define EP_USER_PWD 197//21 bytes 20+1 = string ; warning does not support multibyte char like chinese #define EP_AP_SSID 218 //33 bytes 32+1 = string ; warning does not support multibyte char like chinese @@ -246,8 +308,9 @@ 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 -#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_SD_SPEED_DIV 331 //1 byte = flag +#define EP_FREE_STRING1 332 //128 bytes 127+1 = string ; warning does not support multibyte char like chinese +#define EP_DHT_TYPE 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 @@ -263,6 +326,10 @@ typedef enum { #define LAST_EEPROM_ADDRESS 855 //next available is 855 //space left 1024 - 855 = 169 +//extra fre +//#define EP_FREE_INT2 168//4 bytes = int +//#define EP_FREE_INT3 172//4 bytes = int +//#define EP_FREE_STRING1 331 //129 bytes 128+1 = string ; warning does not support multibyte char like chinese //default values #define DEFAULT_WIFI_MODE AP_MODE @@ -276,7 +343,6 @@ const byte DEFAULT_IP_VALUE[] = {192, 168, 0, 1}; const byte DEFAULT_MASK_VALUE[] = {255, 255, 255, 0}; #define DEFAULT_GATEWAY_VALUE DEFAULT_IP_VALUE const long DEFAULT_BAUD_RATE = 115200; -const char M117_[] PROGMEM = "M117 "; #define DEFAULT_PHY_MODE WIFI_PHY_MODE_11G #define DEFAULT_SLEEP_MODE WIFI_MODEM_SLEEP #define DEFAULT_CHANNEL 11 @@ -286,10 +352,6 @@ const char M117_[] PROGMEM = "M117 "; #define DEFAULT_BEACON_INTERVAL 100 const int DEFAULT_WEB_PORT = 80; const int DEFAULT_DATA_PORT = 8888; -#define DEFAULT_REFRESH_PAGE_TIME 3 -const int DEFAULT_XY_FEEDRATE=1000; -const int DEFAULT_Z_FEEDRATE =100; -const int DEFAULT_E_FEEDRATE=400; const char DEFAULT_ADMIN_PWD [] PROGMEM = "admin"; const char DEFAULT_USER_PWD [] PROGMEM = "user"; const char DEFAULT_ADMIN_LOGIN [] PROGMEM = "admin"; @@ -299,16 +361,28 @@ const char DEFAULT_TIME_SERVER2 [] PROGMEM = "0.pool.ntp.org"; const char DEFAULT_TIME_SERVER3 [] PROGMEM = "1.pool.ntp.org"; #define DEFAULT_TIME_ZONE 0 #define DEFAULT_TIME_DST 0 -#define DEFAULT_PRIMARY_SD 1 -#define DEFAULT_SECONDARY_SD 2 +#define DEFAULT_PRIMARY_SD 2 +#define DEFAULT_SECONDARY_SD 1 #define DEFAULT_DIRECT_SD_CHECK 0 #define DEFAULT_SD_CHECK_UPDATE_AT_BOOT 1 +#define DEFAULT_OUTPUT_FLAG 0 +#define DEFAULT_DHT_TYPE 255 +const int DEFAULT_DHT_INTERVAL = 30; #define DEFAULT_IS_DIRECT_SD 0 - +//SD Card reader speed +//possible values are :SPI_FULL_SPEED, SPI_DIV3_SPEED, +//SPI_HALF_SPEED, SPI_DIV6_SPEED, SPI_QUARTER_SPEED, +//SPI_EIGHTH_SPEED, SPI_SIXTEENTH_SPEED +//Decrease if reader give error +#ifdef ARDUINO_ARCH_ESP8266 +#define DEFAULT_SDREADER_SPEED SPI_HALF_SPEED +#else +#define DEFAULT_SDREADER_SPEED 4 +#endif const uint16_t Setting[][2] = { {EP_WIFI_MODE, LEVEL_ADMIN},//0 @@ -326,44 +400,41 @@ const uint16_t Setting[][2] = { {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 + {EP_HOSTNAME, LEVEL_ADMIN},//15 + {EP_ADMIN_PWD, LEVEL_ADMIN},//16 + {EP_USER_PWD, LEVEL_USER},//17 + {EP_AP_SSID, LEVEL_ADMIN},//18 + {EP_AP_PASSWORD, LEVEL_ADMIN},//19 + {EP_AP_IP_VALUE, LEVEL_ADMIN},//20 + {EP_AP_MASK_VALUE, LEVEL_ADMIN},//21 + {EP_AP_GATEWAY_VALUE, LEVEL_ADMIN},//22 + {EP_AP_IP_MODE, LEVEL_ADMIN},//23 + {EP_AP_PHY_MODE, LEVEL_ADMIN},//24 + {EP_TARGET_FW, LEVEL_USER},//25 + {EP_TIMEZONE, LEVEL_USER},//26 + {EP_TIME_ISDST, LEVEL_USER},//27 + {EP_TIME_SERVER1, LEVEL_USER},//28 + {EP_TIME_SERVER2, LEVEL_USER},//29 + {EP_TIME_SERVER3, LEVEL_USER},//30 + {EP_IS_DIRECT_SD, LEVEL_USER},//31 + {EP_PRIMARY_SD, LEVEL_USER},//32 + {EP_SECONDARY_SD, LEVEL_USER},//33 + {EP_DIRECT_SD_CHECK, LEVEL_USER}, //34 + {EP_SD_CHECK_UPDATE_AT_BOOT, LEVEL_USER},//35 + {EP_OUTPUT_FLAG, LEVEL_USER},//36 + {EP_DHT_INTERVAL, LEVEL_USER},//37 + {EP_DHT_TYPE, LEVEL_USER},//38 + {EP_SD_SPEED_DIV, LEVEL_USER}//39 }; -#define AUTH_ENTRY_NB 42 +#define AUTH_ENTRY_NB 40 + +#define FLAG_BLOCK_M117 0x01 +#define FLAG_BLOCK_OLED 0x02 +#define FLAG_BLOCK_SERIAL 0x04 +#define FLAG_BLOCK_WSOCKET 0x08 +#define FLAG_BLOCK_TCP 0x010 + //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 @@ -389,42 +460,69 @@ const uint16_t Setting[][2] = { #define MIN_HOSTNAME_LENGTH 1 #define WL_MAC_ADDR_LENGTH 6 +#if defined(ASYNCWEBSERVER) +class AsyncResponseStream; +typedef AsyncResponseStream ESPResponseStream; +#else +class ESPResponseStream{ + public: + bool header_sent; + String buffer_web; + ESPResponseStream(){header_sent=false;}; +}; +#endif + class CONFIG { public: + static void wait (uint32_t milliseconds); + static void wdtFeed(); + static byte output_flag; +#ifdef DHT_FEATURE + static byte DHT_type; + static int DHT_interval; + static void InitDHT(bool refresh = false); +#endif + static bool is_locked(byte flag); 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); - static bool read_byte(int pos, byte * value); - static bool write_string(int pos, const char * byte_buffer); - static bool write_string(int pos, const __FlashStringHelper *str); - static bool write_buffer(int pos, const byte * byte_buffer, int size_buffer); - static bool write_byte(int pos, const byte value); + 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); + static bool read_byte (int pos, byte * value); + static bool write_string (int pos, const char * byte_buffer); + static bool write_string (int pos, const __FlashStringHelper *str); + 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, bool plaintext); - static bool SetFirmwareTarget(uint8_t fw); + static void print_config (tpipe output, bool plaintext, ESPResponseStream *espresponse = NULL); + static bool SetFirmwareTarget (uint8_t fw); static void InitFirmwareTarget(); + static void InitOutput(); static void InitDirectSD(); static void InitPins(); - static bool InitBaudrate(); + static bool InitBaudrate(long value = 0); 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(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 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 (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 (bool async = false); +#if defined(TIMESTAMP_FEATURE) + static void init_time_client(); +#endif private: static uint8_t FirmwareTarget; }; + + #endif diff --git a/src/esp3d.cpp b/src/esp3d.cpp new file mode 100644 index 00000000..2a428cad --- /dev/null +++ b/src/esp3d.cpp @@ -0,0 +1,317 @@ +/* + This file is part of ESP3D Firmware for 3D printer. + + ESP3D Firmware for 3D printer is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ESP3D Firmware for 3D printer 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this Firmware. If not, see . + + This firmware is using the standard arduino IDE with module to support ESP8266/ESP32: + https://github.com/esp8266/Arduino + https://github.com/espressif/arduino-esp32 + + Latest version of the code and documentation can be found here : + https://github.com/luc-github/ESP3D + + Main author: luc lebosse + +*/ +#include "esp3d.h" +#include +#ifndef FS_NO_GLOBALS +#define FS_NO_GLOBALS +#endif +#if defined (ASYNCWEBSERVER) +#include +#endif +#include "config.h" +#if defined(TIMESTAMP_FEATURE) +#include +#endif +#include "wificonf.h" +#include "espcom.h" +#include "webinterface.h" +#include "command.h" +#ifdef ARDUINO_ARCH_ESP8266 +#include "ESP8266WiFi.h" +#if defined (ASYNCWEBSERVER) +#include +#else +#include +#endif +#else //ESP32 +#include +#if defined (ASYNCWEBSERVER) +#include +#else +#include +#endif +#include +#include "esp_wifi.h" +#include "FS.h" +#include "SPIFFS.h" +#include "Update.h" +#endif +#include + +#ifdef CAPTIVE_PORTAL_FEATURE +#include +extern DNSServer dnsServer; +#endif + +#ifndef FS_NO_GLOBALS +#define FS_NO_GLOBALS +#endif +#include +#ifdef ESP_OLED_FEATURE +#include "esp_oled.h" +#endif +#ifdef DHT_FEATURE +#include "DHTesp.h" +DHTesp dht; +#endif + +#if defined (ASYNCWEBSERVER) +#include "asyncwebserver.h" +#else +#include "syncwebserver.h" +#endif + +//Contructor +Esp3D::Esp3D() { + +} + +//Begin which setup everything +void Esp3D::begin(uint16_t startdelayms, uint16_t recoverydelayms) +{ + // init: +#if defined( DEBUG_ESP3D) && defined(DEBUG_OUTPUT_SERIAL) + CONFIG::InitBaudrate(DEFAULT_BAUD_RATE); + delay (2000); + LOG ("\r\nDebug Serial set\r\n") +#endif +CONFIG::InitOutput(); +#ifdef ESP_OLED_FEATURE +OLED_DISPLAY::begin(); +OLED_DISPLAY::splash(); +#endif +#ifdef ARDUINO_ARCH_ESP8266 + struct rst_info *rtc_info = system_get_rst_info(); +#else + RESET_REASON reason_0 = rtc_get_reset_reason(0); + RESET_REASON reason_1 = rtc_get_reset_reason(1); +#endif + bool breset_config = false; + web_interface = NULL; +#ifdef TCP_IP_DATA_FEATURE + data_server = NULL; +#endif + + //WiFi.disconnect(); + WiFi.mode (WIFI_OFF); + wifi_config.WiFi_on = false; +#ifdef ESP_OLED_FEATURE + uint32_t start_display_time = millis(); + uint32_t now = millis(); + while ( now - start_display_time < startdelayms){ + int v = (100 * (millis() - start_display_time)) / startdelayms; + OLED_DISPLAY::display_mini_progress(v); + OLED_DISPLAY::update_lcd(); + delay(100); + now = millis(); + } +#else + delay (startdelayms); +#endif + + CONFIG::InitDirectSD(); + CONFIG::InitPins(); +#ifdef RECOVERY_FEATURE + delay (recoverydelayms); + //check if reset config is requested + if (digitalRead (RESET_CONFIG_PIN) == 0) { + breset_config = true; //if requested =>reset settings + } +#endif + //check if EEPROM has value + if ( !CONFIG::InitBaudrate() || !CONFIG::InitExternalPorts() ) { + breset_config = true; //cannot access to config settings=> reset settings + LOG ("Error no EEPROM access\r\n") + } + + //reset is requested + if (breset_config) { + //update EEPROM with default settings + CONFIG::InitBaudrate(DEFAULT_BAUD_RATE); +#ifdef ARDUINO_ARCH_ESP8266 + Serial.setRxBufferSize (SERIAL_RX_BUFFER_SIZE); +#endif + delay (2000); + ESPCOM::println (F ("ESP EEPROM reset"), PRINTER_PIPE); +#ifdef DEBUG_ESP3D + 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); + wifi_config.WiFi_on = true; +#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); + ESPCOM::flush (DEFAULT_PRINTER_PIPE); +#endif + //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() ) { + ESPCOM::println (F ("Safe mode 1"), PRINTER_PIPE); + //try again in AP mode + if (!wifi_config.Setup (true) ) { + ESPCOM::println (F ("Safe mode 2"), PRINTER_PIPE); + wifi_config.Safe_Setup(); + } + } + delay (1000); + //setup servers + if (!wifi_config.Enable_servers() ) { + ESPCOM::println (F ("Error enabling servers"), PRINTER_PIPE); + } +#ifdef ARDUINO_ARCH_ESP8266 + if (rtc_info->reason == REASON_WDT_RST || + + rtc_info->reason == REASON_EXCEPTION_RST || + + rtc_info->reason == REASON_SOFT_WDT_RST) { + String s = "reset "; + s+= String(rtc_info->reason); + + if (rtc_info->reason == REASON_EXCEPTION_RST) { + s+=" except "; + s+=String(rtc_info->exccause); + + } + ESPCOM::println (s, PRINTER_PIPE); + } +#else + if((( reason_0< 17) || ( reason_1< 17)) && !(((reason_0 == 1) && (reason_1 == 14)) || ((reason_0 == 16) && (reason_1 == 14)))) + { + String s = "reset "; + ESPCOM::println (s, PRINTER_PIPE); + s+=String(reason_0); + s+="/"; + s+=String(reason_1); + + } +#endif + +#ifdef ASYNCWEBSERVER + if (WiFi.getMode() != WIFI_AP) { + WiFi.scanNetworks (true); + } +#endif + LOG ("Setup Done\r\n"); +} + +//Process which handle all input +void Esp3D::process() +{ +#if !defined(ASYNCWEBSERVER) +//web requests for sync + web_interface->web_server.handleClient(); + socket_server->loop(); +#endif +//be sure wifi is on to proceed wifi function + if ((WiFi.getMode() != WIFI_OFF) || wifi_config.WiFi_on) { +#ifdef CAPTIVE_PORTAL_FEATURE + if (WiFi.getMode() != WIFI_STA ) { + dnsServer.processNextRequest(); + } +#endif + //TODO use config + CONFIG::wait(0); + } +//read / bridge all input + ESPCOM::bridge(); +//in case of restart requested + if (web_interface->restartmodule) { + CONFIG::esp_restart(); + } + +#ifdef ESP_OLED_FEATURE + static uint32_t last_oled_update= 0; + if ( !CONFIG::is_locked(FLAG_BLOCK_OLED)){ + uint32_t now_oled = millis(); + if (now_oled - last_oled_update > 1000) { + last_oled_update = now_oled; + //refresh signal + if ((WiFi.getMode() == WIFI_OFF) || !wifi_config.WiFi_on) OLED_DISPLAY::display_signal(-1); + else OLED_DISPLAY::display_signal(wifi_config.getSignal (WiFi.RSSI ())); + //if line 0 is > 85 refresh + if(OLED_DISPLAY::L0_size >85)OLED_DISPLAY::display_text(OLED_DISPLAY::L0.c_str(), 0, 0, 85); + //if line 1 is > 128 refresh + if(OLED_DISPLAY::L1_size >128) OLED_DISPLAY::display_text(OLED_DISPLAY::L1.c_str(), 0, 16, 128); + //if line 2 is > 128 refresh + if(OLED_DISPLAY::L2_size >128) OLED_DISPLAY::display_text(OLED_DISPLAY::L2.c_str(), 0, 32, 128); + //if line 3 is > 128 refresh + if(OLED_DISPLAY::L3_size >128) OLED_DISPLAY::display_text(OLED_DISPLAY::L3.c_str(), 0, 48, 128); + OLED_DISPLAY::update_lcd(); + } + } +#endif + +#ifdef DHT_FEATURE + if (CONFIG::DHT_type != 255) { + static uint32_t last_dht_update= 0; + uint32_t now_dht = millis(); + if (now_dht - last_dht_update > (CONFIG::DHT_interval * 1000)) { + last_dht_update = now_dht; + float humidity = dht.getHumidity(); + float temperature = dht.getTemperature(); + if (dht.getStatusString() == "OK") { + String s = String(temperature,2); + String s2 = s + " " +String(humidity,2); + #if defined (ASYNCWEBSERVER) + web_interface->web_events.send( s2.c_str(),"DHT", millis()); + #else + s = "DHT:" + s2; + socket_server->sendTXT(ESPCOM::current_socket_id, s); + #endif + #ifdef ESP_OLED_FEATURE + if ( !CONFIG::is_locked(FLAG_BLOCK_OLED)){ + s = String(temperature,2); + s +="°C"; + OLED_DISPLAY::display_text(s.c_str(), 84, 16); + } + #endif + } + } + } +#endif +//todo use config + CONFIG::wait(0); +} diff --git a/src/esp3d.h b/src/esp3d.h new file mode 100644 index 00000000..f53f113a --- /dev/null +++ b/src/esp3d.h @@ -0,0 +1,36 @@ +/* + esp3d.h - esp3d class + + 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 +*/ + +#ifndef ESP3D_H +#define ESP3D_H +//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 "Arduino.h" +class Esp3D +{ +public: + Esp3D(); + void begin(uint16_t startdelayms = 8000, uint16_t recoverydelayms = 8000); + void process(); +}; +#endif diff --git a/src/esp_oled.cpp b/src/esp_oled.cpp new file mode 100644 index 00000000..59d21b58 --- /dev/null +++ b/src/esp_oled.cpp @@ -0,0 +1,226 @@ +/* + esp_oled.cpp - ESP3D oled class + + 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 +*/ +#include "config.h" +#ifdef ESP_OLED_FEATURE +#include "esp_oled.h" +#include "SSD1306.h" +#include "Wire.h" +SSD1306 esp_display(OLED_ADDR, OLED_PIN_A, OLED_PIN_B); +#define ESP3D_Logo_width 62 +#define ESP3D_Logo_height 45 +const char ESP3D_Logo[] PROGMEM = { + 0x00, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, + 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x00, + 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0xE0, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x80, 0x01, + 0xF0, 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x03, 0xF8, 0xFF, 0xFF, 0xFF, + 0x07, 0x00, 0x00, 0x06, 0xFC, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x0C, + 0x0C, 0x7C, 0x78, 0xC0, 0xC1, 0xC1, 0x0F, 0x18, 0x06, 0x38, 0x60, 0x80, + 0xE1, 0xC3, 0x3F, 0x10, 0xC6, 0x19, 0x67, 0x1C, 0xF1, 0xC7, 0x7F, 0x10, + 0xC6, 0x1F, 0x7F, 0x3C, 0x31, 0xCF, 0xF1, 0x10, 0xC7, 0x1F, 0x7F, 0x3C, + 0x01, 0xCE, 0xE1, 0x20, 0xC7, 0x3F, 0x7E, 0x1C, 0x01, 0xC7, 0xC1, 0x21, + 0x07, 0x3C, 0x78, 0x80, 0xE1, 0xC3, 0xC1, 0x21, 0x07, 0xFC, 0x70, 0xC0, + 0xE1, 0xC7, 0xC1, 0x21, 0xC7, 0xFF, 0x63, 0xFC, 0x01, 0xCF, 0xC1, 0x21, + 0xC7, 0xFF, 0x63, 0xFC, 0x01, 0xCE, 0xC1, 0x21, 0xC7, 0xDF, 0x63, 0xFC, + 0x01, 0xCE, 0xE1, 0x20, 0xC6, 0x99, 0x73, 0xFC, 0x31, 0xCF, 0x7F, 0x10, + 0x06, 0x18, 0x70, 0xFC, 0xF0, 0xC7, 0x3F, 0x10, 0x0E, 0x78, 0x78, 0xFC, + 0xE0, 0xC3, 0x0F, 0x10, 0xFC, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x18, + 0xFC, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x0C, 0xF8, 0xFF, 0xFF, 0x3F, + 0x00, 0x00, 0x00, 0x06, 0xF0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x03, + 0xE0, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x80, 0x01, 0xC0, 0xFF, 0xFF, 0x03, + 0x00, 0x00, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, + 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x07, 0x00, 0x00, + 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0x00, + }; + +void OLED_DISPLAY::splash(){ + if ( CONFIG::is_locked(FLAG_BLOCK_OLED))return; + esp_display.drawXbm(33, 10, ESP3D_Logo_width, ESP3D_Logo_height, ESP3D_Logo); + update_lcd(); +} + +void OLED_DISPLAY::begin(){ + //For Embeded OLED on Wifi kit 32 +#if HELTEC_EMBEDDED_PIN > 0 + pinMode(HELTEC_EMBEDDED_PIN,OUTPUT); + digitalWrite(HELTEC_EMBEDDED_PIN, LOW); // turn the LED on (HIGH is the voltage level) + delay(100); // wait for a second + digitalWrite(HELTEC_EMBEDDED_PIN, HIGH); // turn the LED off by making the voltage LOW +#endif + esp_display.init(); + esp_display.clear(); + #if OLED_FLIP_VERTICALY + esp_display.flipScreenVertically(); +#endif +} + +void OLED_DISPLAY::setCursor(int col, int row){ + if (col!=-1) OLED_DISPLAY::col = col; + if (row!=-1) OLED_DISPLAY::row = row; +} +void OLED_DISPLAY::print(String & s){ + OLED_DISPLAY::print(s.c_str()); +} +void OLED_DISPLAY::print(const char * s){ + display_text(s, col, row); +} + +void OLED_DISPLAY::display_signal(int value, int x, int y) { + if(CONFIG::is_locked(FLAG_BLOCK_OLED))return; + //clear area only + esp_display.setColor(BLACK); + esp_display.fillRect(x, y, x + 46, 16); + esp_display.setColor(WHITE); + if (value == -1){ + + esp_display.setFont(ArialMT_Plain_10); + esp_display.drawString(x+20, y, "X"); + + } else { + if (value > 0) { + esp_display.fillRect(x, y + 6, 3, 4); + } else { + esp_display.drawRect(x, y + 6, 3, 4); + } + + if (value >= 25) { + esp_display.fillRect(x + 4, y + 4, 3, 6); + } else { + esp_display.drawRect(x + 4, y + 4, 3, 6); + } + + if (value >= 50) { + esp_display.fillRect(x + 8, y + 2, 3, 8); + } else { + esp_display.drawRect(x + 8, y + 2, 3, 8); + } + + if (value >= 75) { + esp_display.fillRect(x + 12, y, 3, 10); + } else { + esp_display.drawRect(x + 12, y, 3, 10); + } + + String s = CONFIG::intTostr (value); + s+="%"; + //set current font size + esp_display.setFont(ArialMT_Plain_10); + esp_display.drawString(x+16, y, s.c_str()); + } +} + + +void OLED_DISPLAY::display_progress(int value, int x, int y) { + if ( CONFIG::is_locked(FLAG_BLOCK_OLED))return; + esp_display.setFont(ArialMT_Plain_10); + esp_display.setColor(BLACK); + esp_display.fillRect(x, y, x + 128, 16); + esp_display.setColor(WHITE); + esp_display.drawProgressBar(x, y, 100, 10, value); + String p = String(value) + "%"; + esp_display.drawString(x+102, y - 1, p.c_str()); +} + +void OLED_DISPLAY::display_mini_progress(int value, int x, int y, int w) { + if ( CONFIG::is_locked(FLAG_BLOCK_OLED))return; + esp_display.setColor(BLACK); + esp_display.fillRect(x, y, x + w, 2); + esp_display.setColor(WHITE); + esp_display.drawRect(x , y, value, 2); +} + +//max is 128 by default but can be 85 for first line + +void OLED_DISPLAY::display_text(const char * txt, int x, int y, int max) { + static int shift_pos[4] = {-1, -1, -1, -1}; + static int t[4] = {0, 0, 0, 0}; + + int p = 0; + if (y==16) { + OLED_DISPLAY::L1 = txt; + OLED_DISPLAY::L1_size = esp_display.getStringWidth( txt); + p=1; + } + else if (y==32){ + OLED_DISPLAY::L2 = txt; + OLED_DISPLAY::L2_size = esp_display.getStringWidth( txt); + p=2; + } + else if (y==0){ + max = 85; + OLED_DISPLAY::L0 = txt; + OLED_DISPLAY::L0_size = esp_display.getStringWidth( txt); + p=0; + } + else{ + OLED_DISPLAY::L3 = txt; + OLED_DISPLAY::L3_size = esp_display.getStringWidth( txt); + p=3; + } + esp_display.setFont(ArialMT_Plain_10); + //clear area only + esp_display.setColor(BLACK); + esp_display.fillRect(x, y, max, 16); + esp_display.setColor(WHITE); + String Stxt = txt; + Stxt += " "; + (t[p])++; + if ((esp_display.getStringWidth( Stxt) > max) && (t[p] > 1)) { + (shift_pos[p]) ++; + String s2 = Stxt.substring(shift_pos[p]); + Stxt = s2; + if (esp_display.getStringWidth( s2) < max) { + //reset for next time + shift_pos[p] = -1; + t[p] = 0; + } + } + //be sure we stay in boundaries + while (esp_display.getStringWidth(Stxt) > max){ + Stxt.remove(Stxt.length()-1, 1); + } + esp_display.drawString(x, y, Stxt.c_str()); +} + +void OLED_DISPLAY::update_lcd(){ + esp_display.display(); +} +void OLED_DISPLAY::clear_lcd(){ + esp_display.clear(); +} +int OLED_DISPLAY::col = 0; +int OLED_DISPLAY::row = 0; +String OLED_DISPLAY::L0 = ""; +String OLED_DISPLAY::L1 = ""; +String OLED_DISPLAY::L2 = ""; +String OLED_DISPLAY::L3 = ""; +int OLED_DISPLAY::L0_size = 0; +int OLED_DISPLAY::L1_size = 0; +int OLED_DISPLAY::L2_size = 0; +int OLED_DISPLAY::L3_size = 0; +#endif diff --git a/src/esp_oled.h b/src/esp_oled.h new file mode 100644 index 00000000..be99979a --- /dev/null +++ b/src/esp_oled.h @@ -0,0 +1,54 @@ +/* + esp_oled.h - ESP3D oled class + + 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 +*/ + +#ifndef ESP_OLED_H +#define ESP_OLED_H +#include "config.h" +#ifdef ESP_OLED_FEATURE +class OLED_DISPLAY +{ +public: +static void begin(); +static void setCursor(int col, int row = -1); +static void print(String & s); +static void print(const char * s); +static void display_signal( int value, int x=86, int y=0); +static void display_text(const char * txt, int x=0, int y=48, int max=128); +static void display_progress(int value, int x=0, int y=48); +static void display_mini_progress(int value, int x = 14, int y=61, int w=100); +static void update_lcd(); +static void clear_lcd(); +static void splash(); +static String L0; +static String L1; +static String L2; +static String L3; +static int L0_size; +static int L1_size; +static int L2_size; +static int L3_size; +private: +static int col; +static int row; + + +}; +#endif +#endif diff --git a/src/espcom.cpp b/src/espcom.cpp new file mode 100644 index 00000000..f01de179 --- /dev/null +++ b/src/espcom.cpp @@ -0,0 +1,420 @@ +/* + espcom.cpp - esp3d communication serial/tcp/etc.. class + + 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 +*/ + +#include "config.h" +#include "espcom.h" +#include "command.h" +#include "webinterface.h" +#if defined (ASYNCWEBSERVER) +#include "asyncwebserver.h" +#else +#include "syncwebserver.h" +#endif + +#ifdef ESP_OLED_FEATURE +#include "esp_oled.h" + bool ESPCOM::block_2_oled = false; +#endif + +uint8_t ESPCOM::current_socket_id=0; + +#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_SERIAL_1 +extern HardwareSerial Serial1; +#endif +#ifdef USE_SERIAL_2 +extern HardwareSerial Serial2; +#endif +#endif + +#ifdef TCP_IP_DATA_FEATURE +WiFiServer * data_server; +WiFiClient serverClients[MAX_SRV_CLIENTS]; +#endif + +bool ESPCOM::block_2_printer = false; + +void ESPCOM::bridge(bool async) +{ +//be sure wifi is on to proceed wifi function + if ((WiFi.getMode() != WIFI_OFF) || wifi_config.WiFi_on) { +//read tcp port input +#ifdef TCP_IP_DATA_FEATURE + ESPCOM::processFromTCP2Serial(); +#endif + } +//read serial input +ESPCOM::processFromSerial(); +} +long ESPCOM::readBytes (tpipe output, uint8_t * sbuf, size_t len) +{ + switch (output) { +#ifdef USE_SERIAL_0 + case SERIAL_PIPE: + return Serial.readBytes(sbuf,len); + break; +#endif +#ifdef USE_SERIAL_1 + case SERIAL_PIPE: + return Serial1.readBytes(sbuf,len); + break; +#endif +#ifdef USE_SERIAL_2 + case SERIAL_PIPE: + return Serial2.readBytes(sbuf,len); + break; +#endif + } +} +long ESPCOM::baudRate(tpipe output) +{ + long br = 0; + switch (output) { +#ifdef USE_SERIAL_0 + case SERIAL_PIPE: + br = Serial.baudRate(); + break; +#endif +#ifdef USE_SERIAL_1 + case SERIAL_PIPE: + br = Serial1.baudRate(); + break; +#endif +#ifdef USE_SERIAL_2 + case SERIAL_PIPE: + br = Serial2.baudRate(); + break; +#endif + } +#ifdef ARDUINO_ARCH_ESP32 + //workaround for ESP32 + if (br == 115201) { + br = 115200; + } + if (br == 230423) { + br = 230400; + } +#endif +return br; +} +size_t ESPCOM::available(tpipe output){ + switch (output) { +#ifdef USE_SERIAL_0 + case SERIAL_PIPE: + return Serial.available(); + break; +#endif +#ifdef USE_SERIAL_1 + case SERIAL_PIPE: + return Serial1.available(); + break; +#endif +#ifdef USE_SERIAL_2 + case SERIAL_PIPE: + return Serial2.available(); + break; +#endif + } +} +size_t ESPCOM::write(tpipe output, uint8_t d){ + if ((DEFAULT_PRINTER_PIPE == output) && (block_2_printer || CONFIG::is_locked(FLAG_BLOCK_M117))) return 0; + if ((SERIAL_PIPE == output) && CONFIG::is_locked(FLAG_BLOCK_SERIAL))return 0; + switch (output) { +#ifdef USE_SERIAL_0 + case SERIAL_PIPE: + return Serial.write(d); + break; +#endif +#ifdef USE_SERIAL_1 + case SERIAL_PIPE: + return Serial1.write(d); + break; +#endif +#ifdef USE_SERIAL_2 + case SERIAL_PIPE: + return Serial2.write(d); + break; +#endif + } +} +void ESPCOM::flush (tpipe output, ESPResponseStream *espresponse) +{ + switch (output) { +#ifdef USE_SERIAL_0 + case SERIAL_PIPE: + Serial.flush(); + break; +#endif +#ifdef USE_SERIAL_1 + case SERIAL_PIPE: + Serial1.flush(); + break; +#endif +#ifdef USE_SERIAL_2 + case SERIAL_PIPE: + Serial2.flush(); + break; +#endif +#if !defined (ASYNCWEBSERVER) + case WEB_PIPE: + if (espresponse) { + if(espresponse->header_sent) { + //send data + web_interface->web_server.sendContent(espresponse->buffer_web); + //close line + web_interface->web_server.sendContent(""); + } + espresponse->header_sent = false; + espresponse->buffer_web = String(); + } + break; +#endif + + } +} + +void ESPCOM::print (const __FlashStringHelper *data, tpipe output, ESPResponseStream *espresponse) +{ + String tmp = data; + ESPCOM::print (tmp.c_str(), output, espresponse); +} +void ESPCOM::print (String & data, tpipe output, ESPResponseStream *espresponse) +{ + ESPCOM::print (data.c_str(), output, espresponse); +} +void ESPCOM::print (const char * data, tpipe output, ESPResponseStream *espresponse) +{ + if ((DEFAULT_PRINTER_PIPE == output) && ( block_2_printer || CONFIG::is_locked(FLAG_BLOCK_M117))) return; + if ((SERIAL_PIPE == output) && CONFIG::is_locked(FLAG_BLOCK_SERIAL))return; +#ifdef TCP_IP_DATA_FEATURE + if ((TCP_PIPE == output) && CONFIG::is_locked(FLAG_BLOCK_TCP))return; +#endif +#ifdef WS_DATA_FEATURE + if ((WS_PIPE == output) && CONFIG::is_locked(FLAG_BLOCK_WSOCKET))return; +#endif +#ifdef ESP_OLED_FEATURE + if ((OLED_PIPE == output) && CONFIG::is_locked(FLAG_BLOCK_OLED))return; +#endif + switch (output) { +#ifdef USE_SERIAL_0 + case SERIAL_PIPE: + Serial.print (data); + break; +#endif +#ifdef USE_SERIAL_1 + case SERIAL_PIPE: + Serial1.print (data); + break; +#endif +#ifdef USE_SERIAL_2 + case SERIAL_PIPE: + Serial2.print (data); + break; +#endif +#ifdef TCP_IP_DATA_FEATURE + case TCP_PIPE: + ESPCOM::send2TCP (data); + break; +#endif + case WEB_PIPE: + if (espresponse != NULL) { +#if defined(ASYNCWEBSERVER) + espresponse->print (data); +#else + if (!espresponse->header_sent) { + 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); + espresponse->header_sent = true; + } + espresponse->buffer_web+=data; + if (espresponse->buffer_web.length() > 1200) { + //send data + web_interface->web_server.sendContent(espresponse->buffer_web); + //reset buffer + espresponse->buffer_web=""; + } +#endif + } + break; +#ifdef WS_DATA_FEATURE + case WS_PIPE: + { +#if defined(ASYNCWEBSERVER) +//Todo +#else + socket_server->sendBIN(current_socket_id,(const uint8_t *)data,strlen(data)); +#endif + } + break; +#endif + +#ifdef ESP_OLED_FEATURE + case OLED_PIPE: + { + if (!ESPCOM::block_2_oled) { + if(!((data=="\n")||(data=="\r")||(data=="\r\n"))) { + OLED_DISPLAY::print(data); + OLED_DISPLAY::update_lcd(); + } + } + } + break; +#endif + case PRINTER_PIPE: + { +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::setCursor(0, 48); + if(!((data=="\n")||(data=="\r")||(data=="\r\n")))ESPCOM::print(data, OLED_PIPE); +#endif + if(!((data=="\n")||(data=="\r")||(data=="\r\n")))ESPCOM::print ("M117 ", DEFAULT_PRINTER_PIPE); + ESPCOM::print (data, DEFAULT_PRINTER_PIPE); + } + break; + default: + break; + } +} +void ESPCOM::println (const __FlashStringHelper *data, tpipe output, ESPResponseStream *espresponse) +{ + ESPCOM::print (data, output, espresponse); +#ifdef TCP_IP_DATA_FEATURE + ESPCOM::print ("\r", output, espresponse); +#endif + ESPCOM::print ("\n", output, espresponse); +} +void ESPCOM::println (String & data, tpipe output, ESPResponseStream *espresponse) +{ + ESPCOM::print (data, output, espresponse); +#ifdef TCP_IP_DATA_FEATURE + ESPCOM::print ("\r", output, espresponse); +#endif + ESPCOM::print ("\n", output, espresponse); +} +void ESPCOM::println (const char * data, tpipe output, ESPResponseStream *espresponse) +{ + ESPCOM::print (data, output, espresponse); +#ifdef TCP_IP_DATA_FEATURE + ESPCOM::print ("\r", output, espresponse); +#endif + ESPCOM::print ("\n", output, espresponse); +} + + +#ifdef TCP_IP_DATA_FEATURE +void ESPCOM::send2TCP (const __FlashStringHelper *data, bool async) +{ + String tmp = data; + ESPCOM::send2TCP (tmp.c_str(), async); +} +void ESPCOM::send2TCP (String data, bool async) +{ + ESPCOM::send2TCP (data.c_str(), async); +} +void ESPCOM::send2TCP (const char * data, bool async) +{ + if (!async) { + for (uint8_t i = 0; i < MAX_SRV_CLIENTS; i++) { + if (serverClients[i] && serverClients[i].connected() ) { + serverClients[i].write (data, strlen (data) ); + delay (0); + } + } + } +} +#endif + +bool ESPCOM::processFromSerial (bool async) +{ + uint8_t i; + //check UART for data + if (ESPCOM::available(DEFAULT_PRINTER_PIPE)) { + size_t len = ESPCOM::available(DEFAULT_PRINTER_PIPE); + uint8_t sbuf[len+1]; + sbuf[len] = '\0'; + ESPCOM::readBytes (DEFAULT_PRINTER_PIPE, sbuf, len); +#ifdef TCP_IP_DATA_FEATURE + if (!async && !CONFIG::is_locked(FLAG_BLOCK_TCP)) { + if ((WiFi.getMode() != WIFI_OFF) || !wifi_config.WiFi_on) { + //push UART data to all connected tcp clients + for (i = 0; i < MAX_SRV_CLIENTS; i++) { + if (serverClients[i] && serverClients[i].connected() ) { + serverClients[i].write (sbuf, len); + delay (0); + } + } + } + } +#endif +#ifdef WS_DATA_FEATURE + +#if defined (ASYNCWEBSERVER) + if (!CONFIG::is_locked(FLAG_BLOCK_WSOCKET)) web_interface->web_socket.textAll(sbuf, len); +#else + if (!CONFIG::is_locked(FLAG_BLOCK_WSOCKET) && socket_server)socket_server->sendBIN(current_socket_id,sbuf,len); +#endif + +#endif + //process data if any + COMMAND::read_buffer_serial (sbuf, len); + return true; + } else { + return false; + } +} +#ifdef TCP_IP_DATA_FEATURE +void ESPCOM::processFromTCP2Serial() +{ + uint8_t i, data; + //check if there are any new clients + if (data_server->hasClient() ) { + for (i = 0; i < MAX_SRV_CLIENTS; i++) { + //find free/disconnected spot + if (!serverClients[i] || !serverClients[i].connected() ) { + if (serverClients[i]) { + serverClients[i].stop(); + } + serverClients[i] = data_server->available(); + continue; + } + } + //no free/disconnected spot so reject + WiFiClient serverClient = data_server->available(); + serverClient.stop(); + } + //check clients for data + //to avoid any pollution if Uploading file to SDCard + if (!((web_interface->blockserial) || CONFIG::is_locked(FLAG_BLOCK_TCP) || CONFIG::is_locked(FLAG_BLOCK_SERIAL))) { + for (i = 0; i < MAX_SRV_CLIENTS; i++) { + if (serverClients[i] && serverClients[i].connected() ) { + if (serverClients[i].available() ) { + //get data from the tcp client and push it to the UART + while (serverClients[i].available() ) { + data = serverClients[i].read(); + ESPCOM::write(DEFAULT_PRINTER_PIPE, data); + COMMAND::read_buffer_tcp (data); + } + } + } + } + } +} +#endif diff --git a/src/espcom.h b/src/espcom.h new file mode 100644 index 00000000..32f508da --- /dev/null +++ b/src/espcom.h @@ -0,0 +1,57 @@ +/* + espcom.h - esp3d communication serial/tcp/etc... class + + 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 +*/ + +#ifndef ESPCOM_H +#define ESPCOM_H +#include +#include "config.h" +#ifdef TCP_IP_DATA_FEATURE +extern WiFiServer * data_server; +#endif + +class ESPCOM +{ +public: + static size_t write(tpipe output, uint8_t d); + static long readBytes (tpipe output, uint8_t * sbuf, size_t len); + static long baudRate(tpipe output); + static size_t available(tpipe output); + static void flush(tpipe output, ESPResponseStream *espresponse = NULL); + static void bridge(bool async = false); + static bool processFromSerial (bool async = false); + static void print (const __FlashStringHelper *data, tpipe output, ESPResponseStream *espresponse = NULL); + static void print (String & data, tpipe output, ESPResponseStream *espresponse = NULL); + static void print (const char * data, tpipe output, ESPResponseStream *espresponse = NULL); + static void println (const __FlashStringHelper *data, tpipe output, ESPResponseStream *espresponse = NULL); + static void println (String & data, tpipe output, ESPResponseStream *espresponse = NULL); + static void println (const char * data, tpipe output, ESPResponseStream *espresponse = NULL); + static uint8_t current_socket_id; +#ifdef TCP_IP_DATA_FEATURE + static void processFromTCP2Serial(); + static void send2TCP (const __FlashStringHelper *data, bool async = false); + static void send2TCP (String data, bool async = false); + static void send2TCP (const char * data, bool async = false); +#endif + static bool block_2_printer; +#ifdef ESP_OLED_FEATURE + static bool block_2_oled; +#endif +}; +#endif diff --git a/src/nofile.h b/src/nofile.h new file mode 100644 index 00000000..da8f3356 --- /dev/null +++ b/src/nofile.h @@ -0,0 +1,333 @@ +/* + 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 4862 +#if defined(ASYNCWEBSERVER) +const uint8_t PAGE_NOFILES [] PROGMEM = { +#else +const char PAGE_NOFILES [] PROGMEM = { +#endif + 0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xED, 0x5C, 0x7D, 0x93, 0xDA, 0x46, + 0x93, 0xFF, 0x2A, 0xB2, 0x52, 0x36, 0x70, 0x2B, 0x40, 0x12, 0xAF, 0x8B, 0x16, 0xF2, 0x24, 0xB1, + 0x7D, 0xF1, 0x95, 0x13, 0xBB, 0xBC, 0xEB, 0x7B, 0xAE, 0x2A, 0x4E, 0xB9, 0x84, 0x34, 0x80, 0xCE, + 0x42, 0xD2, 0x49, 0xC3, 0xEE, 0x62, 0xC2, 0x77, 0xBF, 0xEE, 0x79, 0x91, 0x46, 0x42, 0xB0, 0xEC, + 0x26, 0x79, 0xF2, 0xFC, 0x91, 0x60, 0x23, 0x98, 0x99, 0xEE, 0xE9, 0xE9, 0xE9, 0xFE, 0x75, 0x4F, + 0x0F, 0xCE, 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, 0xEF, 0x26, 0xA3, 0x13, 0xCB, 0x34, 0x9F, 0x3B, 0xED, 0x3B, 0x32, 0xFF, 0x12, + 0xD0, 0x23, 0xBD, 0x8C, 0x1D, 0xB6, 0xC2, 0xD7, 0xE4, 0x7E, 0x3F, 0x8F, 0xFD, 0x6D, 0x69, 0x0A, + 0xFD, 0x47, 0x12, 0xDE, 0x12, 0x1A, 0x78, 0xAE, 0xF6, 0x33, 0xD9, 0x10, 0xDD, 0xC8, 0xBF, 0x1B, + 0xDF, 0xA5, 0x81, 0x1B, 0x1A, 0x8A, 0x0C, 0x0A, 0xAF, 0x7E, 0x72, 0xEF, 0x84, 0x41, 0x44, 0xDA, + 0x2B, 0x12, 0x2C, 0x57, 0x30, 0x57, 0xA7, 0x6F, 0x8F, 0x07, 0x23, 0xAB, 0xDF, 0x73, 0xBC, 0x38, + 0x8C, 0xD3, 0xC9, 0x37, 0xBD, 0x5E, 0xCF, 0x99, 0xBB, 0xDE, 0x97, 0x65, 0x1A, 0x6F, 0x22, 0xBF, + 0x2D, 0x5A, 0x17, 0x8B, 0xC5, 0xBE, 0xE3, 0x01, 0x1F, 0x17, 0x88, 0xD3, 0xDD, 0xDA, 0x4D, 0x97, + 0x41, 0xD4, 0x4E, 0x19, 0x0F, 0x77, 0x43, 0x63, 0x47, 0xB4, 0x84, 0x64, 0x21, 0x1A, 0x12, 0xD7, + 0xF7, 0x83, 0x68, 0xC9, 0x5B, 0xAC, 0x01, 0xCC, 0x2B, 0x5B, 0x38, 0x15, 0x36, 0xED, 0xA9, 0x3B, + 0x0F, 0xC9, 0x6E, 0x1E, 0xA7, 0x3E, 0x49, 0x27, 0xA6, 0xC3, 0x3F, 0xB4, 0xB3, 0xC4, 0xF5, 0x60, + 0x20, 0x34, 0xAC, 0xDD, 0xFB, 0xF6, 0x5D, 0xE0, 0xD3, 0x15, 0x53, 0xCA, 0xBE, 0xC3, 0xC6, 0xB7, + 0xF9, 0x30, 0xE2, 0xEF, 0x8A, 0x2E, 0x41, 0x3A, 0xB1, 0x92, 0x7B, 0x2D, 0x8B, 0xC3, 0xC0, 0xD7, + 0xBE, 0xF1, 0x7D, 0x5F, 0x4A, 0x35, 0x8F, 0x29, 0x8D, 0xD7, 0x13, 0x1B, 0x35, 0x49, 0x81, 0x6C, + 0x15, 0x50, 0xC2, 0x66, 0x21, 0x93, 0x28, 0xBE, 0x4B, 0xDD, 0x44, 0xCA, 0x36, 0xB1, 0xD7, 0xEB, + 0x3D, 0x5D, 0xED, 0xD8, 0x9E, 0xB8, 0x61, 0xB0, 0x8C, 0x26, 0x28, 0xBF, 0x98, 0x78, 0x46, 0x71, + 0x1B, 0x66, 0x34, 0x9D, 0x51, 0xDF, 0x38, 0x68, 0x5A, 0xE5, 0x4D, 0xCC, 0x36, 0xCA, 0xA3, 0xF2, + 0xA6, 0xD5, 0x4E, 0x4E, 0x35, 0x3E, 0xBE, 0x15, 0xB7, 0x24, 0xC5, 0x9D, 0x0C, 0x85, 0x08, 0x34, + 0x4E, 0xA4, 0x6A, 0xE0, 0x63, 0x65, 0x8D, 0x55, 0xA5, 0xD4, 0x08, 0x59, 0xD7, 0xB7, 0x3A, 0xEC, + 0x3B, 0x10, 0xBB, 0xAE, 0x6F, 0xB5, 0xAB, 0xD5, 0xF4, 0xA1, 0x14, 0x8F, 0xE2, 0x26, 0x76, 0x48, + 0xEC, 0xB5, 0x0D, 0xDB, 0x24, 0x68, 0x32, 0x9A, 0x06, 0x89, 0x22, 0xF8, 0x24, 0xA2, 0xAB, 0x76, + 0xBC, 0x68, 0xD3, 0x6D, 0x42, 0x9A, 0xB1, 0xEF, 0xB7, 0x76, 0x35, 0xB6, 0x7A, 0x89, 0xAF, 0xFD, + 0x3F, 0xD6, 0xC4, 0x0F, 0x5C, 0xAD, 0xB9, 0x06, 0x03, 0xE0, 0x7C, 0x47, 0x43, 0xD0, 0x79, 0x6B, + 0xA7, 0xD8, 0xB1, 0x68, 0x1F, 0xA0, 0x61, 0xD4, 0x10, 0x5C, 0x5E, 0xDA, 0xB5, 0x04, 0x97, 0xA3, + 0x23, 0x04, 0x96, 0x6D, 0x9A, 0xB5, 0x14, 0x96, 0xC5, 0x49, 0x3A, 0x91, 0x7B, 0xAB, 0x9A, 0xAD, + 0x10, 0xD9, 0xF3, 0xBC, 0x8A, 0xC3, 0x98, 0x55, 0x77, 0x31, 0xC1, 0x58, 0x32, 0x70, 0x63, 0x44, + 0x1C, 0xB0, 0xDA, 0x88, 0xD4, 0x78, 0x29, 0xF3, 0x5D, 0xAE, 0xD0, 0xD4, 0xF5, 0x83, 0x4D, 0x36, + 0x19, 0x82, 0x91, 0xD5, 0x38, 0x81, 0xBB, 0x4B, 0xE2, 0x2C, 0xA0, 0x41, 0x1C, 0x4D, 0x52, 0x12, + 0xBA, 0x34, 0xB8, 0x25, 0x8E, 0x1F, 0x64, 0x49, 0xE8, 0x6E, 0x27, 0xF3, 0x30, 0xF6, 0xBE, 0xE4, + 0x0E, 0x81, 0xE8, 0xA3, 0x31, 0xF7, 0x65, 0x3E, 0xE1, 0x13, 0x2F, 0x4E, 0x5D, 0x46, 0xC8, 0x64, + 0x28, 0xE4, 0xDF, 0x77, 0x5C, 0x0F, 0xF9, 0xEC, 0x0A, 0xC4, 0xA8, 0x91, 0xD0, 0x34, 0x4D, 0x39, + 0x50, 0x73, 0x0D, 0x77, 0xB2, 0x88, 0xBD, 0x4D, 0x06, 0xCF, 0x55, 0x0C, 0x36, 0xBF, 0x53, 0xC1, + 0x26, 0x71, 0x23, 0x12, 0xEE, 0x0E, 0x65, 0xAF, 0x07, 0xA7, 0x23, 0xFE, 0x5F, 0x56, 0x06, 0x82, + 0x9F, 0x44, 0xDD, 0x79, 0x7C, 0xDF, 0xCE, 0x56, 0xAE, 0x1F, 0xDF, 0x4D, 0x4C, 0x0D, 0xA9, 0xF0, + 0x6F, 0xBA, 0x9C, 0xBB, 0x4D, 0xD3, 0xC0, 0x57, 0xC7, 0x1C, 0xB4, 0x9C, 0x73, 0x06, 0x09, 0x49, + 0xDB, 0x0C, 0xA1, 0x73, 0xAD, 0x21, 0xB8, 0x89, 0x0E, 0x34, 0x76, 0x68, 0xDB, 0x1D, 0x6A, 0xF4, + 0x34, 0xE2, 0x0E, 0xF0, 0x25, 0x57, 0x20, 0x1A, 0x95, 0x35, 0x01, 0x12, 0x70, 0xD3, 0x90, 0xAB, + 0xEB, 0xA1, 0x6E, 0x8A, 0x3E, 0x34, 0xA3, 0x9A, 0x2E, 0xA1, 0xC9, 0x8A, 0xF7, 0x86, 0xEE, 0x1C, + 0x94, 0x2D, 0x2D, 0x20, 0x88, 0x18, 0x2E, 0x71, 0x43, 0x28, 0x43, 0x70, 0xC5, 0x98, 0x70, 0x15, + 0x2C, 0xBA, 0xDC, 0x71, 0x0C, 0x1B, 0xE1, 0xF6, 0x32, 0x43, 0x09, 0xA2, 0x45, 0x2C, 0xF7, 0xB3, + 0x07, 0xC6, 0x3F, 0x86, 0x2D, 0x5D, 0xC4, 0xE9, 0xBA, 0x8D, 0x9E, 0x91, 0xC6, 0xC5, 0x64, 0x7C, + 0x16, 0x3E, 0x03, 0x0B, 0x1C, 0x02, 0x0E, 0x7B, 0xFD, 0x22, 0x64, 0xA0, 0x19, 0x6B, 0x96, 0x2D, + 0x27, 0x3B, 0x37, 0x94, 0x0D, 0x06, 0x83, 0x63, 0xD6, 0x52, 0xB4, 0x06, 0x6B, 0x77, 0x29, 0x1D, + 0xEA, 0xC0, 0x86, 0xD0, 0x2F, 0xCF, 0xB2, 0xA1, 0x20, 0xCA, 0x08, 0xD5, 0x8E, 0x18, 0xC9, 0xA8, + 0x6C, 0x4A, 0x0F, 0x8E, 0x6D, 0xC7, 0x6D, 0x9A, 0x42, 0xF8, 0xE6, 0x0E, 0xAA, 0x5A, 0x80, 0x46, + 0xDC, 0x8C, 0x80, 0x6E, 0xDB, 0xF1, 0x86, 0x6A, 0x1D, 0x6B, 0x90, 0x19, 0x05, 0xDF, 0x83, 0xBE, + 0xB2, 0xC2, 0xB9, 0xAB, 0xED, 0xCA, 0xF6, 0x34, 0x1C, 0xBA, 0x0B, 0x72, 0xE9, 0x00, 0x05, 0x6A, + 0x12, 0x02, 0xEE, 0x13, 0x96, 0x66, 0x98, 0xD0, 0x39, 0x96, 0x1D, 0x96, 0x69, 0x1B, 0xD6, 0x68, + 0x60, 0xD8, 0xBD, 0x9E, 0xD1, 0x19, 0xB6, 0x84, 0x0C, 0xA8, 0xEB, 0xA4, 0xE2, 0xCC, 0xDC, 0x47, + 0xE6, 0x34, 0x3A, 0x66, 0x77, 0xEA, 0x60, 0xB3, 0x64, 0x66, 0x7D, 0xD3, 0x74, 0x94, 0x10, 0xED, + 0x91, 0x88, 0x92, 0xB4, 0x1A, 0x35, 0xD7, 0x81, 0xEF, 0x87, 0x84, 0x27, 0x60, 0xF1, 0xC6, 0x5B, + 0xB5, 0x11, 0x76, 0x40, 0x9F, 0x6B, 0x37, 0x0A, 0x92, 0x4D, 0xC8, 0x40, 0xCC, 0x39, 0xDE, 0xE3, + 0x6D, 0xD2, 0x0C, 0x54, 0x94, 0xC4, 0x01, 0x63, 0x7E, 0xA6, 0xC5, 0xB0, 0x7D, 0x4B, 0xDC, 0x14, + 0x24, 0x72, 0x4E, 0xA4, 0x19, 0x8F, 0xB4, 0xE7, 0x1A, 0x13, 0x5C, 0xC7, 0x5F, 0xDB, 0x9B, 0x0C, + 0x93, 0x25, 0x12, 0x12, 0x8F, 0x72, 0x71, 0x70, 0xAD, 0x07, 0x8D, 0xD5, 0x06, 0xA6, 0xF3, 0x76, + 0x92, 0xC2, 0x32, 0xD2, 0xED, 0x69, 0xB4, 0xEE, 0xF5, 0x46, 0xEE, 0x7C, 0x54, 0xC1, 0x20, 0x9B, + 0x0C, 0x7D, 0xB7, 0x5F, 0xE2, 0x22, 0x10, 0xDD, 0x28, 0xB5, 0x71, 0x68, 0x2F, 0x35, 0x31, 0x94, + 0x2F, 0x35, 0x4D, 0x6A, 0x28, 0x27, 0x87, 0x94, 0x07, 0xF1, 0xA1, 0x46, 0x58, 0x7B, 0x3C, 0x34, + 0x2F, 0xCD, 0x8A, 0xB0, 0x96, 0x6D, 0xCF, 0xFB, 0xE6, 0xDE, 0x73, 0x13, 0xDC, 0x54, 0x89, 0xC1, + 0x2C, 0x8D, 0x1A, 0x2B, 0x29, 0xA9, 0xB0, 0xB2, 0x71, 0x01, 0xCA, 0xA3, 0xD1, 0xC8, 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, 0x57, 0x23, + 0xC1, 0x90, 0x5C, 0x7A, 0xC3, 0x3C, 0x84, 0x78, 0xA3, 0x61, 0xCF, 0x97, 0xAC, 0x7C, 0x37, 0x5A, + 0x82, 0xB6, 0x6A, 0xA0, 0xCF, 0xF6, 0x89, 0x4F, 0x2A, 0x9C, 0xC8, 0xDC, 0xF3, 0x7C, 0x4B, 0x72, + 0x72, 0x2F, 0xFB, 0xFD, 0xBE, 0xBD, 0xEF, 0xAC, 0xDC, 0xAC, 0x4D, 0xD2, 0x14, 0x20, 0xA7, 0x0C, + 0xDB, 0x65, 0x5A, 0x3E, 0xFA, 0xCF, 0x06, 0xC4, 0xA3, 0xD2, 0xD4, 0x62, 0xDA, 0xB8, 0xDF, 0x1B, + 0xF4, 0xFA, 0x4F, 0x46, 0x32, 0x74, 0xCD, 0x6F, 0x3C, 0x32, 0xEE, 0x8F, 0x7B, 0x8F, 0x91, 0xB1, + 0x4A, 0x5B, 0x92, 0x59, 0x88, 0xDB, 0xE6, 0x61, 0xB6, 0x46, 0xD3, 0x62, 0xF3, 0x4F, 0xEA, 0x9A, + 0xEF, 0xF1, 0xBF, 0x46, 0xD7, 0xB5, 0xF2, 0xD4, 0x6A, 0xDB, 0x9E, 0x0F, 0xFA, 0xB6, 0xF7, 0xFB, + 0xB4, 0x3D, 0x1C, 0xCD, 0xAD, 0xE1, 0xF8, 0x69, 0xDA, 0xE6, 0xB4, 0x15, 0xA9, 0x6B, 0xF5, 0x2D, + 0x7D, 0x04, 0x61, 0x45, 0x78, 0xC8, 0x49, 0x3C, 0xF1, 0x2F, 0xC1, 0x8C, 0x16, 0x55, 0xB7, 0xEB, + 0xF7, 0x16, 0x3D, 0x57, 0x65, 0x52, 0xC2, 0x3E, 0xD1, 0xA4, 0x00, 0x98, 0x68, 0x51, 0x90, 0x8F, + 0xB7, 0x4C, 0x0E, 0xC9, 0x26, 0x07, 0x64, 0xE7, 0xC0, 0x9E, 0x77, 0xD9, 0x33, 0x6D, 0xAF, 0x22, + 0xE6, 0x68, 0x68, 0x79, 0xD6, 0x25, 0x13, 0x33, 0x58, 0x2F, 0x77, 0x22, 0x96, 0xAD, 0xDC, 0xA8, + 0x9A, 0x12, 0x0F, 0xEB, 0xF0, 0x8A, 0x27, 0xE0, 0x9C, 0x56, 0x88, 0x50, 0x83, 0x25, 0x26, 0xBE, + 0x2A, 0xF3, 0x9A, 0x20, 0xE2, 0x5F, 0xEE, 0x78, 0x20, 0x38, 0x93, 0xF4, 0xF4, 0xCA, 0x7B, 0xA6, + 0x48, 0x3F, 0xE4, 0xD8, 0x87, 0x56, 0xFA, 0xD7, 0xAF, 0x2B, 0x04, 0xD1, 0x20, 0x43, 0xF8, 0x22, + 0x0D, 0x82, 0x1D, 0xA6, 0xF2, 0xD6, 0x89, 0xB0, 0xB1, 0x45, 0x10, 0x12, 0xF6, 0x9D, 0xBB, 0x6B, + 0x3E, 0xF6, 0xB2, 0x0F, 0xBB, 0x1A, 0x44, 0xC9, 0x86, 0xFE, 0x82, 0xA7, 0xE7, 0x29, 0x8E, 0xFB, + 0x75, 0x32, 0x91, 0xCB, 0xC2, 0xAF, 0xED, 0x4D, 0x12, 0xC6, 0xAE, 0xDF, 0x9E, 0x6F, 0x20, 0x9A, + 0xFD, 0x9D, 0x97, 0xFD, 0x6B, 0xF3, 0x32, 0xE7, 0xA4, 0x9B, 0x0F, 0xE6, 0x9E, 0x79, 0x10, 0xBA, + 0xFB, 0xC3, 0xF9, 0xD8, 0x77, 0x1F, 0xB5, 0xA9, 0xC2, 0x2A, 0xFE, 0xDE, 0xDA, 0x7F, 0x9F, 0xAD, + 0xED, 0x59, 0x73, 0xD3, 0xAF, 0x9E, 0xF4, 0xAD, 0xF9, 0xD0, 0x1F, 0x0F, 0x1E, 0xB7, 0xB5, 0x1C, + 0xC0, 0xFE, 0xDE, 0xDA, 0x7F, 0xF3, 0xAD, 0xB5, 0x87, 0x97, 0xEE, 0xDC, 0xDB, 0xE7, 0x40, 0x5D, + 0x82, 0xF3, 0x32, 0x7A, 0x2B, 0x68, 0x5E, 0x4A, 0x05, 0x04, 0x9A, 0x8B, 0x0A, 0xD3, 0x22, 0x8E, + 0x41, 0xA9, 0x27, 0x0A, 0x4C, 0xAC, 0xFE, 0xF2, 0xB4, 0x1A, 0xD3, 0x41, 0x9D, 0x17, 0x0D, 0x0E, + 0xC3, 0x24, 0xDF, 0xAB, 0xBE, 0x92, 0x34, 0xF4, 0xF0, 0xA5, 0x92, 0x2A, 0x9D, 0xBD, 0xFE, 0xE5, + 0xD8, 0x9F, 0x57, 0x54, 0x3F, 0x30, 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, 0x7A, 0x43, 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, 0x7E, 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, 0xA1, 0x94, 0x5E, 0x18, 0x4D, 0xBF, 0x33, 0x20, + 0xEB, 0xC7, 0x2F, 0xE5, 0x50, 0x9C, 0xDF, 0xB9, 0xDB, 0x27, 0xEE, 0x6D, 0xCA, 0xD6, 0xC8, 0xFB, + 0x06, 0x63, 0xF5, 0x2A, 0x26, 0xF3, 0x52, 0x42, 0x22, 0x0D, 0xB2, 0x7D, 0xA0, 0xCF, 0x0B, 0xD7, + 0xA3, 0xE1, 0xE8, 0x28, 0x3D, 0xBB, 0x57, 0xDC, 0x5F, 0x75, 0xF9, 0x4D, 0xEE, 0x55, 0x97, 0xDF, + 0xEB, 0xB2, 0xDB, 0xA6, 0x2B, 0x3F, 0xB8, 0xD5, 0x58, 0xFB, 0x54, 0xCF, 0x4D, 0xC8, 0x9D, 0xC3, + 0x62, 0x37, 0x94, 0x08, 0xE7, 0xE3, 0x97, 0x33, 0xA6, 0x3E, 0xFB, 0x6F, 0xAB, 0x63, 0x6B, 0x2F, + 0xA2, 0x79, 0x96, 0x38, 0xFC, 0xFD, 0xAA, 0x0B, 0xE4, 0xB3, 0x2B, 0x1E, 0x4D, 0x67, 0x57, 0x2B, + 0x7B, 0xF6, 0x86, 0x6A, 0x19, 0x21, 0xEB, 0x4C, 0xDB, 0xC6, 0x1B, 0xCD, 0x8F, 0xB5, 0x28, 0xA6, + 0xDA, 0xCA, 0xC5, 0x8B, 0x90, 0x68, 0xAB, 0x31, 0x87, 0xEF, 0xE0, 0x4D, 0xB2, 0x16, 0x91, 0x80, + 0xAE, 0x48, 0xAA, 0x34, 0x75, 0x96, 0x5F, 0x0D, 0x2D, 0x09, 0xB1, 0xC0, 0xAB, 0xF1, 0x90, 0xAF, + 0x05, 0x54, 0x8B, 0x53, 0xF8, 0xE2, 0x03, 0x9C, 0x21, 0xC3, 0x54, 0x5B, 0x04, 0xE9, 0xFA, 0x0E, + 0x62, 0xA5, 0x16, 0x2C, 0x80, 0x05, 0x1E, 0x84, 0xB1, 0xE4, 0x06, 0x2B, 0xB2, 0x67, 0x38, 0xA1, + 0xE7, 0x46, 0x30, 0x04, 0x14, 0x03, 0x78, 0xA3, 0x01, 0x7B, 0xA2, 0x4D, 0xB4, 0x2B, 0x57, 0xF3, + 0x42, 0x37, 0xCB, 0xA6, 0x7A, 0x7E, 0x8A, 0xD0, 0xB5, 0x55, 0x4A, 0x16, 0x53, 0x7D, 0x45, 0x69, + 0x92, 0x4D, 0xBA, 0xDD, 0x25, 0xC8, 0xB2, 0x99, 0xC3, 0x89, 0x7A, 0xDD, 0x0D, 0x37, 0x5E, 0x9B, + 0x7F, 0xED, 0xBE, 0xBA, 0x7E, 0xDF, 0x7B, 0xD9, 0xFE, 0xE7, 0xAB, 0xEF, 0x3F, 0xBE, 0xD1, 0x67, + 0x67, 0x0F, 0xBD, 0xEA, 0xBA, 0xA0, 0x61, 0xA9, 0x11, 0xD4, 0xAE, 0x98, 0x9D, 0x81, 0xB0, 0xAE, + 0x05, 0xFE, 0x54, 0xBF, 0x7E, 0xFF, 0xE6, 0xF5, 0xEB, 0x6B, 0xFD, 0xB0, 0x5B, 0xDE, 0xA3, 0xE8, + 0xB3, 0xD7, 0xD0, 0xBA, 0xD2, 0x5E, 0x43, 0x60, 0xCC, 0xB6, 0x19, 0x25, 0x6B, 0xA1, 0xE9, 0x03, + 0x02, 0xDC, 0x44, 0x60, 0xC4, 0x52, 0x28, 0x8D, 0xA5, 0x50, 0x3A, 0x46, 0x53, 0x3E, 0x0F, 0x4B, + 0x9F, 0x78, 0x1C, 0xD7, 0xB5, 0x08, 0xC2, 0xC8, 0x54, 0x5F, 0x6F, 0xB1, 0x31, 0xFB, 0xE5, 0x57, + 0x5D, 0x5B, 0x6F, 0x42, 0x1A, 0x24, 0xB8, 0xF1, 0xF2, 0x93, 0x3E, 0xD3, 0x04, 0x27, 0xA9, 0x31, + 0x1A, 0x69, 0x4A, 0x85, 0x52, 0x17, 0x33, 0xF0, 0x54, 0x8C, 0xCF, 0x51, 0xCA, 0xCE, 0x74, 0x50, + 0xBC, 0x17, 0x06, 0xDE, 0x17, 0x58, 0x23, 0x89, 0x7C, 0x9C, 0xAA, 0xD9, 0x72, 0x74, 0xED, 0xD6, + 0x0D, 0x37, 0x40, 0xF7, 0x91, 0x8D, 0xD5, 0x67, 0x25, 0x13, 0x4A, 0xD2, 0x78, 0x99, 0x62, 0x45, + 0x43, 0x58, 0xE1, 0x6D, 0x90, 0x05, 0xF3, 0x20, 0x0C, 0xE8, 0x76, 0xB2, 0x82, 0x7C, 0x8C, 0x44, + 0x52, 0xF4, 0x24, 0x5D, 0xF2, 0x29, 0xD9, 0x07, 0xB0, 0xFC, 0xA9, 0x0E, 0x86, 0x0D, 0x8B, 0xEF, + 0x4A, 0x16, 0x60, 0xD3, 0x29, 0xFF, 0x7B, 0xA0, 0xF7, 0xE3, 0xAA, 0xE3, 0x97, 0xD7, 0x57, 0x14, + 0xA8, 0xA8, 0xAF, 0x31, 0x87, 0x99, 0xEA, 0xE6, 0xF3, 0x5C, 0xA9, 0xE7, 0xA9, 0xA2, 0xB4, 0xEE, + 0x1F, 0xE2, 0x35, 0x24, 0x86, 0x7E, 0xB3, 0x81, 0xB7, 0x99, 0x0D, 0xA3, 0xE1, 0x86, 0x61, 0x43, + 0x51, 0xC3, 0x07, 0xB2, 0x00, 0x69, 0x57, 0x28, 0x39, 0xF5, 0x0F, 0x66, 0x45, 0x39, 0x73, 0x6E, + 0x3F, 0xA4, 0x04, 0x6C, 0xDF, 0x0F, 0xD2, 0x66, 0x4B, 0x57, 0x24, 0x81, 0x93, 0x3C, 0x8C, 0xCC, + 0x6E, 0x97, 0x92, 0xB2, 0x6F, 0x82, 0x4D, 0x33, 0x8C, 0xE3, 0x9F, 0x6F, 0x03, 0x72, 0xF7, 0x7D, + 0x0C, 0x1A, 0xC2, 0x03, 0x76, 0x1F, 0xFF, 0xC0, 0xF8, 0x14, 0xEC, 0x40, 0x83, 0xB6, 0x81, 0xAE, + 0x6D, 0x51, 0x77, 0xBA, 0xA4, 0xEE, 0x29, 0xD4, 0x36, 0x7C, 0x4E, 0x61, 0x90, 0x0D, 0x8F, 0x2D, + 0x7B, 0xC0, 0x2E, 0x86, 0x53, 0x5D, 0xA4, 0x79, 0x7A, 0xB7, 0xE0, 0x83, 0x43, 0xB7, 0x8C, 0x9D, + 0xE0, 0x63, 0x0D, 0x0A, 0x3E, 0xF8, 0xF9, 0x01, 0x3E, 0x98, 0x8F, 0x23, 0x1F, 0x8B, 0x0B, 0x64, + 0xC3, 0x23, 0x4F, 0x6E, 0xA1, 0x75, 0x2C, 0xBE, 0xDE, 0x09, 0x8E, 0x63, 0xD8, 0x6C, 0xC1, 0x84, + 0xE5, 0xC9, 0xFA, 0xEC, 0x02, 0x14, 0x08, 0x3C, 0x40, 0x8F, 0xA0, 0x8A, 0x99, 0x70, 0x11, 0xA1, + 0x53, 0xAE, 0x48, 0x34, 0x17, 0x9E, 0xCC, 0xE5, 0xEA, 0x13, 0x5F, 0x2B, 0xC3, 0xF3, 0x25, 0x98, + 0xF9, 0x26, 0x30, 0x53, 0x73, 0xE9, 0x2A, 0xA7, 0xC4, 0xEB, 0x3E, 0x69, 0xBC, 0x2A, 0x75, 0x17, + 0x6D, 0xA7, 0x2B, 0xED, 0x08, 0x1F, 0x92, 0x82, 0x7F, 0x29, 0xDD, 0xF2, 0xEB, 0xD2, 0xCE, 0x8B, + 0x83, 0x00, 0x1A, 0x21, 0x87, 0x64, 0x66, 0x84, 0x2B, 0xD5, 0x1C, 0x6E, 0xC0, 0xCA, 0x80, 0xF7, + 0x0A, 0xDB, 0x67, 0x3F, 0x83, 0x1F, 0xE4, 0x5F, 0xAE, 0x41, 0x4B, 0xF2, 0x4B, 0xC9, 0x80, 0x2A, + 0x6D, 0x62, 0x45, 0xAC, 0x55, 0x48, 0x2A, 0x26, 0x43, 0x07, 0xC8, 0x71, 0xE2, 0x33, 0xDA, 0x2A, + 0x1B, 0xC7, 0xE3, 0x82, 0x5C, 0xCF, 0x11, 0xDC, 0xE1, 0x91, 0x96, 0xFB, 0x63, 0x06, 0xE9, 0xE6, + 0x26, 0x2B, 0x34, 0x7A, 0xF0, 0x7E, 0x8E, 0x37, 0x16, 0xC8, 0x27, 0x21, 0xFE, 0x23, 0xC3, 0xFD, + 0x07, 0x80, 0xAF, 0xE4, 0xBD, 0x47, 0x71, 0xF0, 0xEE, 0x28, 0x0A, 0x2A, 0xF6, 0xF2, 0x34, 0xE4, + 0x03, 0xDE, 0x07, 0x18, 0xC0, 0x71, 0xEE, 0x10, 0xFD, 0x70, 0x3D, 0xEA, 0x8C, 0x8F, 0x81, 0xBE, + 0xC5, 0x5D, 0x0E, 0x7E, 0xF8, 0xB1, 0x1E, 0xFE, 0x72, 0xCE, 0x70, 0x84, 0x8C, 0xD8, 0xF0, 0x75, + 0xB6, 0xD4, 0x8F, 0xB3, 0x9F, 0x7D, 0x20, 0xB0, 0x79, 0x70, 0x06, 0x8E, 0x96, 0x79, 0xEC, 0xBD, + 0x73, 0x03, 0xDA, 0x81, 0xFF, 0xC0, 0xA9, 0x80, 0x89, 0xC2, 0xCA, 0x83, 0x1C, 0x89, 0x72, 0xCF, + 0xE1, 0x3D, 0x87, 0xC6, 0x5F, 0xDD, 0x74, 0xEE, 0x7E, 0x90, 0x85, 0x26, 0x70, 0xFE, 0xCD, 0xFD, + 0x88, 0xA5, 0x28, 0x65, 0x1B, 0x28, 0x65, 0x2D, 0x75, 0x5D, 0x3C, 0x2D, 0x85, 0x9E, 0x55, 0x6F, + 0xF6, 0x06, 0x44, 0xA7, 0xC1, 0x02, 0x0E, 0xEE, 0x98, 0xAD, 0x40, 0xF0, 0xEF, 0xD5, 0x18, 0x5A, + 0x91, 0x2E, 0xEA, 0x7C, 0x0D, 0x62, 0x25, 0xA5, 0x6E, 0x44, 0x0F, 0x9D, 0x9F, 0xC0, 0x21, 0x69, + 0x26, 0xB3, 0x8F, 0x70, 0xF0, 0x9D, 0x88, 0xE5, 0x55, 0x42, 0xA1, 0x7A, 0x49, 0x20, 0xAD, 0x81, + 0x93, 0xE7, 0x8B, 0xFC, 0x8C, 0xE7, 0xE6, 0xCF, 0xBC, 0x51, 0xA8, 0xBC, 0xB8, 0xF2, 0x2F, 0xD4, + 0xB6, 0x4A, 0xCF, 0x17, 0xE8, 0x3D, 0xF4, 0xDD, 0x01, 0x50, 0x3C, 0x42, 0xA8, 0x44, 0x90, 0xA8, + 0x82, 0xC9, 0xB6, 0x87, 0x85, 0xC3, 0xE0, 0x79, 0x44, 0x97, 0xC2, 0xE5, 0xCB, 0x4E, 0x26, 0x6D, + 0xFF, 0x98, 0xDB, 0x14, 0x71, 0x71, 0x33, 0x5F, 0x07, 0xF4, 0x03, 0xF9, 0xBF, 0x0D, 0x98, 0x1C, + 0x46, 0x33, 0xE1, 0x15, 0xBC, 0xBD, 0x16, 0x3C, 0x20, 0xD1, 0x0D, 0x12, 0x3A, 0x5B, 0x6C, 0x22, + 0x56, 0x6C, 0x01, 0x5F, 0xB8, 0x9D, 0xBB, 0x10, 0x09, 0x77, 0xB7, 0x70, 0x46, 0x06, 0x52, 0xC5, + 0xF9, 0x75, 0x83, 0x4E, 0xBD, 0x4D, 0x8A, 0x45, 0x14, 0x84, 0xEC, 0x0E, 0x1C, 0x3B, 0x03, 0xDA, + 0xD4, 0xBB, 0x7A, 0xCB, 0x88, 0xA6, 0xF0, 0x30, 0x82, 0xA9, 0xE5, 0x80, 0xB6, 0x9A, 0xE4, 0x02, + 0xE9, 0x7C, 0x21, 0x6F, 0x83, 0xC7, 0xD2, 0x86, 0x96, 0xCB, 0xF9, 0x49, 0x57, 0xD8, 0x4C, 0x1B, + 0xDD, 0x86, 0xA3, 0x1D, 0x8F, 0xE8, 0x9F, 0xF4, 0x59, 0x97, 0x79, 0x81, 0xEE, 0x04, 0x57, 0xB4, + 0x13, 0x92, 0x68, 0x49, 0x57, 0x6D, 0xCB, 0x69, 0x45, 0x17, 0x53, 0xFA, 0x4B, 0xF0, 0xEB, 0x05, + 0xCE, 0x7C, 0x64, 0xC6, 0x23, 0x13, 0xEA, 0x17, 0xD1, 0x85, 0xFE, 0xD0, 0xA4, 0xFA, 0x05, 0xE7, + 0x9E, 0xFB, 0xBB, 0x90, 0xC2, 0x08, 0x2E, 0x2E, 0x9C, 0x94, 0xD0, 0x4D, 0x1A, 0x69, 0x6C, 0x5A, + 0xD5, 0x39, 0xF5, 0x7D, 0xAE, 0x48, 0xB0, 0xAF, 0x6C, 0xF5, 0x39, 0x00, 0xC3, 0x51, 0x94, 0x59, + 0x64, 0x13, 0x0D, 0xBB, 0xDF, 0x90, 0x71, 0x9C, 0x7D, 0x96, 0xD9, 0x44, 0x03, 0xB3, 0x09, 0xCB, + 0x1E, 0xE3, 0xDF, 0x06, 0x2C, 0x5A, 0x9D, 0x4A, 0x24, 0x05, 0x8D, 0x81, 0xDD, 0x80, 0x60, 0xDE, + 0xB0, 0xE0, 0x01, 0xE1, 0xBF, 0x31, 0x6C, 0x60, 0xF8, 0xC7, 0x87, 0xE4, 0x3D, 0x28, 0x78, 0x8F, + 0x1A, 0xC2, 0x14, 0x1B, 0x18, 0xD6, 0xE1, 0xE4, 0xEA, 0x3B, 0x0D, 0xAD, 0x3B, 0x13, 0x3A, 0xAB, + 0x72, 0xAC, 0xE7, 0x61, 0x97, 0x79, 0xB0, 0xCC, 0xA0, 0x8E, 0x4B, 0xCF, 0xE4, 0x5C, 0xC6, 0x47, + 0xE4, 0x1A, 0x8E, 0x0A, 0x9E, 0x80, 0xAB, 0x67, 0x49, 0x66, 0x97, 0x79, 0x5A, 0x26, 0x67, 0x8A, + 0x4F, 0xC1, 0x75, 0xAC, 0x72, 0xED, 0x3F, 0x86, 0xA9, 0x7D, 0x59, 0xCB, 0xA4, 0x77, 0xE6, 0x72, + 0xFB, 0x9C, 0x4B, 0xBF, 0xC7, 0x45, 0x1B, 0x71, 0xC9, 0x46, 0x39, 0x4F, 0x85, 0xE5, 0xF0, 0x5C, + 0x9E, 0xC3, 0x3F, 0x81, 0xE7, 0xF8, 0x8F, 0xE0, 0xC9, 0xF3, 0x3F, 0xC5, 0xC0, 0xF1, 0x4C, 0x2F, + 0xED, 0x9B, 0x1B, 0xE9, 0xF9, 0xF6, 0x6D, 0xF7, 0xE1, 0x4F, 0x03, 0x82, 0x33, 0xF8, 0xA3, 0xE6, + 0x4F, 0x1B, 0x3F, 0x8D, 0x8C, 0x9E, 0xF6, 0xD6, 0x36, 0xC6, 0xDA, 0xDB, 0x91, 0x61, 0xF5, 0xD8, + 0xBB, 0xA9, 0xBD, 0xB5, 0xC4, 0x63, 0x6C, 0x58, 0x16, 0x7F, 0x0C, 0x78, 0xE3, 0x10, 0x1E, 0x26, + 0x7B, 0x5C, 0x1A, 0xD6, 0x88, 0xBD, 0x5F, 0xB2, 0x26, 0x1B, 0x86, 0xDB, 0xE2, 0x61, 0x1B, 0xD6, + 0x98, 0x3D, 0xC6, 0xAC, 0x6D, 0x88, 0x5C, 0x87, 0xDA, 0x57, 0x5C, 0x60, 0x1A, 0x7F, 0x81, 0x15, + 0xB2, 0xB3, 0x6A, 0x83, 0xA7, 0xBB, 0x0D, 0xB6, 0xD2, 0xDA, 0x85, 0xF2, 0xB4, 0xE6, 0x33, 0x1E, + 0x10, 0x48, 0x6B, 0xA7, 0x20, 0xC9, 0xC5, 0x94, 0x30, 0xF4, 0x51, 0x71, 0x44, 0x67, 0x29, 0x9E, + 0xA1, 0x03, 0x8E, 0xE8, 0xAD, 0x82, 0x07, 0x9C, 0x65, 0xB1, 0xFC, 0x7C, 0x0D, 0xF9, 0x69, 0xB4, + 0xCC, 0x9A, 0xC4, 0xA0, 0x52, 0x69, 0x80, 0x0B, 0xA4, 0x43, 0xE3, 0xB7, 0xF1, 0x1D, 0x49, 0x7F, + 0x80, 0xDC, 0xA0, 0xD9, 0x02, 0x98, 0xA5, 0x95, 0x16, 0x72, 0x45, 0xBF, 0x6D, 0x5B, 0x13, 0x32, + 0xA3, 0xDF, 0x5A, 0x13, 0xB3, 0x60, 0x8B, 0xB5, 0x3F, 0x97, 0x7A, 0x2B, 0x96, 0x69, 0xB1, 0x0C, + 0x11, 0x45, 0x44, 0xB4, 0x81, 0x5C, 0x1E, 0x00, 0x73, 0xD1, 0x84, 0xA7, 0x7A, 0x12, 0xBC, 0x66, + 0x83, 0x26, 0x9A, 0x7E, 0x41, 0x3A, 0x9C, 0xC0, 0xA0, 0x17, 0xE5, 0x21, 0xBF, 0xA9, 0x5F, 0x6E, + 0x62, 0xEA, 0x86, 0x1A, 0x2F, 0x96, 0x33, 0x22, 0x8A, 0x0D, 0xA7, 0x69, 0x20, 0xC0, 0xFB, 0x2A, + 0x09, 0x44, 0x6C, 0xFF, 0x34, 0xC5, 0x3B, 0xCF, 0xDB, 0x24, 0xBC, 0x4A, 0xAB, 0xE9, 0x6C, 0xE8, + 0xD5, 0x9A, 0x40, 0x1C, 0xD4, 0xD6, 0x41, 0x04, 0x06, 0xD3, 0x60, 0x99, 0x18, 0x47, 0x8C, 0x15, + 0x58, 0xD4, 0xB4, 0x71, 0x09, 0x9F, 0x78, 0x6C, 0x6B, 0xE0, 0x0C, 0x71, 0x4E, 0x0F, 0xB8, 0x0E, + 0x1B, 0xC8, 0x88, 0xC5, 0x21, 0xA2, 0xDA, 0xFF, 0x5C, 0x37, 0xFC, 0xD8, 0xDB, 0xAC, 0x61, 0x0F, + 0x3B, 0x4B, 0x42, 0x5F, 0x85, 0x04, 0x3F, 0x7E, 0xBF, 0x7D, 0x03, 0x7B, 0x27, 0x92, 0xEC, 0x56, + 0x27, 0x88, 0x22, 0x92, 0xFE, 0x78, 0xF3, 0xD3, 0xDB, 0x29, 0x35, 0x50, 0x93, 0x06, 0x6C, 0xF3, + 0x33, 0x35, 0xF8, 0x71, 0x25, 0x47, 0xA5, 0x78, 0x08, 0xB1, 0x87, 0xBE, 0xC1, 0x52, 0xCB, 0xBB, + 0x05, 0x46, 0x45, 0xA3, 0xD4, 0xC7, 0xC3, 0x96, 0xDD, 0x72, 0xD8, 0xEA, 0x68, 0x2A, 0xBD, 0x4C, + 0xBD, 0x57, 0x3E, 0x11, 0xB0, 0x4A, 0x71, 0x17, 0x86, 0x90, 0xA6, 0x69, 0x44, 0x17, 0x56, 0xEB, + 0xE1, 0x38, 0x86, 0x61, 0x11, 0x82, 0x99, 0xE2, 0xAD, 0x45, 0x4C, 0x03, 0xB3, 0x0C, 0x31, 0x25, + 0x01, 0x6C, 0x68, 0xCC, 0xE0, 0x2C, 0x80, 0x09, 0xA9, 0xCC, 0x37, 0xF5, 0x3D, 0x61, 0x77, 0x06, + 0x59, 0x07, 0xE4, 0xA3, 0x4D, 0x69, 0x72, 0x25, 0xD3, 0xAD, 0x5A, 0x75, 0x07, 0x33, 0x69, 0x83, + 0xB2, 0x47, 0x6B, 0xDF, 0x62, 0xC9, 0x00, 0xEA, 0x29, 0x98, 0x9A, 0x10, 0xBC, 0x25, 0x3F, 0xAE, + 0x0B, 0x07, 0xE2, 0x68, 0x4B, 0x6F, 0x5B, 0xA0, 0x57, 0x4E, 0xDF, 0x14, 0xFD, 0x10, 0x75, 0x3B, + 0x78, 0x3A, 0x6D, 0xBD, 0x78, 0xD1, 0x64, 0xCA, 0xBA, 0xF9, 0x30, 0x13, 0x46, 0xC1, 0xB2, 0x6E, + 0x80, 0x18, 0x15, 0x57, 0x14, 0xB8, 0xA9, 0x81, 0x18, 0x6D, 0xF6, 0x89, 0x16, 0x28, 0x63, 0x19, + 0x36, 0xA0, 0x84, 0x61, 0x5B, 0x88, 0x35, 0x36, 0x7E, 0x1E, 0xF2, 0xC7, 0x88, 0xB5, 0x59, 0x88, + 0x0F, 0x6F, 0x2D, 0x5B, 0xBC, 0x5B, 0x1A, 0x0E, 0xB3, 0xCE, 0x40, 0x0C, 0xBC, 0x52, 0xD0, 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, 0xBA, 0x72, 0x89, 0x39, 0xAB, 0x81, 0xE0, 0x64, + 0x0A, 0x56, 0x3D, 0xCE, 0xCA, 0x32, 0xCF, 0xE0, 0x05, 0x6B, 0x3E, 0xE0, 0xD3, 0xAF, 0xF0, 0xE9, + 0x3F, 0x91, 0xCF, 0xB8, 0xC2, 0x67, 0x7C, 0x06, 0x1F, 0x59, 0x3B, 0x60, 0xF9, 0x13, 0x2C, 0xB3, + 0x71, 0x75, 0xF3, 0x52, 0xE4, 0x6A, 0x9F, 0x44, 0xB2, 0xF6, 0xA9, 0x91, 0x57, 0x48, 0x65, 0x19, + 0x3D, 0xB9, 0x77, 0xC0, 0x7C, 0x5D, 0x51, 0x47, 0x6C, 0x80, 0x1F, 0x33, 0xD0, 0x55, 0xCC, 0x04, + 0xED, 0xEC, 0xA2, 0x01, 0x49, 0xB9, 0x9B, 0x82, 0x2B, 0x4F, 0x3F, 0x83, 0x00, 0xD1, 0x97, 0x52, + 0x56, 0x5D, 0x94, 0x23, 0x67, 0x0D, 0x9C, 0xB9, 0x42, 0xCC, 0x75, 0x2E, 0xF2, 0x61, 0xAC, 0x29, + 0xDE, 0xBC, 0x9C, 0x81, 0x6C, 0x5C, 0xCA, 0x8A, 0x3D, 0x8A, 0xB1, 0x7C, 0x84, 0x34, 0x3B, 0xF3, + 0x79, 0x43, 0x9D, 0xEF, 0x93, 0x28, 0x1C, 0x7D, 0xD2, 0x15, 0x57, 0x7E, 0x09, 0xF1, 0x83, 0x92, + 0x26, 0x43, 0xAA, 0xB2, 0xE8, 0x7A, 0xA3, 0x85, 0x99, 0x26, 0x72, 0x56, 0xD3, 0xC5, 0x92, 0x54, + 0x38, 0x1F, 0xF7, 0x49, 0x7C, 0x43, 0x5F, 0x28, 0xFC, 0x2A, 0x04, 0xBF, 0x0A, 0xAB, 0x7E, 0x15, + 0x0A, 0xBF, 0x9A, 0x56, 0xFD, 0x2A, 0xFC, 0x43, 0xFD, 0x4A, 0xF1, 0xAA, 0x4B, 0x1E, 0x9E, 0x2F, + 0x31, 0xD0, 0x42, 0x90, 0x86, 0x78, 0x2C, 0xDE, 0x06, 0x18, 0x72, 0xFB, 0xE8, 0x45, 0x7D, 0xF4, + 0xBB, 0x01, 0x73, 0x3E, 0x9B, 0x0D, 0xC5, 0x07, 0x06, 0x6A, 0x74, 0xC5, 0x1E, 0xA3, 0x1F, 0xB0, + 0x77, 0x9B, 0x7B, 0x22, 0xF4, 0x9F, 0x17, 0xA7, 0x0B, 0xA3, 0xD2, 0x71, 0x5B, 0xCA, 0x27, 0x00, + 0x2D, 0xDF, 0xFE, 0x3C, 0xAB, 0x39, 0xBC, 0x99, 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, 0xAF, 0x12, 0x9A, + 0xFA, 0x0F, 0xFC, 0x83, 0xE6, 0x63, 0x17, 0x8E, 0x89, 0x17, 0xB8, 0x99, 0x3C, 0x3D, 0x00, 0x73, + 0x2C, 0xE5, 0x4F, 0x6C, 0x10, 0x81, 0x0C, 0xF6, 0x80, 0xA7, 0x4C, 0xC0, 0x4E, 0xB1, 0x85, 0x31, + 0xB0, 0x81, 0x71, 0xBA, 0x3D, 0xC1, 0x1B, 0xC6, 0x94, 0xD9, 0x2B, 0x05, 0x60, 0x71, 0x52, 0x4B, + 0x52, 0x88, 0x6C, 0x70, 0xB6, 0x7D, 0xCF, 0xCB, 0x35, 0xEC, 0xA2, 0xA1, 0xE0, 0xCD, 0x0A, 0x46, + 0x90, 0x13, 0x80, 0x6A, 0xA3, 0x4D, 0x18, 0x3E, 0x9B, 0x92, 0xCA, 0x3C, 0x9E, 0x64, 0x08, 0xF3, + 0x74, 0xC0, 0x09, 0xD7, 0xCD, 0x96, 0x32, 0x9D, 0x3A, 0x94, 0x45, 0x53, 0x9E, 0x4A, 0x44, 0xE4, + 0x4E, 0xFB, 0x9F, 0x9F, 0xDE, 0xFE, 0x48, 0x69, 0x22, 0x4E, 0xF0, 0x70, 0xA0, 0xD6, 0xBB, 0xCC, + 0x04, 0xBE, 0xE5, 0x3F, 0x85, 0x98, 0xC2, 0x9A, 0x20, 0x6E, 0x42, 0x26, 0x85, 0xAD, 0xBC, 0x6C, + 0x75, 0x41, 0x22, 0x2F, 0xF6, 0xC9, 0xC7, 0x0F, 0x6F, 0x9A, 0xB4, 0x65, 0xB0, 0x4E, 0x96, 0x34, + 0xA8, 0x1D, 0x6A, 0xE2, 0x72, 0x7C, 0x73, 0x45, 0xD1, 0xB6, 0xD5, 0x61, 0xAE, 0xD2, 0x29, 0x2A, + 0x59, 0xA2, 0xAA, 0x15, 0xC2, 0x9A, 0xA3, 0x4E, 0x1C, 0xC1, 0xE2, 0xFC, 0x2D, 0xA6, 0x4A, 0xC4, + 0x5B, 0xE1, 0xAF, 0x0B, 0xA7, 0x79, 0x6E, 0xD0, 0xDA, 0x41, 0xC6, 0xD9, 0x9F, 0x4E, 0xA3, 0x0E, + 0x1B, 0x83, 0xC9, 0x26, 0x69, 0x41, 0x93, 0x6D, 0x9A, 0xD8, 0xC8, 0xD3, 0x2B, 0xA9, 0xE3, 0xFF, + 0xBA, 0x7E, 0xF7, 0x33, 0x20, 0x7A, 0x0A, 0x09, 0x2E, 0x8E, 0xCF, 0x92, 0x38, 0xCA, 0xC8, 0x0D, + 0xB9, 0xA7, 0x27, 0x0C, 0xF6, 0x84, 0x88, 0xA2, 0xDA, 0x66, 0xD4, 0xA6, 0xC4, 0x7B, 0x12, 0xC2, + 0x36, 0x56, 0xCA, 0x23, 0x7B, 0x5C, 0x4D, 0x42, 0xA2, 0xA6, 0xFE, 0x9F, 0xAF, 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, 0xDF, 0xE0, 0x77, 0x45, 0xCA, 0xFC, 0x1F, 0x90, 0xBD, 0x1E, 0x5F, 0x17, 0xDE, 0xB6, + 0xB4, 0x3A, 0x3C, 0xA3, 0x3D, 0xE1, 0x7E, 0xE5, 0x7B, 0x20, 0x49, 0x20, 0x4A, 0xA1, 0x80, 0x6A, + 0x5A, 0xA7, 0xD3, 0xD1, 0x2F, 0xF0, 0xF0, 0xF0, 0x1A, 0xAF, 0xFF, 0x9B, 0x66, 0x0B, 0xF3, 0xDD, + 0xFD, 0x9E, 0x8B, 0x74, 0x12, 0x06, 0x64, 0xF1, 0xB6, 0xC5, 0x31, 0x07, 0x8F, 0x0E, 0xE6, 0xB3, + 0xA9, 0xAC, 0xB7, 0xB4, 0x76, 0x4F, 0x96, 0x09, 0x45, 0x3A, 0x01, 0x28, 0x6C, 0xE1, 0xC7, 0x0D, + 0xCE, 0x29, 0x7C, 0xE3, 0x75, 0x9C, 0xAE, 0x5F, 0xBA, 0xD4, 0x75, 0xA2, 0x8E, 0x9B, 0x24, 0xB8, + 0x49, 0x1C, 0x8E, 0xD4, 0x3C, 0xBB, 0x9A, 0x72, 0x52, 0x35, 0xD9, 0xDC, 0xF1, 0x90, 0x89, 0x65, + 0x1D, 0xC3, 0x57, 0x33, 0xF7, 0x8B, 0x50, 0xE0, 0xEB, 0xB5, 0x5E, 0x30, 0xF7, 0x8D, 0x90, 0x87, + 0x4B, 0xA3, 0x98, 0xAF, 0x28, 0x69, 0x1B, 0xA1, 0x71, 0xC8, 0xA0, 0xC5, 0x14, 0xED, 0xD6, 0x78, + 0xB2, 0xE3, 0x0A, 0x4B, 0x7B, 0xFF, 0xEE, 0xFA, 0x06, 0x4F, 0x13, 0x8C, 0x8F, 0xCE, 0x2C, 0xCE, + 0xED, 0x70, 0x15, 0x76, 0x20, 0x32, 0xBD, 0xBA, 0x05, 0x8E, 0x6F, 0x01, 0x90, 0x09, 0x00, 0x2C, + 0x6A, 0x87, 0x17, 0x9D, 0x01, 0x46, 0x8C, 0x67, 0x16, 0x0E, 0x8D, 0x23, 0x1C, 0x5A, 0xF1, 0x38, + 0xE6, 0x5E, 0x53, 0x37, 0xF7, 0xAF, 0xA7, 0xED, 0xD5, 0x13, 0x76, 0x29, 0xF7, 0xB9, 0xF3, 0x2C, + 0x4B, 0x4C, 0xA7, 0x3B, 0x35, 0x3E, 0xAA, 0xA0, 0x81, 0x5B, 0x46, 0x03, 0xE1, 0xBC, 0xEC, 0x5F, + 0x24, 0x34, 0xF5, 0xEF, 0xC0, 0xF9, 0xD8, 0x6F, 0xE1, 0xF1, 0x4C, 0x07, 0x1B, 0xE0, 0x3F, 0x83, + 0x63, 0x36, 0x28, 0x86, 0x79, 0x6D, 0xD4, 0xDA, 0x17, 0x7E, 0xAB, 0x5E, 0x10, 0xFC, 0xA9, 0x9E, + 0xBB, 0xB8, 0x3B, 0xC7, 0x77, 0xF1, 0x92, 0x40, 0x8D, 0x9C, 0x0F, 0x7B, 0x2D, 0x48, 0x79, 0x10, + 0xEB, 0x2A, 0x17, 0x37, 0xDA, 0xB7, 0x7A, 0x4B, 0xCA, 0x7D, 0x7C, 0x13, 0xEE, 0x7E, 0xA7, 0x73, + 0x17, 0xD7, 0x2F, 0x4F, 0x33, 0x02, 0x65, 0xFE, 0x27, 0x90, 0x73, 0xC5, 0x9D, 0x88, 0x49, 0xE7, + 0xAB, 0xFC, 0xC4, 0x58, 0xF1, 0x73, 0x01, 0x39, 0x91, 0xF8, 0x51, 0xD5, 0x54, 0xC7, 0x5F, 0x55, + 0x9D, 0xF6, 0x0C, 0xB6, 0xFD, 0x27, 0x10, 0x4C, 0x22, 0x52, 0x19, 0xC5, 0x8C, 0x07, 0x01, 0x4A, + 0xEF, 0xEA, 0x8F, 0x04, 0x26, 0x8E, 0x4B, 0x05, 0xDD, 0x23, 0xF0, 0x88, 0xFF, 0xFE, 0x03, 0x96, + 0xF2, 0xD7, 0x40, 0x92, 0x6A, 0x61, 0xE7, 0x82, 0xD2, 0xC1, 0xEE, 0x9E, 0xB8, 0x67, 0x3B, 0xC1, + 0x46, 0xDE, 0xB8, 0x3D, 0xCD, 0xC4, 0xFE, 0x10, 0x07, 0x39, 0xC6, 0x44, 0x5E, 0x38, 0x27, 0xF7, + 0x67, 0x7A, 0x97, 0x04, 0xD8, 0x3F, 0xCD, 0x17, 0x0F, 0xC9, 0x15, 0x19, 0x9D, 0x83, 0xBC, 0xAE, + 0x82, 0xE4, 0x88, 0x3B, 0x3A, 0x16, 0x7C, 0x64, 0xED, 0xF1, 0xC5, 0x0B, 0xBD, 0x5F, 0xFE, 0xAA, + 0xF6, 0xFE, 0xF6, 0x9B, 0xC0, 0x7C, 0x81, 0x75, 0x0B, 0x17, 0x6C, 0xDC, 0xD7, 0x5B, 0x86, 0x6E, + 0xC3, 0xE1, 0x56, 0x8E, 0x6A, 0x95, 0x07, 0x79, 0x6E, 0xE4, 0x81, 0x84, 0x18, 0x16, 0x1C, 0x16, + 0x37, 0x70, 0xCE, 0x9E, 0x3A, 0x9E, 0xC3, 0xA5, 0x11, 0x4D, 0x4D, 0xF0, 0xC1, 0x87, 0x1C, 0xDB, + 0x09, 0x3A, 0x58, 0x7B, 0xEC, 0x9B, 0x06, 0x9D, 0x66, 0x84, 0xBE, 0x41, 0x53, 0x01, 0x2D, 0x37, + 0x15, 0x6B, 0x8F, 0x2E, 0xA6, 0xD6, 0xC3, 0x00, 0xC1, 0xB7, 0x26, 0x3A, 0xC7, 0x0E, 0x0B, 0x93, + 0xEE, 0x5B, 0xED, 0xC8, 0x88, 0x66, 0x7D, 0x13, 0x8E, 0xEC, 0x1E, 0x58, 0x74, 0x9A, 0xCF, 0x0F, + 0xA9, 0x7E, 0x18, 0xF3, 0xBB, 0x59, 0xD0, 0x31, 0x9A, 0x0F, 0x1E, 0x31, 0x0C, 0x8B, 0xF4, 0xCA, + 0xE1, 0xB2, 0xA4, 0x3A, 0x0C, 0x95, 0x8F, 0x8A, 0xA5, 0x45, 0x30, 0x15, 0xB8, 0xF1, 0x16, 0x6F, + 0x3A, 0x9B, 0x27, 0xDC, 0xF9, 0x9C, 0x04, 0xFD, 0x38, 0xAD, 0xBC, 0xC4, 0x3E, 0x40, 0x61, 0xF6, + 0xDB, 0x73, 0xA5, 0x24, 0x5F, 0xC9, 0xE4, 0x4F, 0xC9, 0x73, 0x94, 0x27, 0x43, 0x76, 0x61, 0xB3, + 0xA7, 0xE9, 0x95, 0x3B, 0x67, 0xB1, 0x91, 0xE2, 0x50, 0x67, 0x9C, 0x88, 0xBA, 0x75, 0x97, 0xC2, + 0x15, 0x6A, 0xBC, 0x35, 0x65, 0xC3, 0xBE, 0xFD, 0x78, 0xFD, 0xEA, 0x83, 0x7A, 0x62, 0xC3, 0xAC, + 0x04, 0xC4, 0x8B, 0x28, 0x64, 0x2B, 0x17, 0xFA, 0x8B, 0xF7, 0xDF, 0x5D, 0x5F, 0xFF, 0xF3, 0xDD, + 0x87, 0x97, 0xF5, 0x43, 0x28, 0x0E, 0xB9, 0xFE, 0xF8, 0xFD, 0x4F, 0x6F, 0x6E, 0xA6, 0x5B, 0xCC, + 0x2A, 0x83, 0x3A, 0xC4, 0x0F, 0x1E, 0x38, 0xB9, 0xC1, 0xB1, 0x2D, 0x50, 0x8E, 0x6D, 0x2F, 0x5E, + 0x00, 0x84, 0x3F, 0x83, 0x26, 0xE9, 0x9A, 0x65, 0x0B, 0xD8, 0x1B, 0x81, 0x7A, 0x76, 0x8A, 0x58, + 0xD8, 0x08, 0xE4, 0xD9, 0x09, 0x95, 0xAA, 0x16, 0xB0, 0x21, 0x24, 0x39, 0x77, 0x41, 0xE4, 0xC7, + 0x77, 0x35, 0xD1, 0xE2, 0xF8, 0xED, 0x89, 0x73, 0xD5, 0x15, 0xD7, 0xD3, 0x57, 0x5D, 0xF1, 0x83, + 0x19, 0xF6, 0xFF, 0xCC, 0xF9, 0x7F, 0xE5, 0xCC, 0x32, 0xCA, 0x3A, 0x47, 0x00, 0x00 +}; diff --git a/esp3d/webinterface.cpp b/src/syncwebserver.cpp similarity index 57% rename from esp3d/webinterface.cpp rename to src/syncwebserver.cpp index 3e8c0e15..16ba573a 100644 --- a/esp3d/webinterface.cpp +++ b/src/syncwebserver.cpp @@ -1,5 +1,5 @@ /* - webinterface.cpp - ESP3D configuration class + syncwebserver.cpp - ESP3D sync functions class Copyright (c) 2014 Luc Lebosse. All rights reserved. @@ -20,19 +20,22 @@ #include #include "config.h" +#if !defined(ASYNCWEBSERVER) + #include "webinterface.h" #include "wificonf.h" #include #include #include +#include #ifndef FS_NO_GLOBALS #define FS_NO_GLOBALS #endif #include -#ifdef ARDUINO_ARCH_ESP8266 +#if defined ( ARDUINO_ARCH_ESP8266) #include "ESP8266WiFi.h" #include -#else +#else //ESP32 #include #include #include "SPIFFS.h" @@ -40,32 +43,71 @@ #endif #include "GenLinkedList.h" -#include "storestrings.h" #include "command.h" -#include "bridge.h" +#include "espcom.h" #ifdef SSDP_FEATURE -#include + #ifdef ARDUINO_ARCH_ESP32 + #include + #else + #include + #endif #endif //embedded response file if no files on SPIFFS #include "nofile.h" +#include "syncwebserver.h" +WebSocketsServer * socket_server; -#define MAX_AUTH_IP 10 -#define HIDDEN_PASSWORD "********" +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + //USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: + { + IPAddress ip = socket_server->remoteIP(num); + //USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + String s = "CURRENT_ID:" + String(num); + // send message to client + ESPCOM::current_socket_id = num; + socket_server->sendTXT(ESPCOM::current_socket_id, s); + s = "ACTIVE_ID:" + String(ESPCOM::current_socket_id); + socket_server->broadcastTXT(s); + } + break; + case WStype_TEXT: + //USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + // send message to client + // webSocket.sendTXT(num, "message here"); + + // send data to all connected clients + // webSocket.broadcastTXT("message here"); + break; + case WStype_BIN: + //USE_SERIAL.printf("[%u] get binary length: %u\n", num, length); + //hexdump(payload, length); + + // send message to client + // webSocket.sendBIN(num, payload, length); + break; + } + +} -typedef enum { - UPLOAD_STATUS_NONE = 0, - UPLOAD_STATUS_FAILED = 1, - UPLOAD_STATUS_CANCELLED = 2, - UPLOAD_STATUS_SUCCESSFUL = 3, - UPLOAD_STATUS_ONGOING =4 -} upload_status_type; +extern bool deleteRecursive(String path); +extern void CloseSerialUpload (bool iserror, String & filename); +extern bool sendLine2Serial (String & line); -const char PAGE_404 [] PROGMEM ="\n\nRedirecting... \n\n\n
Unknown page : $QUERY$- you will be redirected...\n

\nif not redirected, click here\n

\n\n\n\n
\n\n\n\n"; -const char PAGE_CAPTIVE [] PROGMEM ="\n\nCaptive Portal \n\n\n
Captive Portal page : $QUERY$- you will be redirected...\n

\nif not redirected, click here\n

\n\n\n\n
\n\n\n\n"; -const char CONTENT_TYPE_HTML [] PROGMEM ="text/html"; + +const uint8_t PAGE_404 [] PROGMEM = "\n\nRedirecting... \n\n\n
Unknown page : $QUERY$- you will be redirected...\n

\nif not redirected, click here\n

\n\n\n\n
\n\n\n\n"; +const uint8_t PAGE_CAPTIVE [] PROGMEM = "\n\nCaptive Portal \n\n\n
Captive Portal page : $QUERY$- you will be redirected...\n

\nif not redirected, click here\n

\n\n\n\n
\n\n\n\n"; +#define CONTENT_TYPE_HTML "text/html" + +//Root of Webserver///////////////////////////////////////////////////// void handle_web_interface_root() { @@ -73,7 +115,7 @@ void handle_web_interface_root() String contentType = web_interface->getContentType(path); String pathWithGz = path + ".gz"; //if have a index.html or gzip version this is default root page - if((SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) && !web_interface->web_server.hasArg("fallback") && web_interface->web_server.arg("forcefallback")!="yes") { + if((SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) && !web_interface->web_server.hasArg("forcefallback") && web_interface->web_server.arg("forcefallback")!="yes") { if(SPIFFS.exists(pathWithGz)) { path = pathWithGz; } @@ -87,621 +129,178 @@ void handle_web_interface_root() web_interface->web_server.send_P(200,CONTENT_TYPE_HTML,PAGE_NOFILES,PAGE_NOFILES_SIZE); } -//concat several catched informations temperatures/position/status/flow/speed -void handle_web_interface_status() +//Login check/////////////////////////////////////////////////////////// +void handle_login() { - // static const char NO_TEMP_LINE[] PROGMEM = "\"temperature\":\"0\",\"target\":\"0\",\"active\":\"0\""; - //we do not care if need authentication - just reset counter - web_interface->is_authenticated(); - //int tagpos,tagpos2; - String buffer2send; - String value; - //start JSON answer - buffer2send="{"; -#ifdef INFO_MSG_FEATURE - //information - buffer2send.concat(F("\"InformationMsg\":[")); - - for (int i=0; iinfo_msg.size(); i++) { - if (i>0) { - buffer2send+=","; - } - buffer2send+="{\"line\":\""; - buffer2send+=web_interface->info_msg.get(i); - buffer2send+="\"}"; +#ifdef AUTHENTICATION_FEATURE + String smsg; + String sUser,sPassword; + String auths; + int code = 200; + bool msg_alert_error=false; + //disconnect can be done anytime no need to check credential + if (web_interface->web_server.hasArg("DISCONNECT")) { + String cookie = web_interface->web_server.header("Cookie"); + int pos = cookie.indexOf("ESPSESSIONID="); + String sessionID; + if (pos!= -1) { + int pos2 = cookie.indexOf(";",pos); + sessionID = cookie.substring(pos+strlen("ESPSESSIONID="),pos2); + } + web_interface->ClearAuthIP(web_interface->web_server.client().remoteIP(), sessionID.c_str()); + web_interface->web_server.sendHeader("Set-Cookie","ESPSESSIONID=0"); + web_interface->web_server.sendHeader("Cache-Control","no-cache"); + String buffer2send = "{\"status\":\"Ok\",\"authentication_lvl\":\"guest\"}"; + web_interface->web_server.send(code, "application/json", buffer2send); + //web_interface->web_server.client().stop(); + return; } - buffer2send+="],"; -#endif -#ifdef ERROR_MSG_FEATURE - //Error - buffer2send.concat(F("\"ErrorMsg\":[")); - for (int i=0; ierror_msg.size(); i++) { - if (i>0) { - buffer2send+=","; - } - buffer2send+="{\"line\":\""; - buffer2send+=web_interface->error_msg.get(i); - buffer2send+="\"}"; - } - buffer2send+="],"; -#endif -#ifdef STATUS_MSG_FEATURE - //Status - buffer2send.concat(F("\"StatusMsg\":[")); - for (int i=0; istatus_msg.size(); i++) { - if (i>0) { - buffer2send+=","; - } - buffer2send+="{\"line\":\""; - buffer2send+=web_interface->status_msg.get(i); - buffer2send+="\"}"; - } - buffer2send+="],"; -#endif - //status color - buffer2send+="\"status\":\""+value +"\""; - buffer2send+="}"; - web_interface->web_server.sendHeader("Cache-Control", "no-cache"); - web_interface->web_server.send(200, "application/json",buffer2send); -} - -//SPIFFS files uploader handle -void SPIFFSFileupload() -{ -#ifdef DEBUG_PERFORMANCE - static uint32_t startupload; - static uint32_t write_time; - static size_t filesize; -#endif - //get authentication status level_authenticate_type auth_level= web_interface->is_authenticated(); - //Guest cannot upload - if (auth_level == LEVEL_GUEST) { - web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; - ESP_SERIAL_OUT.println("M117 Error ESP upload"); -#ifdef ARDUINO_ARCH_ESP8266 - web_interface->web_server.client().stopAll(); -#else - web_interface->web_server.client().stop(); -#endif - return; - } + if (auth_level == LEVEL_GUEST) auths = F("guest"); + else if (auth_level == LEVEL_USER) auths = F("user"); + else if (auth_level == LEVEL_ADMIN) auths = F("admin"); + else auths = F("???"); - static String filename; - //get current file ID - HTTPUpload& upload = (web_interface->web_server).upload(); - //Upload start - //************** - if(upload.status == UPLOAD_FILE_START) { -#ifdef DEBUG_PERFORMANCE - startupload = millis(); - write_time = 0; - filesize = 0; -#endif - //according User or Admin the root is different as user is isolate to /user when admin has full access - if(auth_level == LEVEL_ADMIN) { - filename = upload.filename; - } else { - filename = "/user" + upload.filename; - } - ESP_SERIAL_OUT.println("M117 Start ESP upload"); - //create file - web_interface->fsUploadFile = SPIFFS.open(filename, SPIFFS_FILE_WRITE); - //check If creation succeed - if (web_interface->fsUploadFile) { - //if yes upload is started - web_interface->_upload_status= UPLOAD_STATUS_ONGOING; - } else { - //if no set cancel flag - web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; - ESP_SERIAL_OUT.println("M117 Error ESP create"); -#ifdef ARDUINO_ARCH_ESP8266 - web_interface->web_server.client().stopAll(); -#else - web_interface->web_server.client().stop(); -#endif - } - //Upload write - //************** - } else if(upload.status == UPLOAD_FILE_WRITE) { - //check if file is available and no error - if(web_interface->fsUploadFile && web_interface->_upload_status == UPLOAD_STATUS_ONGOING) { -#ifdef DEBUG_PERFORMANCE - filesize+=upload.currentSize; - uint32_t startwrite = millis(); -#endif - //no error so write post date - web_interface->fsUploadFile.write(upload.buf, upload.currentSize); -#ifdef DEBUG_PERFORMANCE - write_time += (millis()-startwrite); -#endif - } else { - //we have a problem set flag UPLOAD_STATUS_CANCELLED - web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; -#ifdef ARDUINO_ARCH_ESP8266 - web_interface->web_server.client().stopAll(); -#else - web_interface->web_server.client().stop(); -#endif - ESP_SERIAL_OUT.println("M117 Error ESP write"); - } - //Upload end - //************** - } else if(upload.status == UPLOAD_FILE_END) { -#ifdef DEBUG_PERFORMANCE - uint32_t endupload = millis(); - DEBUG_PERF_VARIABLE.add(String(endupload-startupload).c_str()); - DEBUG_PERF_VARIABLE.add(String(write_time).c_str()); - DEBUG_PERF_VARIABLE.add(String(filesize).c_str()); -#endif - ESP_SERIAL_OUT.println("M117 End ESP upload"); - //check if file is still open - if(web_interface->fsUploadFile) { - //close it - web_interface->fsUploadFile.close(); - web_interface->_upload_status=UPLOAD_STATUS_SUCCESSFUL; - } else { - //we have a problem set flag UPLOAD_STATUS_CANCELLED - web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; -#ifdef ARDUINO_ARCH_ESP8266 - web_interface->web_server.client().stopAll(); -#else - web_interface->web_server.client().stop(); -#endif - SPIFFS.remove(filename); - ESP_SERIAL_OUT.println("M117 Error ESP close"); - } - //Upload cancelled - //************** - } else { - ESP_SERIAL_OUT.println("M117 Error ESP close"); - return; - web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; - SPIFFS.remove(filename); - ESP_SERIAL_OUT.println("M117 Error ESP upload"); - } - delay(0); -} + //check is it is a submission or a query + if (web_interface->web_server.hasArg("SUBMIT")) { + //is there a correct list of query? + if ( web_interface->web_server.hasArg("PASSWORD")&& web_interface->web_server.hasArg("USER")) { + //USER + sUser = web_interface->web_server.arg("USER"); + if ( !((sUser==FPSTR(DEFAULT_ADMIN_LOGIN)) || (sUser==FPSTR(DEFAULT_USER_LOGIN)))) { + msg_alert_error=true; + smsg=F("Error : Incorrect User"); + code=401; + } + if (msg_alert_error == false) { + //Password + sPassword = web_interface->web_server.arg("PASSWORD"); + String sadminPassword; -#define NB_RETRY 5 -#define MAX_RESEND_BUFFER 128 -//SD file upload by serial -void SDFile_serial_upload() -{ - static char buffer_line[MAX_RESEND_BUFFER]; //if need to resend - static char previous = 0; - static int buffer_size; - static bool com_error = false; - static bool is_comment = false; - bool client_closed = false; - static String filename; - String response; - //Guest cannot upload - only admin and user - if(web_interface->is_authenticated() == LEVEL_GUEST) { - web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; - ESP_SERIAL_OUT.println("M117 SD upload rejected"); - LOG("SD upload rejected\r\n"); - if (!client_closed){ - //web_interface->web_server.client().stopAll(); - LOG("Need to stop"); - client_closed = true; - } - return; - } -#ifdef DEBUG_PERFORMANCE - static uint32_t startupload; - static uint32_t write_time; - static size_t filesize; -#endif - //retrieve current file id - HTTPUpload& upload = (web_interface->web_server).upload(); - //Upload start - //************** - if(upload.status == UPLOAD_FILE_START) { - //need to lock serial out to avoid garbage in file - (web_interface->blockserial) = true; - //init flags - buffer_size=0; - com_error = false; - is_comment = false; - previous = 0; - web_interface->_upload_status= UPLOAD_STATUS_ONGOING; - ESP_SERIAL_OUT.println("M117 Uploading..."); - ESP_SERIAL_OUT.flush(); -#ifdef DEBUG_PERFORMANCE - startupload = millis(); - write_time = 0; - filesize = 0; -#endif - LOG("Clear Serial\r\n"); - if(ESP_SERIAL_OUT.available()) { - //get size of buffer - size_t len = ESP_SERIAL_OUT.available(); - uint8_t sbuf[len+1]; - //read buffer - ESP_SERIAL_OUT.readBytes(sbuf, len); - //convert buffer to zero end array - sbuf[len]='\0'; - //use string because easier to handle - response = (const char*)sbuf; - LOG(response); - LOG("\r\n"); + if (!CONFIG::read_string(EP_ADMIN_PWD, sadminPassword, MAX_LOCAL_PASSWORD_LENGTH)) { + sadminPassword=FPSTR(DEFAULT_ADMIN_PWD); } - //command to pritnter to start print - String command = "M28 " + upload.filename; - LOG(command); - LOG("\r\n"); - ESP_SERIAL_OUT.println(command); - ESP_SERIAL_OUT.flush(); - filename = upload.filename; - //now need to purge all serial data - //let's sleep 1s - //delay(1000); - for (int retry=0; retry < 400; retry++) { //time out is 5x400ms = 2000ms - //if there is something in serial buffer - if(ESP_SERIAL_OUT.available()) { - //get size of buffer - size_t len = ESP_SERIAL_OUT.available(); - uint8_t sbuf[len+1]; - //read buffer - ESP_SERIAL_OUT.readBytes(sbuf, len); - //convert buffer to zero end array - sbuf[len]='\0'; - //use string because easier to handle - response =(const char*)sbuf; - LOG(response); - //if there is a wait it means purge is done - if (response.indexOf("wait")>-1) { - LOG("Exit start writing\r\n"); - break; + + String suserPassword; + + if (!CONFIG::read_string(EP_USER_PWD, suserPassword, MAX_LOCAL_PASSWORD_LENGTH)) { + suserPassword=FPSTR(DEFAULT_USER_PWD); } - if (response.indexOf("Resend")>-1 || response.indexOf("failed")>-1) { - com_error = true; - web_interface->blockserial = false; - LOG("Error start writing\r\n"); - break; + + if(!(((sUser==FPSTR(DEFAULT_ADMIN_LOGIN)) && (strcmp(sPassword.c_str(),sadminPassword.c_str())==0)) || + ((sUser==FPSTR(DEFAULT_USER_LOGIN)) && (strcmp(sPassword.c_str(),suserPassword.c_str()) == 0)))) { + msg_alert_error=true; + smsg=F("Error: Incorrect password"); + code = 401; } } - delay(5); + } else { + msg_alert_error=true; + smsg = F("Error: Missing data"); + code = 500; } - //Upload write - //************** - //upload is on going with data coming by 2K blocks - } else if((upload.status == UPLOAD_FILE_WRITE) && (com_error == false)) { //if com error no need to send more data to serial - web_interface->_upload_status= UPLOAD_STATUS_ONGOING; -#ifdef DEBUG_PERFORMANCE - filesize+=upload.currentSize; - uint32_t startwrite = millis(); -#endif - for (int pos = 0; pos < upload.currentSize; pos++) { //parse full post data - if (buffer_size < MAX_RESEND_BUFFER-1) { //raise error/handle if overbuffer - copy is space available - //remove/ignore every comment to save transfert time and avoid over buffer issues - if (upload.buf[pos] == ';') { - is_comment = true; - previous = ';'; + //change password + if ( web_interface->web_server.hasArg("PASSWORD")&& web_interface->web_server.hasArg("USER") && web_interface->web_server.hasArg("NEWPASSWORD") && (msg_alert_error==false) ) { + String newpassword = web_interface->web_server.arg("NEWPASSWORD"); + if (CONFIG::isLocalPasswordValid(newpassword.c_str())) { + int pos=0; + if(sUser==FPSTR(DEFAULT_ADMIN_LOGIN)) pos = EP_ADMIN_PWD; + else pos = EP_USER_PWD; + if (!CONFIG::write_string(pos,newpassword.c_str())){ + msg_alert_error=true; + smsg = F("Error: Cannot apply changes"); + code = 500; } - if (!is_comment) { - buffer_line[buffer_size] = upload.buf[pos]; //copy current char to buffer to send/resend - buffer_size++; - //convert buffer to zero end array - buffer_line[buffer_size] = '\0'; - //check it is not an end line char and line is not empty - if (((buffer_line[0] == '\n') && (buffer_size==1)) ||((buffer_line[1] == '\n') && (buffer_line[0] == '\r') && (buffer_size==2)) || ((buffer_line[0] == ' ') && (buffer_size==1)) ) { - //ignore empty line - buffer_size=0; - buffer_line[buffer_size] = '\0'; - } - //line is not empty so check if last char is an end line - //if error no need to proceed - else if (((buffer_line[buffer_size-1] == '\n')) && (com_error == false)) { //end of line and no error - //if resend use buffer - bool success = false; - - //check NB_RETRY times if get no error when send line - for (int r = 0 ; r < NB_RETRY ; r++) { - response = ""; - //print out line - ESP_SERIAL_OUT.print(buffer_line); - LOG(buffer_line); - //ensure buffer is empty before continuing - ESP_SERIAL_OUT.flush(); - //wait for answer with time out - for (int retry=0; retry < 30; retry++) { //time out 30x5ms = 150ms - //if there is serial data - if(ESP_SERIAL_OUT.available()) { - //get size of available data - size_t len = ESP_SERIAL_OUT.available(); - uint8_t sbuf[len+1]; - //read serial buffer - ESP_SERIAL_OUT.readBytes(sbuf, len); - //convert buffer in zero end array - sbuf[len]='\0'; - //use string because easier - response = (const char*)sbuf; - LOG("Retry:"); - LOG(String(retry)); - LOG("\r\n"); - LOG(response); - //if buffer contain ok or wait - it means command is pass - if ((response.indexOf("wait")>-1)||(response.indexOf("ok")>-1)) { - success = true; - break; - } - //if buffer contain resend then need to resend - if (response.indexOf("Resend") > -1) { //if error - success = false; - break; - } - } - delay(5); - } - //if command is pass no need to retry - if (success == true) { - break; - } - //purge extra serial if any - if(ESP_SERIAL_OUT.available()) { - //get size of available data - size_t len = ESP_SERIAL_OUT.available(); - uint8_t sbuf[len+1]; - //read serial buffer - ESP_SERIAL_OUT.readBytes(sbuf, len); - //convert buffer in zero end array - sbuf[len]='\0'; - } - } - //if even after the number of retry still have error - then we are in error - if (!success) { - //raise error - LOG("Error detected\r\n"); - LOG(response); - com_error = true; - } - //reset buffer for next command - buffer_size = 0; - buffer_line[buffer_size] = '\0'; - } - } else { //it is a comment - if (upload.buf[pos] == '\r') { //store if CR - previous = '\r'; - } else if (upload.buf[pos] == '\n') { //this is the end of the comment - is_comment = false; - if (buffer_size > 0) { - if (previous == '\r') { - pos--; - } - pos--; //do a loop back and process as normal - } - previous = '\n'; - }//if not just ignore and continue - else { - previous = upload.buf[pos]; - } - - } - } else { //raise error - LOG("\r\nlong line detected\r\n"); - LOG(buffer_line); - com_error = true; - } - } -#ifdef DEBUG_PERFORMANCE - write_time += (millis()-startwrite); -#endif - //Upload end - //************** - } else if(upload.status == UPLOAD_FILE_END) { - if (buffer_size > 0) { //if last part does not have '\n' - //print the line - ESP_SERIAL_OUT.print(buffer_line); - if (is_comment && (previous == '\r')) { - ESP_SERIAL_OUT.print("\r\n"); } else { - ESP_SERIAL_OUT.print("\n"); + msg_alert_error=true; + smsg = F("Error: Incorrect password"); + code = 500; } - ESP_SERIAL_OUT.flush(); - //if resend use buffer - bool success = false; - //check NB_RETRY times if get no error when send line - for (int r = 0 ; r < NB_RETRY ; r++) { - response = ""; - ESP_SERIAL_OUT.print(buffer_line); - ESP_SERIAL_OUT.flush(); - //wait for answer with time out - for (int retry=0; retry < 20; retry++) { //time out - if(ESP_SERIAL_OUT.available()) { - size_t len = ESP_SERIAL_OUT.available(); - uint8_t sbuf[len+1]; - ESP_SERIAL_OUT.readBytes(sbuf, len); - sbuf[len]='\0'; - response = (const char*)sbuf; - if ((response.indexOf("wait")>-1)||(response.indexOf("ok")>-1)) { - success = true; - break; - } - if (response.indexOf("Resend") > -1) { //if error - success = false; - break; - } + } + if ((code == 200) || (code == 500)) { + level_authenticate_type current_auth_level; + if(sUser == FPSTR(DEFAULT_ADMIN_LOGIN)) { + current_auth_level = LEVEL_ADMIN; + } else if(sUser == FPSTR(DEFAULT_USER_LOGIN)){ + current_auth_level = LEVEL_USER; + } else { + current_auth_level = LEVEL_GUEST; + } + //create Session + if ((current_auth_level != auth_level) || (auth_level== LEVEL_GUEST)) { + auth_ip * current_auth = new auth_ip; + current_auth->level = current_auth_level; + current_auth->ip=web_interface->web_server.client().remoteIP(); + strcpy(current_auth->sessionID,web_interface->create_session_ID()); + strcpy(current_auth->userID,sUser.c_str()); + current_auth->last_time=millis(); + if (web_interface->AddAuthIP(current_auth)) { + String tmps ="ESPSESSIONID="; + tmps+=current_auth->sessionID; + web_interface->web_server.sendHeader("Set-Cookie",tmps); + web_interface->web_server.sendHeader("Cache-Control","no-cache"); + switch(current_auth->level) { + case LEVEL_ADMIN: + auths = "admin"; + break; + case LEVEL_USER: + auths = "user"; + break; + default: + auths = "guest"; } - delay(5); - } - if (success == true) { - break; - } - } - if (!success) { - //raise error - LOG("Error detected 2\r\n"); - LOG(response); - com_error = true; - } - //reset buffer for next command - buffer_size = 0; - buffer_line[buffer_size] = '\0'; - - } - LOG("Upload finished "); - buffer_size=0; - buffer_line[buffer_size] = '\0'; - //send M29 command to close file on SD - ESP_SERIAL_OUT.print("\r\nM29\r\n"); - ESP_SERIAL_OUT.flush(); - web_interface->blockserial = false; - delay(1000);//give time to FW - //resend M29 command to close file on SD as first command may be lost - ESP_SERIAL_OUT.print("\r\nM29\r\n"); - ESP_SERIAL_OUT.flush(); -#ifdef DEBUG_PERFORMANCE - uint32_t endupload = millis(); - DEBUG_PERF_VARIABLE.add(String(endupload-startupload).c_str()); - DEBUG_PERF_VARIABLE.add(String(write_time).c_str()); - DEBUG_PERF_VARIABLE.add(String(filesize).c_str()); -#endif - if (com_error) { - web_interface->blockserial = false; - LOG("with error\r\n"); - web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; - if (!client_closed){ - //web_interface->web_server.client().stopAll(); - LOG("Need to stop"); - client_closed = true; - } - filename = "M30 " + filename; - ESP_SERIAL_OUT.println(filename); - ESP_SERIAL_OUT.println("M117 SD upload failed"); - ESP_SERIAL_OUT.flush(); - - } else { - LOG("with success\r\n"); - web_interface->_upload_status=UPLOAD_STATUS_SUCCESSFUL; - ESP_SERIAL_OUT.println("M117 SD upload done"); - ESP_SERIAL_OUT.flush(); - } - //Upload cancelled - //************** - } else { //UPLOAD_FILE_ABORTED - LOG("Error, Something happened\r\n"); - com_error = true; - web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; - buffer_size=0; - buffer_line[buffer_size] = '\0'; - //send M29 command to close file on SD - ESP_SERIAL_OUT.print("\r\nM29\r\n"); - ESP_SERIAL_OUT.flush(); - web_interface->blockserial = false; - delay(1000); - //resend M29 command to close file on SD as first command may be lost - ESP_SERIAL_OUT.print("\r\nM29\r\n"); - ESP_SERIAL_OUT.flush(); - filename = "M30 " + filename; - ESP_SERIAL_OUT.println(filename); - ESP_SERIAL_OUT.println("M117 SD upload failed"); - ESP_SERIAL_OUT.flush(); - } -} - - -//FW update using Web interface -#ifdef WEB_UPDATE_FEATURE -void WebUpdateUpload() -{ - static size_t last_upload_update; - static uint32_t maxSketchSpace ; - //only admin can update FW - if(web_interface->is_authenticated() != LEVEL_ADMIN) { - web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; -#ifdef ARDUINO_ARCH_ESP8266 - web_interface->web_server.client().stopAll(); -#else - web_interface->web_server.client().stop(); -#endif - ESP_SERIAL_OUT.println("M117 Update failed"); - LOG("SD Update failed\r\n"); - return; - } - //get current file ID - HTTPUpload& upload = (web_interface->web_server).upload(); - //Upload start - //************** - if(upload.status == UPLOAD_FILE_START) { - ESP_SERIAL_OUT.println(F("M117 Update Firmware")); - web_interface->_upload_status= UPLOAD_STATUS_ONGOING; -#ifdef ARDUINO_ARCH_ESP8266 - WiFiUDP::stopAll(); -#endif -#ifdef ARDUINO_ARCH_ESP8266 - maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; -#else -//Not sure can do OTA on 2Mb board - maxSketchSpace = (ESP.getFlashChipSize()>0x20000)?0x140000:0x140000/2; -#endif - last_upload_update = 0; - if(!Update.begin(maxSketchSpace)) { //start with max available size - web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; - } else { - if (( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER)) ESP_SERIAL_OUT.println(F("M117 Update 0%%")); - else ESP_SERIAL_OUT.println(F("M117 Update 0%")); - } - //Upload write - //************** - } else if(upload.status == UPLOAD_FILE_WRITE) { - //check if no error - if (web_interface->_upload_status == UPLOAD_STATUS_ONGOING) { - //we do not know the total file size yet but we know the available space so let's use it - if ( ((100 * upload.totalSize) / maxSketchSpace) !=last_upload_update) { - last_upload_update = (100 * upload.totalSize) / maxSketchSpace; - ESP_SERIAL_OUT.print(F("M117 Update ")); - ESP_SERIAL_OUT.print(last_upload_update); - if (( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER)) ESP_SERIAL_OUT.println(F("%%")); - else ESP_SERIAL_OUT.println(F("%")); - } - if(Update.write(upload.buf, upload.currentSize) != upload.currentSize) { - web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; + } else { + delete current_auth; + msg_alert_error=true; + code = 500; + smsg = F("Error: Too many connections"); } } - //Upload end - //************** - } else if(upload.status == UPLOAD_FILE_END) { - if(Update.end(true)) { //true to set the size to the current progress - //Now Reboot - if (( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER)) ESP_SERIAL_OUT.println(F("M117 Update 100%%")); - else ESP_SERIAL_OUT.println(F("M117 Update 100%")); - web_interface->_upload_status=UPLOAD_STATUS_SUCCESSFUL; - } - } else if(upload.status == UPLOAD_FILE_ABORTED) { - ESP_SERIAL_OUT.println(F("M117 Update Failed")); - Update.end(); - web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; - } - delay(0); -} + } + if (code == 200) smsg = F("Ok"); -void handleUpdate() -{ - level_authenticate_type auth_level = web_interface->is_authenticated(); - if (auth_level != LEVEL_ADMIN) { - web_interface->_upload_status=UPLOAD_STATUS_NONE; - web_interface->web_server.send(403,"text/plain","Not allowed, log in first!\n"); - return; - } - String jsonfile = "{\"status\":\"" ; - jsonfile+=CONFIG::intTostr(web_interface->_upload_status); - jsonfile+="\"}"; - //send status - web_interface->web_server.sendHeader("Cache-Control", "no-cache"); - web_interface->web_server.send(200, "application/json", jsonfile); - //if success restart - if (web_interface->_upload_status==UPLOAD_STATUS_SUCCESSFUL) { - web_interface->restartmodule=true; + //build JSON + String buffer2send = "{\"status\":\"" + smsg + "\",\"authentication_lvl\":\""; + buffer2send += auths; + buffer2send += "\"}"; + web_interface->web_server.send(code, "application/json", buffer2send); } else { - web_interface->_upload_status=UPLOAD_STATUS_NONE; + if (auth_level != LEVEL_GUEST) { + String cookie = web_interface->web_server.header("Cookie"); + int pos = cookie.indexOf("ESPSESSIONID="); + String sessionID; + if (pos!= -1) { + int pos2 = cookie.indexOf(";",pos); + sessionID = cookie.substring(pos+strlen("ESPSESSIONID="),pos2); + auth_ip * current_auth_info = web_interface->GetAuth(web_interface->web_server.client().remoteIP(), sessionID.c_str()); + if (current_auth_info != NULL){ + sUser = current_auth_info->userID; + } + } } + String buffer2send = "{\"status\":\"200\",\"authentication_lvl\":\""; + buffer2send += auths; + buffer2send += "\",\"user\":\""; + buffer2send += sUser; + buffer2send +="\"}"; + web_interface->web_server.send(code, "application/json", buffer2send); + } +#else + web_interface->web_server.sendHeader("Cache-Control","no-cache"); + web_interface->web_server.send(200, "application/json", "{\"status\":\"Ok\",\"authentication_lvl\":\"admin\"}"); +#endif +} + +//SSDP interface//////////////////////////////////////// +#ifdef SSDP_FEATURE +void handle_SSDP() +{ + SSDP.schema(web_interface->web_server.client()); } #endif -//SPIFFS files list and file commands +//SPIFFS files list and file commands/////////////////////////////////// void handleFileList() { level_authenticate_type auth_level = web_interface->is_authenticated(); @@ -734,37 +333,39 @@ void handleFileList() //check if query need some action if(web_interface->web_server.hasArg("action")) { //delete a file - if(web_interface->web_server.arg("action") == "delete" && web_interface->web_server.hasArg("filename")) { + if(web_interface->web_server.arg ("action") == "delete" && web_interface->web_server.hasArg ("filename")) { String filename; - String shortname = web_interface->web_server.arg("filename"); - shortname.replace("/",""); - filename = path + web_interface->web_server.arg("filename"); - filename.replace("//","/"); - if(!SPIFFS.exists(filename)) { + String shortname = web_interface->web_server.arg ("filename"); + shortname.replace ("/",""); + filename = path + web_interface->web_server.arg ("filename"); + filename.replace ("//","/"); + if (!SPIFFS.exists (filename)) { status = shortname + F(" does not exists!"); } else { if (SPIFFS.remove(filename)) { status = shortname + F(" deleted"); //what happen if no "/." and no other subfiles ? -#ifdef ARDUINO_ARCH_ESP8266 +#if defined(ARDUINO_ARCH_ESP8266) FS_DIR dir = SPIFFS.openDir(path); if (!dir.next()) { #else String ptmp = path; - if ((path != "/") && (path[path.length()-1]='/'))ptmp = path.substring(0,path.length()-1); - FS_FILE dir = SPIFFS.open(ptmp); + if ( (path != "/") && (path[path.length() - 1] = '/') ) { + ptmp = path.substring (0, path.length() - 1); + } + FS_FILE dir = SPIFFS.open (ptmp); FS_FILE dircontent = dir.openNextFile(); if (!dircontent) { #endif //keep directory alive even empty - FS_FILE r = SPIFFS.open(path+"/.", SPIFFS_FILE_WRITE); + FS_FILE r = SPIFFS.open (path+"/.", SPIFFS_FILE_WRITE); if (r) { r.close(); } } } else { status = F("Cannot deleted ") ; - status+=shortname ; + status += shortname ; } } } @@ -778,7 +379,7 @@ void handleFileList() filename.replace("//","/"); if (filename != "/") { bool delete_error = false; -#ifdef ARDUINO_ARCH_ESP8266 +#if defined ( ARDUINO_ARCH_ESP8266) FS_DIR dir = SPIFFS.openDir(path + shortname); { while (dir.next()) { @@ -788,7 +389,7 @@ void handleFileList() FS_FILE file2deleted = dir.openNextFile(); while (file2deleted) { #endif -#ifdef ARDUINO_ARCH_ESP8266 +#if defined ( ARDUINO_ARCH_ESP8266) String fullpath = dir.fileName(); #else String fullpath = file2deleted.name(); @@ -798,7 +399,7 @@ void handleFileList() status = F("Cannot deleted ") ; status+=fullpath; } -#ifdef ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP32) file2deleted = dir.openNextFile(); #endif } @@ -831,17 +432,19 @@ void handleFileList() } } String jsonfile = "{"; -#ifdef ARDUINO_ARCH_ESP8266 +#if defined ( ARDUINO_ARCH_ESP8266 ) FS_DIR dir = SPIFFS.openDir(path); #else String ptmp = path; - if ((path != "/") && (path[path.length()-1]='/'))ptmp = path.substring(0,path.length()-1); + if ( (path != "/") && (path[path.length() - 1] = '/') ) { + ptmp = path.substring (0, path.length() - 1); + } FS_FILE dir = SPIFFS.open(ptmp); #endif jsonfile+="\"files\":["; bool firstentry=true; String subdirlist=""; -#ifdef ARDUINO_ARCH_ESP8266 +#if defined ( ARDUINO_ARCH_ESP8266 ) while (dir.next()) { String filename = dir.fileName(); #else @@ -873,7 +476,7 @@ void handleFileList() } else { //do not add "." file if (!((filename==".") || (filename==""))) { -#ifdef ARDUINO_ARCH_ESP8266 +#if defined ( ARDUINO_ARCH_ESP8266) size = CONFIG::formatBytes(dir.fileSize()); #else size = CONFIG::formatBytes(fileparsed.size()); @@ -906,7 +509,7 @@ void handleFileList() jsonfile+="\"status\":\"" + status + "\","; size_t totalBytes; size_t usedBytes; -#ifdef ARDUINO_ARCH_ESP8266 +#if defined ( ARDUINO_ARCH_ESP8266) fs::FSInfo info; SPIFFS.info(info); totalBytes = info.totalBytes; @@ -927,32 +530,222 @@ void handleFileList() web_interface->_upload_status=UPLOAD_STATUS_NONE; } -//serial SD files list -void handle_serial_SDFileList() +//SPIFFS files uploader handle +void SPIFFSFileupload() { - //this is only for admin an user - if (web_interface->is_authenticated() == LEVEL_GUEST) { - web_interface->_upload_status=UPLOAD_STATUS_NONE; - web_interface->web_server.sendHeader("Cache-Control", "no-cache"); - web_interface->web_server.send(401, "application/json", "{\"status\":\"Authentication failed!\"}"); + static FS_FILE fsUploadFile = (FS_FILE)0; + //get authentication status + level_authenticate_type auth_level= web_interface->is_authenticated(); + //Guest cannot upload + if (auth_level == LEVEL_GUEST) { + web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; + ESPCOM::println (F ("Upload rejected"), PRINTER_PIPE); +#if defined ( ARDUINO_ARCH_ESP8266) + web_interface->web_server.client().stopAll(); +#else + web_interface->web_server.client().stop(); +#endif return; } - LOG("serial SD upload done\r\n") - String sstatus="Ok"; - if ((web_interface->_upload_status == UPLOAD_STATUS_FAILED) || (web_interface->_upload_status == UPLOAD_STATUS_CANCELLED)) { - sstatus = "Upload failed"; - web_interface->_upload_status = UPLOAD_STATUS_NONE; + + static String filename; + //get current file ID + HTTPUpload& upload = (web_interface->web_server).upload(); + //Upload start + //************** + if(upload.status == UPLOAD_FILE_START) { + String upload_filename = upload.filename; + String sizeargname = upload_filename + "S"; + if (web_interface->web_server.hasArg ("plain") ) Serial.println("Yes"); + if (upload_filename[0] != '/') filename = "/" + upload_filename; + else filename = upload.filename; + //according User or Admin the root is different as user is isolate to /user when admin has full access + if(auth_level != LEVEL_ADMIN) { + upload_filename = filename; + filename = "/user" + upload_filename; + } + + if (SPIFFS.exists (filename) ) { + SPIFFS.remove (filename); + } + if (fsUploadFile ) { + fsUploadFile.close(); + } + //create file + fsUploadFile = SPIFFS.open(filename, SPIFFS_FILE_WRITE); + //check If creation succeed + if (fsUploadFile) { + //if yes upload is started + web_interface->_upload_status= UPLOAD_STATUS_ONGOING; + } else { + //if no set cancel flag + web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; + ESPCOM::println (F ("Error ESP create"), PRINTER_PIPE); +#if defined ( ARDUINO_ARCH_ESP8266) + web_interface->web_server.client().stopAll(); +#else + web_interface->web_server.client().stop(); +#endif + } + //Upload write + //************** + } else if(upload.status == UPLOAD_FILE_WRITE) { + //check if file is available and no error + if(fsUploadFile && web_interface->_upload_status == UPLOAD_STATUS_ONGOING) { + //no error so write post date + fsUploadFile.write(upload.buf, upload.currentSize); + } else { + //we have a problem set flag UPLOAD_STATUS_CANCELLED + web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; +#if defined ( ARDUINO_ARCH_ESP8266) + web_interface->web_server.client().stopAll(); +#else + web_interface->web_server.client().stop(); +#endif + ESPCOM::println (F ("Error ESP write"), PRINTER_PIPE); + } + //Upload end + //************** + } else if(upload.status == UPLOAD_FILE_END) { + //check if file is still open + if(fsUploadFile) { + //close it + fsUploadFile.close(); + if (web_interface->_upload_status == UPLOAD_STATUS_ONGOING) { + web_interface->_upload_status = UPLOAD_STATUS_SUCCESSFUL; + } + } else { + //we have a problem set flag UPLOAD_STATUS_CANCELLED + web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; +#if defined ( ARDUINO_ARCH_ESP8266) + web_interface->web_server.client().stopAll(); +#else + web_interface->web_server.client().stop(); +#endif + if (SPIFFS.exists (filename) ) { + SPIFFS.remove (filename); + } + ESPCOM::println (F ("Error ESP close"), PRINTER_PIPE); + + } + //Upload cancelled + //************** + } else { + if (web_interface->_upload_status == UPLOAD_STATUS_ONGOING) { + web_interface->_upload_status = UPLOAD_STATUS_CANCELLED; + } + ESPCOM::println (F ("Error ESP upload"), PRINTER_PIPE); + return; } - String jsonfile = "{\"status\":\"" + sstatus + "\"}"; - web_interface->web_server.sendHeader("Cache-Control", "no-cache"); - web_interface->web_server.send(200, "application/json", jsonfile); - web_interface->blockserial = false; - web_interface->_upload_status=UPLOAD_STATUS_NONE; + CONFIG::wait(0); } +//FW update using Web interface///////////////////////////////////////// +#ifdef WEB_UPDATE_FEATURE +void WebUpdateUpload() +{ + static size_t last_upload_update; + static uint32_t maxSketchSpace ; + //only admin can update FW + if(web_interface->is_authenticated() != LEVEL_ADMIN) { + web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; +#if defined ( ARDUINO_ARCH_ESP8266) + web_interface->web_server.client().stopAll(); +#else + web_interface->web_server.client().stop(); +#endif + ESPCOM::println (F ("Update failed"), PRINTER_PIPE); + LOG("Web Update failed\r\n"); + return; + } + //get current file ID + HTTPUpload& upload = (web_interface->web_server).upload(); + //Upload start + //************** + if(upload.status == UPLOAD_FILE_START) { + ESPCOM::println (F ("Update Firmware"), PRINTER_PIPE); + web_interface->_upload_status= UPLOAD_STATUS_ONGOING; +#if defined ( ARDUINO_ARCH_ESP8266) + WiFiUDP::stopAll(); +#endif +#if defined ( ARDUINO_ARCH_ESP8266) + maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; +#else +//Not sure can do OTA on 2Mb board + maxSketchSpace = (ESP.getFlashChipSize()>0x20000)?0x140000:0x140000/2; +#endif + last_upload_update = 0; + if(!Update.begin(maxSketchSpace)) { //start with max available size + web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; +#if defined ( ARDUINO_ARCH_ESP8266) + web_interface->web_server.client().stopAll(); +#else + web_interface->web_server.client().stop(); +#endif + } else { + if (( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER)) ESPCOM::println (F ("Update 0%%"), PRINTER_PIPE); + else ESPCOM::println (F ("Update 0%"), PRINTER_PIPE); + } + //Upload write + //************** + } else if(upload.status == UPLOAD_FILE_WRITE) { + //check if no error + if (web_interface->_upload_status == UPLOAD_STATUS_ONGOING) { + //we do not know the total file size yet but we know the available space so let's use it + if ( ((100 * upload.totalSize) / maxSketchSpace) !=last_upload_update) { + last_upload_update = (100 * upload.totalSize) / maxSketchSpace; + String s = "Update "; + s+= String(last_upload_update); + s+= "%"; + if (( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER)) s+= "%"; + ESPCOM::println (s.c_str(), PRINTER_PIPE); + } + if(Update.write(upload.buf, upload.currentSize) != upload.currentSize) { + web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; + } + } + //Upload end + //************** + } else if(upload.status == UPLOAD_FILE_END) { + if(Update.end(true)) { //true to set the size to the current progress + //Now Reboot + if (( CONFIG::GetFirmwareTarget() == REPETIER4DV) || (CONFIG::GetFirmwareTarget() == REPETIER)) ESPCOM::println (F("Update 100%%"), PRINTER_PIPE); + else ESPCOM::println (F("Update 100%"), PRINTER_PIPE); + web_interface->_upload_status=UPLOAD_STATUS_SUCCESSFUL; + } + } else if(upload.status == UPLOAD_FILE_ABORTED) { + ESPCOM::println (F("Update Failed"), PRINTER_PIPE); + Update.end(); + web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; + } + CONFIG::wait(0); +} -//do a redirect to avoid to many query -//and handle not registred path +void handleUpdate() +{ + level_authenticate_type auth_level = web_interface->is_authenticated(); + if (auth_level != LEVEL_ADMIN) { + web_interface->_upload_status=UPLOAD_STATUS_NONE; + web_interface->web_server.send(403,"text/plain","Not allowed, log in first!\n"); + return; + } + String jsonfile = "{\"status\":\"" ; + jsonfile+=CONFIG::intTostr(web_interface->_upload_status); + jsonfile+="\"}"; + //send status + web_interface->web_server.sendHeader("Cache-Control", "no-cache"); + web_interface->web_server.send(200, "application/json", jsonfile); + //if success restart + if (web_interface->_upload_status==UPLOAD_STATUS_SUCCESSFUL) { + CONFIG::wait(2000); + web_interface->restartmodule=true; + } else { + web_interface->_upload_status=UPLOAD_STATUS_NONE; + } +} +#endif + +//Handle not registred path on SPIFFS neither SD /////////////////////// void handle_not_found() { static const char NOT_AUTH_NF [] PROGMEM = "HTTP/1.1 301 OK\r\nLocation: /\r\nCache-Control: no-cache\r\n\r\n"; @@ -1050,167 +843,7 @@ void handle_not_found() } } -#ifdef AUTHENTICATION_FEATURE -void handle_login() -{ - String smsg; - String sUser,sPassword; - String auths; - int code = 200; - bool msg_alert_error=false; - //disconnect can be done anytime no need to check credential - if (web_interface->web_server.hasArg("DISCONNECT")) { - String cookie = web_interface->web_server.header("Cookie"); - int pos = cookie.indexOf("ESPSESSIONID="); - String sessionID; - if (pos!= -1) { - int pos2 = cookie.indexOf(";",pos); - sessionID = cookie.substring(pos+strlen("ESPSESSIONID="),pos2); - } - web_interface->ClearAuthIP(web_interface->web_server.client().remoteIP(), sessionID.c_str()); - web_interface->web_server.sendHeader("Set-Cookie","ESPSESSIONID=0"); - web_interface->web_server.sendHeader("Cache-Control","no-cache"); - String buffer2send = "{\"status\":\"Ok\",\"authentication_lvl\":\"guest\"}"; - web_interface->web_server.send(code, "application/json", buffer2send); - //web_interface->web_server.client().stop(); - return; - } - - level_authenticate_type auth_level= web_interface->is_authenticated(); - if (auth_level == LEVEL_GUEST) auths = F("guest"); - else if (auth_level == LEVEL_USER) auths = F("user"); - else if (auth_level == LEVEL_ADMIN) auths = F("admin"); - else auths = F("???"); - - //check is it is a submission or a query - if (web_interface->web_server.hasArg("SUBMIT")) { - //is there a correct list of query? - if ( web_interface->web_server.hasArg("PASSWORD")&& web_interface->web_server.hasArg("USER")) { - //USER - sUser = web_interface->web_server.arg("USER"); - if ( !((sUser==FPSTR(DEFAULT_ADMIN_LOGIN)) || (sUser==FPSTR(DEFAULT_USER_LOGIN)))) { - msg_alert_error=true; - smsg=F("Error : Incorrect User"); - code=401; - } - if (msg_alert_error == false) { - //Password - sPassword = web_interface->web_server.arg("PASSWORD"); - String sadminPassword; - - if (!CONFIG::read_string(EP_ADMIN_PWD, sadminPassword, MAX_LOCAL_PASSWORD_LENGTH)) { - sadminPassword=FPSTR(DEFAULT_ADMIN_PWD); - } - - String suserPassword; - - if (!CONFIG::read_string(EP_USER_PWD, suserPassword, MAX_LOCAL_PASSWORD_LENGTH)) { - suserPassword=FPSTR(DEFAULT_USER_PWD); - } - - if(!(((sUser==FPSTR(DEFAULT_ADMIN_LOGIN)) && (strcmp(sPassword.c_str(),sadminPassword.c_str())==0)) || - ((sUser==FPSTR(DEFAULT_USER_LOGIN)) && (strcmp(sPassword.c_str(),suserPassword.c_str()) == 0)))) { - msg_alert_error=true; - smsg=F("Error: Incorrect password"); - code = 401; - } - } - } else { - msg_alert_error=true; - smsg = F("Error: Missing data"); - code = 500; - } - //change password - if ( web_interface->web_server.hasArg("PASSWORD")&& web_interface->web_server.hasArg("USER") && web_interface->web_server.hasArg("NEWPASSWORD") && (msg_alert_error==false) ) { - String newpassword = web_interface->web_server.arg("NEWPASSWORD"); - if (CONFIG::isLocalPasswordValid(newpassword.c_str())) { - int pos=0; - if(sUser==FPSTR(DEFAULT_ADMIN_LOGIN)) pos = EP_ADMIN_PWD; - else pos = EP_USER_PWD; - if (!CONFIG::write_string(pos,newpassword.c_str())){ - msg_alert_error=true; - smsg = F("Error: Cannot apply changes"); - code = 500; - } - } else { - msg_alert_error=true; - smsg = F("Error: Incorrect password"); - code = 500; - } - } - if ((code == 200) || (code == 500)) { - level_authenticate_type current_auth_level; - if(sUser == FPSTR(DEFAULT_ADMIN_LOGIN)) { - current_auth_level = LEVEL_ADMIN; - } else if(sUser == FPSTR(DEFAULT_USER_LOGIN)){ - current_auth_level = LEVEL_USER; - } else { - current_auth_level = LEVEL_GUEST; - } - //create Session - if ((current_auth_level != auth_level) || (auth_level== LEVEL_GUEST)) { - auth_ip * current_auth = new auth_ip; - current_auth->level = current_auth_level; - current_auth->ip=web_interface->web_server.client().remoteIP(); - strcpy(current_auth->sessionID,web_interface->create_session_ID()); - strcpy(current_auth->userID,sUser.c_str()); - current_auth->last_time=millis(); - if (web_interface->AddAuthIP(current_auth)) { - String tmps ="ESPSESSIONID="; - tmps+=current_auth->sessionID; - web_interface->web_server.sendHeader("Set-Cookie",tmps); - web_interface->web_server.sendHeader("Cache-Control","no-cache"); - switch(current_auth->level) { - case LEVEL_ADMIN: - auths = "admin"; - break; - case LEVEL_USER: - auths = "user"; - break; - default: - auths = "guest"; - } - } else { - delete current_auth; - msg_alert_error=true; - code = 500; - smsg = F("Error: Too many connections"); - } - } - } - if (code == 200) smsg = F("Ok"); - - //build JSON - String buffer2send = "{\"status\":\"" + smsg + "\",\"authentication_lvl\":\""; - buffer2send += auths; - buffer2send += "\"}"; - web_interface->web_server.send(code, "application/json", buffer2send); - } else { - if (auth_level != LEVEL_GUEST) { - String cookie = web_interface->web_server.header("Cookie"); - int pos = cookie.indexOf("ESPSESSIONID="); - String sessionID; - if (pos!= -1) { - int pos2 = cookie.indexOf(";",pos); - sessionID = cookie.substring(pos+strlen("ESPSESSIONID="),pos2); - auth_ip * current_auth_info = web_interface->GetAuth(web_interface->web_server.client().remoteIP(), sessionID.c_str()); - if (current_auth_info != NULL){ - sUser = current_auth_info->userID; - } - } - } - String buffer2send = "{\"status\":\"200\",\"authentication_lvl\":\""; - buffer2send += auths; - buffer2send += "\",\"user\":\""; - buffer2send += sUser; - buffer2send +="\"}"; - web_interface->web_server.send(code, "application/json", buffer2send); - } -} -#endif - - -//Handle web command query and send answer +//Handle web command query and send answer ///////////////////////////// void handle_web_command() { level_authenticate_type auth_level= web_interface->is_authenticated(); @@ -1219,6 +852,7 @@ void handle_web_command() return; }*/ String buffer2send = ""; + ESPResponseStream espresponse; LOG(String (web_interface->web_server.args())) LOG(" Web command\r\n") #ifdef DEBUG_ESP3D @@ -1267,8 +901,8 @@ void handle_web_command() } //if command is a valid number then execute command if(cmd_part1.toInt()!=0) { - COMMAND::execute_command(cmd_part1.toInt(), cmd_part2, WEB_PIPE, auth_level); - BRIDGE::flush(WEB_PIPE); + COMMAND::execute_command(cmd_part1.toInt(), cmd_part2, WEB_PIPE, auth_level, &espresponse); + ESPCOM::flush(WEB_PIPE, &espresponse); } //if not is not a valid [ESPXXX] command } @@ -1285,9 +919,9 @@ void handle_web_command() LOG("Block Serial\r\n") //empty the serial buffer and incoming data LOG("Start PurgeSerial\r\n") - if(ESP_SERIAL_OUT.available()) { - BRIDGE::processFromSerial2TCP(); - delay(1); + if(ESPCOM::available(DEFAULT_PRINTER_PIPE)) { + ESPCOM::bridge(); + CONFIG::wait(1); } LOG("End PurgeSerial\r\n") web_interface->web_server.setContentLength(CONTENT_LENGTH_UNKNOWN); @@ -1297,13 +931,13 @@ void handle_web_command() //send command LOG(String(cmd.length())) LOG("Start PurgeSerial\r\n") - if(ESP_SERIAL_OUT.available()) { - BRIDGE::processFromSerial2TCP(); - delay(1); + if(ESPCOM::available(DEFAULT_PRINTER_PIPE)) { + ESPCOM::bridge(); + CONFIG::wait(1); } LOG("End PurgeSerial\r\n") LOG("Send Command\r\n") - ESP_SERIAL_OUT.println(cmd); + ESPCOM::println (cmd, DEFAULT_PRINTER_PIPE); count = 0; String current_buffer; String current_line; @@ -1314,12 +948,12 @@ void handle_web_command() //pickup the list while (count < MAX_TRY) { //give some time between each buffer - if (ESP_SERIAL_OUT.available()) { + if (ESPCOM::available(DEFAULT_PRINTER_PIPE)) { count = 0; - size_t len = ESP_SERIAL_OUT.available(); + size_t len = ESPCOM::available(DEFAULT_PRINTER_PIPE); uint8_t sbuf[len+1]; //read buffer - ESP_SERIAL_OUT.readBytes(sbuf, len); + ESPCOM::readBytes (DEFAULT_PRINTER_PIPE, sbuf, len); //change buffer as string sbuf[len]='\0'; //add buffer to current one if any @@ -1376,9 +1010,9 @@ void handle_web_command() current_buffer = tmp; delay(0); } - delay (0); + CONFIG::wait (0); } else { - delay(1); + CONFIG::wait(1); } //it is sending too many temp status should be heating so let's exit the loop if (temp_counter > 5) { @@ -1396,9 +1030,9 @@ void handle_web_command() } web_interface->web_server.sendContent(""); LOG("Start PurgeSerial\r\n") - if(ESP_SERIAL_OUT.available()) { - BRIDGE::processFromSerial2TCP(); - delay(1); + if(ESPCOM::available(DEFAULT_PRINTER_PIPE)) { + ESPCOM::bridge(); + CONFIG::wait(1); } LOG("End PurgeSerial\r\n") web_interface->blockserial = false; @@ -1409,7 +1043,7 @@ void handle_web_command() } } -//Handle web command query and sent ack or fail instead of answer +//Handle web command query and sent ack or fail instead of answer ////// void handle_web_command_silent() { level_authenticate_type auth_level= web_interface->is_authenticated(); @@ -1476,7 +1110,7 @@ void handle_web_command_silent() if ((web_interface->blockserial) == false) { LOG("Send Command\r\n") //send command - ESP_SERIAL_OUT.println(cmd); + ESPCOM::println (cmd, DEFAULT_PRINTER_PIPE); web_interface->web_server.send(200,"text/plain","ok"); } else { web_interface->web_server.send(200,"text/plain","Serial is busy, retry later!"); @@ -1485,256 +1119,214 @@ void handle_web_command_silent() } -#ifdef SSDP_FEATURE -void handle_SSDP() -{ - SSDP.schema(web_interface->web_server.client()); -} -#endif -//constructor -WEBINTERFACE_CLASS::WEBINTERFACE_CLASS (int port):web_server(port) +//Serial SD files list////////////////////////////////////////////////// +void handle_serial_SDFileList() { - //init what will handle "/" - web_server.on("/",HTTP_ANY, handle_web_interface_root); - web_server.on("/command",HTTP_ANY, handle_web_command); - web_server.on("/command_silent",HTTP_ANY, handle_web_command_silent); - web_server.on("/upload_serial", HTTP_ANY, handle_serial_SDFileList,SDFile_serial_upload); - web_server.on("/files", HTTP_ANY, handleFileList,SPIFFSFileupload); -#ifdef WEB_UPDATE_FEATURE - web_server.on("/updatefw",HTTP_ANY, handleUpdate,WebUpdateUpload); -#endif -#ifdef AUTHENTICATION_FEATURE - web_server.on("/login", HTTP_ANY, handle_login); -#endif - //TODO: to be reviewed - web_server.on("/STATUS",HTTP_ANY, handle_web_interface_status); -#ifdef SSDP_FEATURE - web_server.on("/description.xml", HTTP_GET, handle_SSDP); -#endif -#ifdef CAPTIVE_PORTAL_FEATURE - web_server.on("/generate_204",HTTP_ANY, handle_web_interface_root); - web_server.on("/gconnectivitycheck.gstatic.com",HTTP_ANY, handle_web_interface_root); - //do not forget the / at the end - web_server.on("/fwlink/",HTTP_ANY, handle_web_interface_root); -#endif - web_server.onNotFound( handle_not_found); - blockserial = false; - restartmodule=false; - //rolling list of 4entries with a maximum of 50 char for each entry -#ifdef ERROR_MSG_FEATURE - error_msg.setsize(4); - error_msg.setlength(50); -#endif -#ifdef INFO_MSG_FEATURE - info_msg.setsize(4); - info_msg.setlength(50); -#endif -#ifdef STATUS_MSG_FEATURE - status_msg.setsize(4); - status_msg.setlength(50); -#endif - fsUploadFile=(FS_FILE)0; - _head=NULL; - _nb_ip=0; - _upload_status=UPLOAD_STATUS_NONE; -} -//Destructor -WEBINTERFACE_CLASS::~WEBINTERFACE_CLASS() -{ -#ifdef INFO_MSG_FEATURE - info_msg.clear(); -#endif -#ifdef ERROR_MSG_FEATURE - error_msg.clear(); -#endif -#ifdef STATUS_MSG_FEATURE - status_msg.clear(); -#endif - while (_head) { - auth_ip * current = _head; - _head=_head->_next; - delete current; + //this is only for admin an user + if (web_interface->is_authenticated() == LEVEL_GUEST) { + web_interface->_upload_status=UPLOAD_STATUS_NONE; + web_interface->web_server.sendHeader("Cache-Control", "no-cache"); + web_interface->web_server.send(401, "application/json", "{\"status\":\"Authentication failed!\"}"); + return; } - _nb_ip=0; -} -//check authentification -level_authenticate_type WEBINTERFACE_CLASS::is_authenticated() -{ -#ifdef AUTHENTICATION_FEATURE - if (web_server.hasHeader("Cookie")) { - String cookie = web_server.header("Cookie"); - int pos = cookie.indexOf("ESPSESSIONID="); - if (pos!= -1) { - int pos2 = cookie.indexOf(";",pos); - String sessionID = cookie.substring(pos+strlen("ESPSESSIONID="),pos2); - IPAddress ip = web_server.client().remoteIP(); - //check if cookie can be reset and clean table in same time - return ResetAuthIP(ip,sessionID.c_str()); - } + LOG("serial SD upload done\r\n") + String sstatus="Ok"; + if ((web_interface->_upload_status == UPLOAD_STATUS_FAILED) || (web_interface->_upload_status == UPLOAD_STATUS_CANCELLED)) { + sstatus = "Upload failed"; + web_interface->_upload_status = UPLOAD_STATUS_NONE; } - return LEVEL_GUEST; -#else - return LEVEL_ADMIN; + String jsonfile = "{\"status\":\"" + sstatus + "\"}"; + web_interface->web_server.sendHeader("Cache-Control", "no-cache"); + web_interface->web_server.send(200, "application/json", jsonfile); + web_interface->blockserial = false; + web_interface->_upload_status=UPLOAD_STATUS_NONE; +} + +#define NB_RETRY 5 +#define MAX_RESEND_BUFFER 128 +#define SERIAL_CHECK_TIMEOUT 2000 +//SD file upload by serial +void SDFile_serial_upload() +{ + static String current_line; + static bool is_comment = false; + static String current_filename; + String response; + //Guest cannot upload - only admin and user + if(web_interface->is_authenticated() == LEVEL_GUEST) { + web_interface->_upload_status=UPLOAD_STATUS_CANCELLED; + ESPCOM::println (F ("SD upload rejected"), PRINTER_PIPE); + LOG("SD upload rejected\r\n"); + LOG("Need to stop"); +#if defined ( ARDUINO_ARCH_ESP8266) + web_interface->web_server.client().stopAll(); +#else + web_interface->web_server.client().stop(); #endif -} - -#ifdef AUTHENTICATION_FEATURE -//add the information in the linked list if possible -bool WEBINTERFACE_CLASS::AddAuthIP(auth_ip * item) -{ - if (_nb_ip>MAX_AUTH_IP) { - return false; + return; } - item->_next=_head; - _head=item; - _nb_ip++; - return true; -} - -//Session ID based on IP and time using 16 char -char * WEBINTERFACE_CLASS::create_session_ID() -{ - static char sessionID[17]; -//reset SESSIONID - for (int i=0; i<17; i++) { - sessionID[i]='\0'; - } -//get time - uint32_t now = millis(); -//get remote IP - IPAddress remoteIP=web_server.client().remoteIP(); -//generate SESSIONID - if (0>sprintf(sessionID,"%02X%02X%02X%02X%02X%02X%02X%02X",remoteIP[0],remoteIP[1],remoteIP[2],remoteIP[3],(uint8_t) ((now >> 0) & 0xff),(uint8_t) ((now >> 8) & 0xff),(uint8_t) ((now >> 16) & 0xff),(uint8_t) ((now >> 24) & 0xff))) { - strcpy(sessionID,"NONE"); - } - return sessionID; -} - - - -bool WEBINTERFACE_CLASS::ClearAuthIP(IPAddress ip, const char * sessionID){ - auth_ip * current = _head; - auth_ip * previous = NULL; - bool done = false; - while (current) { - if ((ip == current->ip) && (strcmp(sessionID,current->sessionID)==0)) { - //remove - done = true; - if (current ==_head) { - _head=current->_next; - _nb_ip--; - delete current; - current=_head; - } else { - previous->_next=current->_next; - _nb_ip--; - delete current; - current=previous->_next; - } - } else { - previous = current; - current=current->_next; - } - } - return done; -} - -//Get info -auth_ip * WEBINTERFACE_CLASS::GetAuth(IPAddress ip,const char * sessionID) -{ - auth_ip * current = _head; - auth_ip * previous = NULL; - //get time - //uint32_t now = millis(); - while (current) { - if (ip==current->ip) { - if (strcmp(sessionID,current->sessionID)==0) { - //found - return current; - } - } - previous = current; - current=current->_next; - } - return NULL; -} - -//Review all IP to reset timers -level_authenticate_type WEBINTERFACE_CLASS::ResetAuthIP(IPAddress ip,const char * sessionID) -{ - auth_ip * current = _head; - auth_ip * previous = NULL; - //get time - //uint32_t now = millis(); - while (current) { - if ((millis()-current->last_time)>180000) { - //remove - if (current==_head) { - _head=current->_next; - _nb_ip--; - delete current; - current=_head; - } else { - previous->_next=current->_next; - _nb_ip--; - delete current; - current=previous->_next; - } - } else { - if (ip==current->ip) { - if (strcmp(sessionID,current->sessionID)==0) { - //reset time - current->last_time=millis(); - return (level_authenticate_type)current->level; + //retrieve current file id + HTTPUpload& upload = (web_interface->web_server).upload(); + //Upload start + //************** + if(upload.status == UPLOAD_FILE_START) { + //need to lock serial out to avoid garbage in file + (web_interface->blockserial) = true; + current_line =""; + is_comment = false; + String response; + web_interface->_upload_status= UPLOAD_STATUS_ONGOING; + ESPCOM::println (F ("Uploading..."), PRINTER_PIPE); + ESPCOM::flush (DEFAULT_PRINTER_PIPE); + LOG("Clear Serial\r\n"); + if(ESPCOM::available(DEFAULT_PRINTER_PIPE)) { + ESPCOM::bridge(); + CONFIG::wait(1); + } + //command to pritnter to start print + String command = "M28 " + upload.filename; + LOG(command); + LOG("\r\n"); + ESPCOM::println (command, DEFAULT_PRINTER_PIPE); + ESPCOM::flush (DEFAULT_PRINTER_PIPE); + current_filename = upload.filename; + CONFIG::wait (500); + uint32_t timeout = millis(); + bool done = false; + while (!done) { //time out is 2000ms + CONFIG::wait(0); + //if there is something in serial buffer + size_t len = ESPCOM::available(DEFAULT_PRINTER_PIPE); + //get size of buffer + if (len > 0) { + CONFIG::wait(0); + uint8_t sbuf[len + 1]; + //read buffer + ESPCOM::readBytes (DEFAULT_PRINTER_PIPE, sbuf, len); + //convert buffer to zero end array + sbuf[len] = '\0'; + //use string because easier to handle + response = (const char*) sbuf; + LOG (response); + //if there is a wait it means purge is done + if (response.indexOf ("wait") > -1) { + LOG ("Exit start writing\r\n"); + done = true; + break; + } + //it is first command if it is failed no need to continue + //and resend command won't help + if (response.indexOf ("Resend") > -1 || response.indexOf ("failed") > -1) { + web_interface->blockserial = false; + LOG ("Error start writing\r\n"); + web_interface->_upload_status = UPLOAD_STATUS_FAILED; +#if defined ( ARDUINO_ARCH_ESP8266) + web_interface->web_server.client().stopAll(); +#else + web_interface->web_server.client().stop(); +#endif + return; } } - previous = current; - current=current->_next; + if ( (millis() - timeout) > SERIAL_CHECK_TIMEOUT) { + done = true; + } } - } - return LEVEL_GUEST; -} + //Upload write + //************** + //upload is on going with data coming by 2K blocks + } else if(upload.status == UPLOAD_FILE_WRITE) { //if com error no need to send more data to serial + web_interface->_upload_status= UPLOAD_STATUS_ONGOING; + for (int pos = 0; pos < upload.currentSize; pos++) { //parse full post data + //feed watchdog + CONFIG::wait(0); + //it is a comment + if (upload.buf[pos] == ';') { + LOG ("Comment\n") + is_comment = true; + } + //it is an end line + else if ( (upload.buf[pos] == 13) || (upload.buf[pos] == 10) ) { + //if comment line then reset + is_comment = false; + //does line fit the buffer ? + if (current_line.length() < 126) { + //do we have something in buffer ? + if (current_line.length() > 0 ) { + current_line += "\r\n"; + if (!sendLine2Serial (current_line) ) { + LOG ("Error over buffer\n") + CloseSerialUpload (true, current_filename); +#if defined ( ARDUINO_ARCH_ESP8266) + web_interface->web_server.client().stopAll(); +#else + web_interface->web_server.client().stop(); #endif + return; + } + //reset line + current_line = ""; -//Check what is the content tye according extension file -String WEBINTERFACE_CLASS::getContentType(String filename) -{ - 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(".jpeg")) { - return "image/jpeg"; - } 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"; - } else if(filename.endsWith(".tpl")) { - return "text/plain"; - } else if(filename.endsWith(".inc")) { - return "text/plain"; - } else if(filename.endsWith(".txt")) { - return "text/plain"; + } else { + LOG ("Empy line\n") + } + } else { + //error buffer overload + LOG ("Error over buffer\n") + CloseSerialUpload (true, current_filename); +#if defined ( ARDUINO_ARCH_ESP8266) + web_interface->web_server.client().stopAll(); +#else + web_interface->web_server.client().stop(); +#endif + return; + } + } else if (!is_comment) { + if (current_line.length() < 126) { + current_line += char (upload.buf[pos]); //copy current char to buffer to send/resend + } else { + LOG ("Error over buffer\n") + CloseSerialUpload (true, current_filename); +#if defined ( ARDUINO_ARCH_ESP8266) + web_interface->web_server.client().stopAll(); +#else + web_interface->web_server.client().stop(); +#endif + return; + } + } + } + //Upload end + //************** + } else if(upload.status == UPLOAD_FILE_END) { + //if last part does not have '\n' + if (current_line.length() > 0) { + current_line += "\r\n"; + if (!sendLine2Serial (current_line) ) { + LOG ("Error sending buffer\n") + CloseSerialUpload (true, current_filename); +#if defined ( ARDUINO_ARCH_ESP8266) + web_interface->web_server.client().stopAll(); +#else + web_interface->web_server.client().stop(); +#endif + return; + } + } + LOG ("Upload finished "); + CloseSerialUpload (false, current_filename); + //Upload cancelled + //************** + } else { //UPLOAD_FILE_ABORTED + LOG("Error, Something happened\r\n"); + CloseSerialUpload (true, current_filename); +#if defined ( ARDUINO_ARCH_ESP8266) + web_interface->web_server.client().stopAll(); +#else + web_interface->web_server.client().stop(); +#endif } - return "application/octet-stream"; } - -WEBINTERFACE_CLASS * web_interface; +#endif diff --git a/src/syncwebserver.h b/src/syncwebserver.h new file mode 100644 index 00000000..4ce6421d --- /dev/null +++ b/src/syncwebserver.h @@ -0,0 +1,47 @@ +/* + syncwebserver.h - ESP3D sync functions 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 +*/ + +#ifndef SYNCWEBSERVER_H +#define SYNCWEBSERVER_H +#include "config.h" +#include + +extern void handle_web_interface_root(); +extern void handle_login(); +extern void handleFileList(); +extern void SPIFFSFileupload(); +extern void handle_not_found(); +extern void handle_web_command(); +extern void handle_web_command_silent(); +extern void handle_serial_SDFileList(); +extern void SDFile_serial_upload(); +extern WebSocketsServer * socket_server; +extern void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length); + +#ifdef SSDP_FEATURE +extern void handle_SSDP (); +#endif + +#ifdef WEB_UPDATE_FEATURE +extern void handleUpdate (); +extern void WebUpdateUpload (); +#endif + +#endif diff --git a/src/webinterface.cpp b/src/webinterface.cpp new file mode 100644 index 00000000..c53fc40e --- /dev/null +++ b/src/webinterface.cpp @@ -0,0 +1,412 @@ +/* + webinterface.cpp - ESP3D configuration class + + 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 +*/ + +#include +#include "config.h" +#include "webinterface.h" +#include "wificonf.h" +#include +#include +#include +#include +#ifndef FS_NO_GLOBALS +#define FS_NO_GLOBALS +#endif +#include +#if defined (ASYNCWEBSERVER) +#include +#endif +#ifdef ARDUINO_ARCH_ESP8266 +#include "ESP8266WiFi.h" +#if defined (ASYNCWEBSERVER) +#include +#else +#include +#endif +#else //ESP32 +#include +#if defined (ASYNCWEBSERVER) +#include +#else +#include +#endif +#include "SPIFFS.h" +#include "Update.h" +#endif + +#include "GenLinkedList.h" +#include "command.h" +#include "espcom.h" + +#ifdef SSDP_FEATURE + #ifdef ARDUINO_ARCH_ESP32 + #include + #else + #include + #endif +#endif + +#if defined(ASYNCWEBSERVER) +#include "asyncwebserver.h" +#else +#include "syncwebserver.h" +#endif + +#define MAX_AUTH_IP 10 + +long id_connection = 0; + + + +//function to send line to serial/////////////////////////////////////// +bool sendLine2Serial (String & line) +{ + LOG (line) + ESPCOM::println (line, DEFAULT_PRINTER_PIPE); + ESPCOM::flush(DEFAULT_PRINTER_PIPE); +#ifdef ARDUINO_ARCH_ESP8266 + CONFIG::wait (5); +#else + CONFIG::wait (2); +#endif + if (ESPCOM::available(DEFAULT_PRINTER_PIPE) > 0 ) { + bool done = false; + uint32_t timeout = millis(); + uint8_t count = 0; + while (!done) { + CONFIG::wdtFeed(); + size_t len = ESPCOM::available(DEFAULT_PRINTER_PIPE); + //get size of buffer + if (len > 0) { + uint8_t sbuf[len + 1]; + //read buffer + ESPCOM::readBytes (DEFAULT_PRINTER_PIPE, sbuf, len); + //convert buffer to zero end array + sbuf[len] = '\0'; + //use string because easier to handle + String response = (const char*) sbuf; + if ( (response.indexOf ("ok") > -1) || (response.indexOf ("wait") > -1) ) { + return true; + } + if (response.indexOf ("Resend") > -1) { + count++; + if (count > 5) { + return false; + } + LOG ("resend\r\n") + ESPCOM::println (line, DEFAULT_PRINTER_PIPE); + ESPCOM::flush (DEFAULT_PRINTER_PIPE); + CONFIG::wait (5); + timeout = millis(); + } + } + //no answer so exit: no news = good news + if ( millis() - timeout > 500) { + done = true; + } + } + } + return true; +} + +//send M29 / M30 command to close file on SD//////////////////////////// + +void CloseSerialUpload (bool iserror, String & filename) +{ + + ESPCOM::println ("\r\nM29", DEFAULT_PRINTER_PIPE); + ESPCOM::flush (DEFAULT_PRINTER_PIPE); + CONFIG::wait (1000); + ESPCOM::println ("\r\nM29", DEFAULT_PRINTER_PIPE); + ESPCOM::flush (DEFAULT_PRINTER_PIPE); + if (iserror) { + String cmdfilename = "M30 " + filename; + ESPCOM::println (cmdfilename, DEFAULT_PRINTER_PIPE); + ESPCOM::println (F ("SD upload failed"), PRINTER_PIPE); + ESPCOM::flush (DEFAULT_PRINTER_PIPE); + web_interface->_upload_status = UPLOAD_STATUS_FAILED; + } else { + ESPCOM::println (F ("SD upload done"), PRINTER_PIPE); + ESPCOM::flush (DEFAULT_PRINTER_PIPE); + web_interface->_upload_status = UPLOAD_STATUS_SUCCESSFUL; + } + //lets give time to FW to proceed + CONFIG::wait (1000); + web_interface->blockserial = false; +} + + +//constructor +WEBINTERFACE_CLASS::WEBINTERFACE_CLASS (int port) : web_server (port) +#if defined(ASYNCWEBSERVER) + , web_events("/events") +#ifdef WS_DATA_FEATURE + , web_socket("/ws") +#endif +#endif +{ + //that handle "/" and default index.html.gz +#if defined(ASYNCWEBSERVER) + //trick to catch command line on "/" before file being processed + web_server.serveStatic ("/", SPIFFS, "/").setDefaultFile ("index.html").setFilter (filterOnRoot); + web_server.serveStatic ("/", SPIFFS, "/Nowhere"); + //events functions + web_events.onConnect(handle_onevent_connect); + //events management + web_server.addHandler(&web_events); +#ifdef WS_DATA_FEATURE + //Websocket function + web_socket.onEvent(handle_Websocket_Event); + //Websocket management + web_server.addHandler(&web_socket); +#endif +#else + web_server.on("/",HTTP_ANY, handle_web_interface_root); +#endif + //need to be there even no authentication to say to UI no authentication + web_server.on("/login", HTTP_ANY, handle_login); +#ifdef SSDP_FEATURE + web_server.on ("/description.xml", HTTP_GET, handle_SSDP); +#endif +#ifdef CAPTIVE_PORTAL_FEATURE + #if defined(ASYNCWEBSERVER) + web_server.on ("/generate_204", HTTP_ANY, [] (AsyncWebServerRequest * request) { + request->redirect ("/"); + }); + web_server.on ("/gconnectivitycheck.gstatic.com", HTTP_ANY, [] (AsyncWebServerRequest * request) { + request->redirect ("/"); + }); + //do not forget the / at the end + web_server.on ("/fwlink/", HTTP_ANY, [] (AsyncWebServerRequest * request) { + request->redirect ("/"); + }); + #else + web_server.on("/generate_204",HTTP_ANY, handle_web_interface_root); + web_server.on("/gconnectivitycheck.gstatic.com",HTTP_ANY, handle_web_interface_root); + //do not forget the / at the end + web_server.on("/fwlink/",HTTP_ANY, handle_web_interface_root); + #endif +#endif + //SPIFFS + web_server.on ("/files", HTTP_ANY, handleFileList, SPIFFSFileupload); +#ifdef WEB_UPDATE_FEATURE + web_server.on ("/updatefw", HTTP_ANY, handleUpdate, WebUpdateUpload); +#endif + //Page not found handler + web_server.onNotFound ( handle_not_found); + //web commands + web_server.on ("/command", HTTP_ANY, handle_web_command); + web_server.on ("/command_silent", HTTP_ANY, handle_web_command_silent); + //Serial SD management + web_server.on ("/upload_serial", HTTP_ANY, handle_serial_SDFileList, SDFile_serial_upload); + + blockserial = false; + restartmodule = false; + _head = NULL; + _nb_ip = 0; + _upload_status = UPLOAD_STATUS_NONE; +} +//Destructor +WEBINTERFACE_CLASS::~WEBINTERFACE_CLASS() +{ + while (_head) { + auth_ip * current = _head; + _head = _head->_next; + delete current; + } + _nb_ip = 0; +} +//check authentification +level_authenticate_type WEBINTERFACE_CLASS::is_authenticated() +{ +#ifdef AUTHENTICATION_FEATURE + if (web_server.hasHeader ("Cookie") ) { + String cookie = web_server.header ("Cookie"); + int pos = cookie.indexOf ("ESPSESSIONID="); + if (pos != -1) { + int pos2 = cookie.indexOf (";", pos); + String sessionID = cookie.substring (pos + strlen ("ESPSESSIONID="), pos2); + IPAddress ip = web_server.client().remoteIP(); + //check if cookie can be reset and clean table in same time + return ResetAuthIP (ip, sessionID.c_str() ); + } + } + return LEVEL_GUEST; +#else + return LEVEL_ADMIN; +#endif +} + +#ifdef AUTHENTICATION_FEATURE +//add the information in the linked list if possible +bool WEBINTERFACE_CLASS::AddAuthIP (auth_ip * item) +{ + if (_nb_ip > MAX_AUTH_IP) { + return false; + } + item->_next = _head; + _head = item; + _nb_ip++; + return true; +} + +//Session ID based on IP and time using 16 char +char * WEBINTERFACE_CLASS::create_session_ID() +{ + static char sessionID[17]; +//reset SESSIONID + for (int i = 0; i < 17; i++) { + sessionID[i] = '\0'; + } +//get time + uint32_t now = millis(); +//get remote IP + IPAddress remoteIP = web_server.client().remoteIP(); +//generate SESSIONID + if (0 > sprintf (sessionID, "%02X%02X%02X%02X%02X%02X%02X%02X", remoteIP[0], remoteIP[1], remoteIP[2], remoteIP[3], (uint8_t) ( (now >> 0) & 0xff), (uint8_t) ( (now >> 8) & 0xff), (uint8_t) ( (now >> 16) & 0xff), (uint8_t) ( (now >> 24) & 0xff) ) ) { + strcpy (sessionID, "NONE"); + } + return sessionID; +} + + + +bool WEBINTERFACE_CLASS::ClearAuthIP (IPAddress ip, const char * sessionID) +{ + auth_ip * current = _head; + auth_ip * previous = NULL; + bool done = false; + while (current) { + if ( (ip == current->ip) && (strcmp (sessionID, current->sessionID) == 0) ) { + //remove + done = true; + if (current == _head) { + _head = current->_next; + _nb_ip--; + delete current; + current = _head; + } else { + previous->_next = current->_next; + _nb_ip--; + delete current; + current = previous->_next; + } + } else { + previous = current; + current = current->_next; + } + } + return done; +} + +//Get info +auth_ip * WEBINTERFACE_CLASS::GetAuth (IPAddress ip, const char * sessionID) +{ + auth_ip * current = _head; + auth_ip * previous = NULL; + //get time + //uint32_t now = millis(); + while (current) { + if (ip == current->ip) { + if (strcmp (sessionID, current->sessionID) == 0) { + //found + return current; + } + } + previous = current; + current = current->_next; + } + return NULL; +} + +//Review all IP to reset timers +level_authenticate_type WEBINTERFACE_CLASS::ResetAuthIP (IPAddress ip, const char * sessionID) +{ + auth_ip * current = _head; + auth_ip * previous = NULL; + //get time + //uint32_t now = millis(); + while (current) { + if ( (millis() - current->last_time) > 180000) { + //remove + if (current == _head) { + _head = current->_next; + _nb_ip--; + delete current; + current = _head; + } else { + previous->_next = current->_next; + _nb_ip--; + delete current; + current = previous->_next; + } + } else { + if (ip == current->ip) { + if (strcmp (sessionID, current->sessionID) == 0) { + //reset time + current->last_time = millis(); + return (level_authenticate_type) current->level; + } + } + previous = current; + current = current->_next; + } + } + return LEVEL_GUEST; +} +#endif + +//Check what is the content tye according extension file +String WEBINTERFACE_CLASS::getContentType (String filename) +{ + 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 (".jpeg") ) { + return "image/jpeg"; + } 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"; + } else if (filename.endsWith (".txt") ) { + return "text/plain"; + } + return "application/octet-stream"; +} + + +WEBINTERFACE_CLASS * web_interface; diff --git a/esp3d/webinterface.h b/src/webinterface.h similarity index 70% rename from esp3d/webinterface.h rename to src/webinterface.h index de3874a9..bcf77b8f 100644 --- a/esp3d/webinterface.h +++ b/src/webinterface.h @@ -27,17 +27,24 @@ #define FS_NO_GLOBALS #endif #include +#if defined(ASYNCWEBSERVER) +#include +#endif #ifdef ARDUINO_ARCH_ESP8266 +#if defined(ASYNCWEBSERVER) +#include +#else #include +#endif +#else //ESP32 +#if defined(ASYNCWEBSERVER) +#include #else #include #endif +#endif -#include "storestrings.h" - -#define MAX_EXTRUDERS 4 - struct auth_ip { IPAddress ip; level_authenticate_type level; @@ -52,30 +59,30 @@ class WEBINTERFACE_CLASS public: WEBINTERFACE_CLASS (int port = 80); ~WEBINTERFACE_CLASS(); -#ifdef ARDUINO_ARCH_ESP8266 - ESP8266WebServer web_server; +#if defined(ASYNCWEBSERVER) + AsyncWebServer web_server; + AsyncEventSource web_events; #else + #ifdef ARDUINO_ARCH_ESP8266 + ESP8266WebServer web_server; + #else WebServer web_server; + #endif #endif - FS_FILE fsUploadFile; -#ifdef ERROR_MSG_FEATURE - STORESTRINGS_CLASS error_msg; +#ifdef WS_DATA_FEATURE +#if defined(ASYNCWEBSERVER) + AsyncWebSocket web_socket; #endif -#ifdef INFO_MSG_FEATURE - STORESTRINGS_CLASS info_msg; -#endif -#ifdef STATUS_MSG_FEATURE - STORESTRINGS_CLASS status_msg; #endif bool restartmodule; - String getContentType(String filename); + String getContentType (String filename); level_authenticate_type is_authenticated(); - bool AddAuthIP(auth_ip * item); + 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); + 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; diff --git a/src/wificonf.cpp b/src/wificonf.cpp new file mode 100644 index 00000000..b85f90d0 --- /dev/null +++ b/src/wificonf.cpp @@ -0,0 +1,666 @@ +/* + wificonf.cpp - ESP3D configuration class + + 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 +*/ +#include "config.h" +#include "wificonf.h" +#include "espcom.h" +#include "webinterface.h" + +#ifdef ARDUINO_ARCH_ESP8266 +#include "ESP8266WiFi.h" +#ifdef MDNS_FEATURE +#include +#endif +#else +#include +#include "esp_wifi.h" +#ifdef MDNS_FEATURE +#include +#endif +#endif +#include "IPAddress.h" +#ifdef CAPTIVE_PORTAL_FEATURE +#include +DNSServer dnsServer; +const byte DNS_PORT = 53; +#endif +#ifdef SSDP_FEATURE + #ifdef ARDUINO_ARCH_ESP8266 + #include + #else + #include + #endif +#endif +#ifdef NETBIOS_FEATURE +#ifdef ARDUINO_ARCH_ESP8266 +#include +#else +#include +#endif +#endif +#ifdef ARDUINO_ARCH_ESP8266 +extern "C" { +#include "user_interface.h" +} +#endif +#ifdef TIMESTAMP_FEATURE +#include +#endif + +#ifdef ESP_OLED_FEATURE +#include "esp_oled.h" +#endif + +#if defined (ASYNCWEBSERVER) +#include "asyncwebserver.h" +#else +#include "syncwebserver.h" +#endif + +#if defined(TIMESTAMP_FEATURE) && defined(ARDUINO_ARCH_ESP8266) +void dateTime (uint16_t* date, uint16_t* dtime) +{ + struct tm tmstruct; + time_t now; + time (&now); + localtime_r (&now, &tmstruct); + *date = FAT_DATE ( (tmstruct.tm_year) + 1900, ( tmstruct.tm_mon) + 1, tmstruct.tm_mday); + *dtime = FAT_TIME (tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec); +} +#endif + +WIFI_CONFIG::WIFI_CONFIG() +{ + iweb_port = DEFAULT_WEB_PORT; + idata_port = DEFAULT_DATA_PORT; + baud_rate = DEFAULT_BAUD_RATE; + sleep_mode = DEFAULT_SLEEP_MODE; + _hostname[0] = 0; + WiFi_on = true; +} + +int32_t WIFI_CONFIG::getSignal (int32_t RSSI) +{ + if (RSSI <= -100) { + return 0; + } + if (RSSI >= -50) { + return 100; + } + return (2 * (RSSI + 100) ); +} + +const char * WIFI_CONFIG::get_hostname() +{ + 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, hname.c_str() ); + } + return _hostname; +} + +const char * WIFI_CONFIG::get_default_hostname() +{ + static char hostname[33]; + uint8_t mac [WL_MAC_ADDR_LENGTH]; + WiFi.macAddress (mac); +#if defined (ESP_HOST_NAME) + strcpy (hostname, ESP_DEFAULT_NAME); +#else + if (0 > sprintf (hostname, "ESP_%02X%02X%02X", mac[3], mac[4], mac[5]) ) { + strcpy (hostname, ESP_DEFAULT_NAME); + } +#endif + return hostname; +} + +//safe setup if no connection +void WIFI_CONFIG::Safe_Setup() +{ +#ifdef CAPTIVE_PORTAL_FEATURE + dnsServer.stop(); + CONFIG::wait(100); +#endif + + WiFi.disconnect(); + //setup Soft AP + WiFi.mode (WIFI_AP); + IPAddress local_ip (DEFAULT_IP_VALUE[0], DEFAULT_IP_VALUE[1], DEFAULT_IP_VALUE[2], DEFAULT_IP_VALUE[3]); + IPAddress gateway (DEFAULT_GATEWAY_VALUE[0], DEFAULT_GATEWAY_VALUE[1], DEFAULT_GATEWAY_VALUE[2], DEFAULT_GATEWAY_VALUE[3]); + IPAddress subnet (DEFAULT_MASK_VALUE[0], DEFAULT_MASK_VALUE[1], DEFAULT_MASK_VALUE[2], DEFAULT_MASK_VALUE[3]); + String ssid = FPSTR (DEFAULT_AP_SSID); + String pwd = FPSTR (DEFAULT_AP_PASSWORD); + WiFi.softAP (ssid.c_str(), pwd.c_str() ); + CONFIG::wait (500); + WiFi.softAPConfig ( local_ip, gateway, subnet); + CONFIG::wait (1000); +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::display_signal(100); + OLED_DISPLAY::setCursor(0, 0); + ESPCOM::print(ssid.c_str(), OLED_PIPE); + OLED_DISPLAY::setCursor(0, 16); + ESPCOM::print(local_ip.toString().c_str(), OLED_PIPE); +#endif + ESPCOM::println (F ("Safe mode started"), PRINTER_PIPE); +} + + +//wifi event +void onWiFiEvent(WiFiEvent_t event){ + + switch (event) { + case WIFI_EVENT_STAMODE_CONNECTED: + ESPCOM::println (F ("Connected"), PRINTER_PIPE); +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::display_signal(wifi_config.getSignal (WiFi.RSSI ())); + OLED_DISPLAY::setCursor(0, 0); + ESPCOM::print("", OLED_PIPE); +#endif + break; + case WIFI_EVENT_STAMODE_DISCONNECTED: + ESPCOM::println (F ("Disconnected"), PRINTER_PIPE); +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::display_signal(-1); + OLED_DISPLAY::setCursor(0, 16); + ESPCOM::print("", OLED_PIPE); + OLED_DISPLAY::setCursor(0, 48); +#endif + break; + case WIFI_EVENT_STAMODE_GOT_IP: + ESPCOM::println (WiFi.localIP().toString().c_str(), PRINTER_PIPE); +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::setCursor(0, 16); + ESPCOM::print(WiFi.localIP().toString().c_str(), OLED_PIPE); + OLED_DISPLAY::setCursor(0, 48); + ESPCOM::print("", OLED_PIPE); +#endif + break; + case WIFI_EVENT_SOFTAPMODE_STACONNECTED: + ESPCOM::println (F ("New client"), PRINTER_PIPE); + break; +#ifdef ARDUINO_ARCH_ESP32 + case SYSTEM_EVENT_STA_LOST_IP: + break; + case SYSTEM_EVENT_ETH_CONNECTED: + break; + case SYSTEM_EVENT_ETH_DISCONNECTED: + break; + case SYSTEM_EVENT_ETH_GOT_IP: + break; +#endif + } + +} + +//Read configuration settings and apply them +bool WIFI_CONFIG::Setup (bool force_ap) +{ + char pwd[MAX_PASSWORD_LENGTH + 1]; + char sbuf[MAX_SSID_LENGTH + 1]; + char hostname [MAX_HOSTNAME_LENGTH + 1]; + //int wstatus; + IPAddress currentIP; + byte bflag = 0; + byte bmode = 0; +#ifdef ARDUINO_ARCH_ESP8266 + WiFi.onEvent(onWiFiEvent, WIFI_EVENT_ANY); +#else + WiFi.onEvent(onWiFiEvent); +#endif + //system_update_cpu_freq(SYS_CPU_160MHZ); + //set the sleep mode + if (!CONFIG::read_byte (EP_SLEEP_MODE, &bflag ) ) { + LOG ("Error read Sleep mode\r\n") + return false; + } +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::clear_lcd(); +#endif + + sleep_mode = bflag; + if (force_ap){ + bmode = AP_MODE; + } else { + //AP or client ? + if (!CONFIG::read_byte (EP_WIFI_MODE, &bmode ) ) { + LOG ("Error read wifi mode\r\n") + return false; + } + } + if (!CONFIG::read_string (EP_HOSTNAME, hostname, MAX_HOSTNAME_LENGTH) ) { + strcpy (hostname, get_default_hostname() ); + } + //this is AP mode + if (bmode == AP_MODE) { + LOG ("Set AP mode\r\n") + if (!CONFIG::read_string (EP_AP_SSID, sbuf, MAX_SSID_LENGTH) ) { + return false; + } + if (!CONFIG::read_string (EP_AP_PASSWORD, pwd, MAX_PASSWORD_LENGTH) ) { + return false; + } +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::display_signal(100); + OLED_DISPLAY::setCursor(0, 0); + ESPCOM::print(sbuf, OLED_PIPE); +#else + ESPCOM::println (sbuf, PRINTER_PIPE); +#endif + LOG ("SSID ") + LOG (sbuf) + LOG ("\r\n") + //DHCP or Static IP ? + if (!CONFIG::read_byte (EP_AP_IP_MODE, &bflag ) ) { + LOG ("Error IP mode\r\n") + return false; + } + LOG ("IP Mode: ") + LOG (CONFIG::intTostr (bflag) ) + LOG ("\r\n") + if (bflag == STATIC_IP_MODE) { + byte ip_buf[4]; + LOG ("Static mode\r\n") + //get the IP + LOG ("IP value:") + if (!CONFIG::read_buffer (EP_AP_IP_VALUE, ip_buf, IP_LENGTH) ) { + LOG ("Error\r\n") + return false; + } + IPAddress local_ip (ip_buf[0], ip_buf[1], ip_buf[2], ip_buf[3]); + LOG (local_ip.toString() ) + LOG ("\r\nGW value:") + //get the gateway + if (!CONFIG::read_buffer (EP_AP_GATEWAY_VALUE, ip_buf, IP_LENGTH) ) { + LOG ("Error\r\n") + return false; + } + IPAddress gateway (ip_buf[0], ip_buf[1], ip_buf[2], ip_buf[3]); + LOG (gateway.toString() ) + LOG ("\r\nMask value:") + //get the mask + if (!CONFIG::read_buffer (EP_AP_MASK_VALUE, ip_buf, IP_LENGTH) ) { + LOG ("Error Mask value\r\n") + return false; + } + IPAddress subnet (ip_buf[0], ip_buf[1], ip_buf[2], ip_buf[3]); + LOG (subnet.toString() ) + LOG ("\r\n") + //apply according active wifi mode + LOG ("Set IP\r\n") + WiFi.softAPConfig ( local_ip, gateway, subnet); + delay (100); + } + 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; + } + LOG ("Set AP\r\n") + //setup Soft AP + WiFi.mode (WIFI_AP); +#ifdef ARDUINO_ARCH_ESP32 + esp_wifi_set_protocol (ESP_IF_WIFI_AP, bflag); +#endif + wifi_config.WiFi_on = true; + delay (50); + WiFi.softAP (sbuf, pwd); +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::display_signal(100); + OLED_DISPLAY::setCursor(0, 0); + ESPCOM::print(sbuf, OLED_PIPE); +#endif + delay (100); +#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) { + ESPCOM::println (F ("Error Wifi AP!"), PRINTER_PIPE); + 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) ) { + ESPCOM::println (F ("Error Wifi AP!"), PRINTER_PIPE); + delay (1000); + } +#endif + } else { + LOG ("Set STA mode\r\n") + if (!CONFIG::read_string (EP_STA_SSID, sbuf, MAX_SSID_LENGTH) ) { + return false; + } + if (!CONFIG::read_string (EP_STA_PASSWORD, pwd, MAX_PASSWORD_LENGTH) ) { + return false; + } + + ESPCOM::println (sbuf, PRINTER_PIPE); +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::display_signal(-1); + OLED_DISPLAY::setCursor(0, 0); + ESPCOM::print(sbuf, OLED_PIPE); +#endif + LOG ("SSID ") + LOG (sbuf) + LOG ("\r\n") + if (!CONFIG::read_byte (EP_STA_IP_MODE, &bflag ) ) { + return false; + } + if (bflag == STATIC_IP_MODE) { + byte ip_buf[4]; + //get the IP + if (!CONFIG::read_buffer (EP_STA_IP_VALUE, ip_buf, IP_LENGTH) ) { + return false; + } + IPAddress local_ip (ip_buf[0], ip_buf[1], ip_buf[2], ip_buf[3]); + //get the gateway + if (!CONFIG::read_buffer (EP_STA_GATEWAY_VALUE, ip_buf, IP_LENGTH) ) { + return false; + } + IPAddress gateway (ip_buf[0], ip_buf[1], ip_buf[2], ip_buf[3]); + //get the mask + if (!CONFIG::read_buffer (EP_STA_MASK_VALUE, ip_buf, IP_LENGTH) ) { + return false; + } + IPAddress subnet (ip_buf[0], ip_buf[1], ip_buf[2], ip_buf[3]); + //apply according active wifi mode + WiFi.config ( local_ip, gateway, subnet); + } + WiFi.enableAP (false); + delay (100); + if (!CONFIG::read_byte (EP_STA_PHY_MODE, &bflag ) ) { + return false; + } + + //setup station mode + WiFi.mode (WIFI_STA); + wifi_config.WiFi_on = true; + delay (100); +#ifdef ARDUINO_ARCH_ESP8266 + WiFi.setPhyMode ( (WiFiPhyMode_t) bflag); +#else + esp_wifi_set_protocol (ESP_IF_WIFI_STA, bflag); +#endif + WiFi.begin (sbuf, pwd); + delay (100); +#ifdef ARDUINO_ARCH_ESP8266 + WiFi.setSleepMode ( (WiFiSleepType_t) bflag); +#else + //for backward compatibility + if ((wifi_ps_type_t) sleep_mode == WIFI_PS_MAX_MODEM)sleep_mode=WIFI_PS_MIN_MODEM; + esp_wifi_set_ps ( (wifi_ps_type_t) sleep_mode); +#endif + delay (100); + byte i = 0; + //try to connect + byte dot = 0; + String msg; + int last = -1; + while (WiFi.status() != WL_CONNECTED && i < 40) { + switch (WiFi.status() ) { + case 1: +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::display_signal(-1); +#endif + if ((dot == 0) || last!=WiFi.status()) { + msg = F ("No SSID"); + last=WiFi.status(); + } + break; + + case 4: + if ((dot == 0) || last!=WiFi.status()) { + msg = F ("No Connection"); + last=WiFi.status(); + } + break; + + default: +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::display_signal(wifi_config.getSignal (WiFi.RSSI ())); +#endif + if ((dot == 0) || last!=WiFi.status()) { + msg = F ("Connecting"); + last=WiFi.status(); + } + } + 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; + } + ESPCOM::println (msg, PRINTER_PIPE); + + delay (500); + i++; + } + if (WiFi.status() != WL_CONNECTED) { + ESPCOM::println (F ("Not Connected!"), PRINTER_PIPE); + return false; + } +#ifdef ARDUINO_ARCH_ESP8266 + WiFi.hostname (hostname); +#else + WiFi.setHostname (hostname); +#endif + } + + //Get IP + if (WiFi.getMode()== WIFI_AP) { + currentIP = WiFi.softAPIP(); + ESPCOM::println (currentIP.toString().c_str(), PRINTER_PIPE); +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::setCursor(0, 16); + ESPCOM::print(currentIP.toString().c_str(), OLED_PIPE); +#endif + } +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::setCursor(0, 48); + if (force_ap){ + ESPCOM::print("Safe mode 1", OLED_PIPE); + } else if ((WiFi.getMode() == WIFI_STA) && (WiFi.status() == WL_CONNECTED)) { + ESPCOM::print("Connected", OLED_PIPE); + OLED_DISPLAY::setCursor(0, 0); + ESPCOM::print(sbuf, OLED_PIPE); + } + else if (WiFi.getMode() != WIFI_STA) ESPCOM::print("AP Ready", OLED_PIPE); +#endif + ESPCOM::flush (DEFAULT_PRINTER_PIPE); + return true; +} + +bool WIFI_CONFIG::Enable_servers() +{ + //start web interface + web_interface = new WEBINTERFACE_CLASS (wifi_config.iweb_port); +#ifdef AUTHENTICATION_FEATURE + //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 ); +#endif +#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 +#if !defined (ASYNCWEBSERVER) + socket_server = new WebSocketsServer (wifi_config.iweb_port+1); + socket_server->begin(); + socket_server->onEvent(webSocketEvent); +#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) ) { + ESPCOM::println (F ("Error with mDNS!"), PRINTER_PIPE); + 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() ); + #if defined(ARDUINO_ARCH_ESP8266) + stmp = String (ESP.getChipId() ); + SSDP.setModelName (ESP8266_MODEL_NAME); + SSDP.setModelURL (ESP8266_MODEL_URL); + #else + stmp = String ( (uint16_t) (ESP.getEfuseMac() >> 32) ); + SSDP.setModelName (ESP32_MODEL_NAME); + SSDP.setModelURL (ESP32_MODEL_URL); + #endif + SSDP.setSerialNumber (stmp.c_str() ); + SSDP.setURL ("/"); + SSDP.setModelNumber (ESP_MODEL_NUMBER); + SSDP.setManufacturer (ESP_MANUFACTURER_NAME); + SSDP.setManufacturerURL (ESP_MANUFACTURER_URL); + 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 + +#if defined(TIMESTAMP_FEATURE) + CONFIG::init_time_client(); + if (CONFIG::is_direct_sd) { +#if defined(ARDUINO_ARCH_ESP8266) + //set callback to get time on files on SD + SdFile::dateTimeCallback (dateTime); +#endif + } +#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 + return true; +} + +WIFI_CONFIG wifi_config; diff --git a/esp3d/wificonf.h b/src/wificonf.h similarity index 94% rename from esp3d/wificonf.h rename to src/wificonf.h index 603bbd4c..8ed7036d 100644 --- a/esp3d/wificonf.h +++ b/src/wificonf.h @@ -42,13 +42,14 @@ public: #ifdef MDNS_FEATURE MDNSResponder mdns; #endif + bool WiFi_on; WIFI_CONFIG(); int iweb_port; int idata_port; long baud_rate; int sleep_mode; - int32_t getSignal(int32_t RSSI); - bool Setup(bool force_ap = false); + int32_t getSignal (int32_t RSSI); + bool Setup (bool force_ap = false); void Safe_Setup(); bool Enable_servers(); bool Disable_servers(); diff --git a/tarvis.yml b/tarvis.yml new file mode 100644 index 00000000..6a3a8e3c --- /dev/null +++ b/tarvis.yml @@ -0,0 +1,75 @@ +sudo: false + +language: bash + +os: + - linux + +before_install: + - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16" + +before_script: + - "export DISPLAY=:99.0" + - sleep 3 # give xvfb some time to start + - wget http://downloads.arduino.cc/arduino-1.8.5-linux64.tar.xz + - tar xf arduino-1.8.5-linux64.tar.xz + - mv arduino-1.8.5 $HOME/arduino_ide + - cd $HOME/arduino_ide/hardware + - mkdir esp8266com + - cd esp8266com + - git clone https://github.com/esp8266/Arduino.git esp8266 + - cd esp8266/tools + - python get.py + - cd .. + - 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/ESPAsyncWebServer $HOME/arduino_ide/hardware/esp32/esp32/libraries/ + - mv $TRAVIS_BUILD_DIR/libraries/ESPAsyncTCP $HOME/arduino_ide/hardware/esp32/esp32/libraries/ + - mv $TRAVIS_BUILD_DIR/libraries/AsyncTCP $HOME/arduino_ide/hardware/esp32/esp32/libraries/ + - mv $TRAVIS_BUILD_DIR/libraries/oled-ssd1306 $HOME/arduino_ide/hardware/esp32/esp32/libraries/ + - mv $TRAVIS_BUILD_DIR/libraries/ESP32SSDP $HOME/arduino_ide/hardware/esp32/esp32/libraries/ + - mv $TRAVIS_BUILD_DIR/libraries/arduinoWebSockets $HOME/arduino_ide/hardware/esp32/esp32/libraries/ + - mv $TRAVIS_BUILD_DIR/libraries/DHT_sensor_library_for_ESPx $HOME/arduino_ide/hardware/esp32/esp32/libraries/ + - mkdir $HOME/arduino_ide/libraries/ESP3D + - mv $TRAVIS_BUILD_DIR/src $HOME/arduino_ide/libraries/ESP3D + - mv $TRAVIS_BUILD_DIR/library.properties $HOME/arduino_ide/libraries/ESP3D + + +script: + - cd $TRAVIS_BUILD_DIR + - source command.sh + - export PATH="$HOME/arduino_ide:$PATH" + - arduino --board esp8266com:esp8266:generic --save-prefs + - arduino --get-pref sketchbook.path + - build_sketch $TRAVIS_BUILD_DIR/examples/basicesp3d/basicesp3d.ino + - arduino --board esp32:esp32:esp32 --save-prefs + - build_sketch $TRAVIS_BUILD_DIR/examples/basicesp3d/basicesp3d.ino + - sed -i "s/\/\/#define AUTHENTICATION_FEATURE /#define AUTHENTICATION_FEATURE/g" $HOME/arduino_ide/libraries/ESP3D/src/config.h + - arduino --board esp8266com:esp8266:generic --save-prefs + - arduino --get-pref sketchbook.path + - build_sketch $TRAVIS_BUILD_DIR/examples/basicesp3d/basicesp3d.ino + - arduino --board esp32:esp32:esp32 --save-prefs + - build_sketch $TRAVIS_BUILD_DIR/examples/basicesp3d/basicesp3d.ino + - sed -i "s/\/\/#define ASYNCWEBSERVER /#define ASYNCWEBSERVER/g" $HOME/arduino_ide/libraries/ESP3D/src/config.h + - arduino --board esp8266com:esp8266:generic --save-prefs + - arduino --get-pref sketchbook.path + - build_sketch $TRAVIS_BUILD_DIR/examples/basicesp3d/basicesp3d.ino + - arduino --board esp32:esp32:esp32 --save-prefs + - build_sketch $TRAVIS_BUILD_DIR/examples/basicesp3d/basicesp3d.ino + +notifications: + email: + on_success: change + on_failure: change