diff --git a/.gitignore b/.gitignore index 6a01030..0159fd9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -.pioenvs -.piolibdeps .vscode/.browse.c_cpp.db* .vscode/c_cpp_properties.json .vscode/launch.json diff --git a/.pioenvs/.sconsign.dblite b/.pioenvs/.sconsign.dblite new file mode 100644 index 0000000..414830b Binary files /dev/null and b/.pioenvs/.sconsign.dblite differ diff --git a/.pioenvs/do-not-modify-files-here.url b/.pioenvs/do-not-modify-files-here.url new file mode 100644 index 0000000..c4a16e5 --- /dev/null +++ b/.pioenvs/do-not-modify-files-here.url @@ -0,0 +1,3 @@ + +[InternetShortcut] +URL=https://docs.platformio.org/page/projectconf/section_platformio.html#build-dir diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/Esp.cpp.o b/.pioenvs/nodemcu-32s/FrameworkArduino/Esp.cpp.o new file mode 100644 index 0000000..3f56ee6 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/Esp.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/HardwareSerial.cpp.o b/.pioenvs/nodemcu-32s/FrameworkArduino/HardwareSerial.cpp.o new file mode 100644 index 0000000..440d6a6 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/HardwareSerial.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/IPAddress.cpp.o b/.pioenvs/nodemcu-32s/FrameworkArduino/IPAddress.cpp.o new file mode 100644 index 0000000..11c65ad Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/IPAddress.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/IPv6Address.cpp.o b/.pioenvs/nodemcu-32s/FrameworkArduino/IPv6Address.cpp.o new file mode 100644 index 0000000..4251076 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/IPv6Address.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/MD5Builder.cpp.o b/.pioenvs/nodemcu-32s/FrameworkArduino/MD5Builder.cpp.o new file mode 100644 index 0000000..335440f Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/MD5Builder.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/Print.cpp.o b/.pioenvs/nodemcu-32s/FrameworkArduino/Print.cpp.o new file mode 100644 index 0000000..1ae4312 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/Print.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/Stream.cpp.o b/.pioenvs/nodemcu-32s/FrameworkArduino/Stream.cpp.o new file mode 100644 index 0000000..9dda85f Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/Stream.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/StreamString.cpp.o b/.pioenvs/nodemcu-32s/FrameworkArduino/StreamString.cpp.o new file mode 100644 index 0000000..c9b75ea Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/StreamString.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/WMath.cpp.o b/.pioenvs/nodemcu-32s/FrameworkArduino/WMath.cpp.o new file mode 100644 index 0000000..fd6a62c Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/WMath.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/WString.cpp.o b/.pioenvs/nodemcu-32s/FrameworkArduino/WString.cpp.o new file mode 100644 index 0000000..a1dc387 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/WString.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/base64.cpp.o b/.pioenvs/nodemcu-32s/FrameworkArduino/base64.cpp.o new file mode 100644 index 0000000..577d8da Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/base64.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/cbuf.cpp.o b/.pioenvs/nodemcu-32s/FrameworkArduino/cbuf.cpp.o new file mode 100644 index 0000000..950a3b3 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/cbuf.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-adc.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-adc.c.o new file mode 100644 index 0000000..500c31a Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-adc.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-bt.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-bt.c.o new file mode 100644 index 0000000..6e772e9 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-bt.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-dac.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-dac.c.o new file mode 100644 index 0000000..2615ade Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-dac.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-gpio.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-gpio.c.o new file mode 100644 index 0000000..deaed55 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-gpio.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-i2c.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-i2c.c.o new file mode 100644 index 0000000..c6af1f6 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-i2c.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-ledc.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-ledc.c.o new file mode 100644 index 0000000..58fd71c Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-ledc.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-matrix.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-matrix.c.o new file mode 100644 index 0000000..8970c51 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-matrix.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-misc.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-misc.c.o new file mode 100644 index 0000000..8c75c83 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-misc.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-psram.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-psram.c.o new file mode 100644 index 0000000..aa56e68 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-psram.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-sigmadelta.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-sigmadelta.c.o new file mode 100644 index 0000000..7863e64 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-sigmadelta.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-spi.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-spi.c.o new file mode 100644 index 0000000..7072faa Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-spi.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-time.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-time.c.o new file mode 100644 index 0000000..772c387 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-time.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-timer.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-timer.c.o new file mode 100644 index 0000000..aa93e9a Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-timer.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-touch.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-touch.c.o new file mode 100644 index 0000000..f2fc397 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-touch.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-uart.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-uart.c.o new file mode 100644 index 0000000..b88b2c9 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/esp32-hal-uart.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/libb64/cdecode.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/libb64/cdecode.c.o new file mode 100644 index 0000000..d2d8fef Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/libb64/cdecode.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/libb64/cencode.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/libb64/cencode.c.o new file mode 100644 index 0000000..fcbc6f6 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/libb64/cencode.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/main.cpp.o b/.pioenvs/nodemcu-32s/FrameworkArduino/main.cpp.o new file mode 100644 index 0000000..a0aab88 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/main.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/stdlib_noniso.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/stdlib_noniso.c.o new file mode 100644 index 0000000..5c4665d Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/stdlib_noniso.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/wiring_pulse.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/wiring_pulse.c.o new file mode 100644 index 0000000..22516e3 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/wiring_pulse.c.o differ diff --git a/.pioenvs/nodemcu-32s/FrameworkArduino/wiring_shift.c.o b/.pioenvs/nodemcu-32s/FrameworkArduino/wiring_shift.c.o new file mode 100644 index 0000000..cb11455 Binary files /dev/null and b/.pioenvs/nodemcu-32s/FrameworkArduino/wiring_shift.c.o differ diff --git a/.pioenvs/nodemcu-32s/firmware.bin b/.pioenvs/nodemcu-32s/firmware.bin new file mode 100644 index 0000000..f508f48 Binary files /dev/null and b/.pioenvs/nodemcu-32s/firmware.bin differ diff --git a/.pioenvs/nodemcu-32s/firmware.elf b/.pioenvs/nodemcu-32s/firmware.elf new file mode 100644 index 0000000..4478d55 Binary files /dev/null and b/.pioenvs/nodemcu-32s/firmware.elf differ diff --git a/.pioenvs/nodemcu-32s/lib0a7/DNSServer/DNSServer.cpp.o b/.pioenvs/nodemcu-32s/lib0a7/DNSServer/DNSServer.cpp.o new file mode 100644 index 0000000..f62d940 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib0a7/DNSServer/DNSServer.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib0a7/libDNSServer.a b/.pioenvs/nodemcu-32s/lib0a7/libDNSServer.a new file mode 100644 index 0000000..2fbaca4 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib0a7/libDNSServer.a differ diff --git a/.pioenvs/nodemcu-32s/lib0f6/AsyncTCP_ID1826/AsyncTCP.cpp.o b/.pioenvs/nodemcu-32s/lib0f6/AsyncTCP_ID1826/AsyncTCP.cpp.o new file mode 100644 index 0000000..116ba4d Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib0f6/AsyncTCP_ID1826/AsyncTCP.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib0f6/libAsyncTCP_ID1826.a b/.pioenvs/nodemcu-32s/lib0f6/libAsyncTCP_ID1826.a new file mode 100644 index 0000000..74b36c3 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib0f6/libAsyncTCP_ID1826.a differ diff --git a/.pioenvs/nodemcu-32s/lib178/EEPROM/EEPROM.cpp.o b/.pioenvs/nodemcu-32s/lib178/EEPROM/EEPROM.cpp.o new file mode 100644 index 0000000..06c9168 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib178/EEPROM/EEPROM.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib178/libEEPROM.a b/.pioenvs/nodemcu-32s/lib178/libEEPROM.a new file mode 100644 index 0000000..5f9d7c5 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib178/libEEPROM.a differ diff --git a/.pioenvs/nodemcu-32s/lib178/libTaskScheduler_ID721.a b/.pioenvs/nodemcu-32s/lib178/libTaskScheduler_ID721.a new file mode 100644 index 0000000..8b277f0 --- /dev/null +++ b/.pioenvs/nodemcu-32s/lib178/libTaskScheduler_ID721.a @@ -0,0 +1 @@ +! diff --git a/.pioenvs/nodemcu-32s/lib1a8/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.cpp.o b/.pioenvs/nodemcu-32s/lib1a8/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.cpp.o new file mode 100644 index 0000000..8af00f8 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib1a8/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib1a8/libLiquidCrystal_I2C_ID576.a b/.pioenvs/nodemcu-32s/lib1a8/libLiquidCrystal_I2C_ID576.a new file mode 100644 index 0000000..44d861b Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib1a8/libLiquidCrystal_I2C_ID576.a differ diff --git a/.pioenvs/nodemcu-32s/lib250/WiFi/ETH.cpp.o b/.pioenvs/nodemcu-32s/lib250/WiFi/ETH.cpp.o new file mode 100644 index 0000000..1f023c1 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib250/WiFi/ETH.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib250/WiFi/WiFi.cpp.o b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFi.cpp.o new file mode 100644 index 0000000..20091df Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFi.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiAP.cpp.o b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiAP.cpp.o new file mode 100644 index 0000000..d7f2123 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiAP.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiClient.cpp.o b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiClient.cpp.o new file mode 100644 index 0000000..82a9d97 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiClient.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiGeneric.cpp.o b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiGeneric.cpp.o new file mode 100644 index 0000000..767995f Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiGeneric.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiMulti.cpp.o b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiMulti.cpp.o new file mode 100644 index 0000000..7c9d385 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiMulti.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiSTA.cpp.o b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiSTA.cpp.o new file mode 100644 index 0000000..3e43a9e Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiSTA.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiScan.cpp.o b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiScan.cpp.o new file mode 100644 index 0000000..f93d6df Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiScan.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiServer.cpp.o b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiServer.cpp.o new file mode 100644 index 0000000..798d64d Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiServer.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiUdp.cpp.o b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiUdp.cpp.o new file mode 100644 index 0000000..ce5dc0d Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib250/WiFi/WiFiUdp.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib250/libWiFi.a b/.pioenvs/nodemcu-32s/lib250/libWiFi.a new file mode 100644 index 0000000..c039ead Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib250/libWiFi.a differ diff --git a/.pioenvs/nodemcu-32s/lib562/ESP8266FtpServer_ID992/ESP8266FtpServer.cpp.o b/.pioenvs/nodemcu-32s/lib562/ESP8266FtpServer_ID992/ESP8266FtpServer.cpp.o new file mode 100644 index 0000000..174f00b Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib562/ESP8266FtpServer_ID992/ESP8266FtpServer.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib562/libESP8266FtpServer_ID992.a b/.pioenvs/nodemcu-32s/lib562/libESP8266FtpServer_ID992.a new file mode 100644 index 0000000..fba0bc7 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib562/libESP8266FtpServer_ID992.a differ diff --git a/.pioenvs/nodemcu-32s/lib69d/SPIFFS/SPIFFS.cpp.o b/.pioenvs/nodemcu-32s/lib69d/SPIFFS/SPIFFS.cpp.o new file mode 100644 index 0000000..3374d05 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib69d/SPIFFS/SPIFFS.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib69d/libSPIFFS.a b/.pioenvs/nodemcu-32s/lib69d/libSPIFFS.a new file mode 100644 index 0000000..9da8b98 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib69d/libSPIFFS.a differ diff --git a/.pioenvs/nodemcu-32s/lib886/Wire/Wire.cpp.o b/.pioenvs/nodemcu-32s/lib886/Wire/Wire.cpp.o new file mode 100644 index 0000000..c22ab0b Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib886/Wire/Wire.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib886/libWire.a b/.pioenvs/nodemcu-32s/lib886/libWire.a new file mode 100644 index 0000000..0ae900e Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib886/libWire.a differ diff --git a/.pioenvs/nodemcu-32s/lib9a6/ESPAsyncWifiManager_ID1438/ESPAsyncWiFiManager.cpp.o b/.pioenvs/nodemcu-32s/lib9a6/ESPAsyncWifiManager_ID1438/ESPAsyncWiFiManager.cpp.o new file mode 100644 index 0000000..8e7aa75 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib9a6/ESPAsyncWifiManager_ID1438/ESPAsyncWiFiManager.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/lib9a6/libESPAsyncWifiManager_ID1438.a b/.pioenvs/nodemcu-32s/lib9a6/libESPAsyncWifiManager_ID1438.a new file mode 100644 index 0000000..fcd2937 Binary files /dev/null and b/.pioenvs/nodemcu-32s/lib9a6/libESPAsyncWifiManager_ID1438.a differ diff --git a/.pioenvs/nodemcu-32s/libFrameworkArduino.a b/.pioenvs/nodemcu-32s/libFrameworkArduino.a new file mode 100644 index 0000000..c40879d Binary files /dev/null and b/.pioenvs/nodemcu-32s/libFrameworkArduino.a differ diff --git a/.pioenvs/nodemcu-32s/libFrameworkArduinoVariant.a b/.pioenvs/nodemcu-32s/libFrameworkArduinoVariant.a new file mode 100644 index 0000000..8b277f0 --- /dev/null +++ b/.pioenvs/nodemcu-32s/libFrameworkArduinoVariant.a @@ -0,0 +1 @@ +! diff --git a/.pioenvs/nodemcu-32s/liba55/ArduinoINA219_ID411/INA219.cpp.o b/.pioenvs/nodemcu-32s/liba55/ArduinoINA219_ID411/INA219.cpp.o new file mode 100644 index 0000000..3e16f00 Binary files /dev/null and b/.pioenvs/nodemcu-32s/liba55/ArduinoINA219_ID411/INA219.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/liba55/libArduinoINA219_ID411.a b/.pioenvs/nodemcu-32s/liba55/libArduinoINA219_ID411.a new file mode 100644 index 0000000..c1423c2 Binary files /dev/null and b/.pioenvs/nodemcu-32s/liba55/libArduinoINA219_ID411.a differ diff --git a/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/AsyncEventSource.cpp.o b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/AsyncEventSource.cpp.o new file mode 100644 index 0000000..dc64aea Binary files /dev/null and b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/AsyncEventSource.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/AsyncWebSocket.cpp.o b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/AsyncWebSocket.cpp.o new file mode 100644 index 0000000..dbf8e82 Binary files /dev/null and b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/AsyncWebSocket.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/SPIFFSEditor.cpp.o b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/SPIFFSEditor.cpp.o new file mode 100644 index 0000000..430cb61 Binary files /dev/null and b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/SPIFFSEditor.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebAuthentication.cpp.o b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebAuthentication.cpp.o new file mode 100644 index 0000000..97f0422 Binary files /dev/null and b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebAuthentication.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebHandlers.cpp.o b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebHandlers.cpp.o new file mode 100644 index 0000000..cdf785d Binary files /dev/null and b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebHandlers.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebRequest.cpp.o b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebRequest.cpp.o new file mode 100644 index 0000000..a2b641b Binary files /dev/null and b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebRequest.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebResponses.cpp.o b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebResponses.cpp.o new file mode 100644 index 0000000..8be2643 Binary files /dev/null and b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebResponses.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebServer.cpp.o b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebServer.cpp.o new file mode 100644 index 0000000..e7c64ee Binary files /dev/null and b/.pioenvs/nodemcu-32s/libb36/ESP Async WebServer_ID306/WebServer.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/libb36/libESP Async WebServer_ID306.a b/.pioenvs/nodemcu-32s/libb36/libESP Async WebServer_ID306.a new file mode 100644 index 0000000..003b161 Binary files /dev/null and b/.pioenvs/nodemcu-32s/libb36/libESP Async WebServer_ID306.a differ diff --git a/.pioenvs/nodemcu-32s/libc3f/FS/FS.cpp.o b/.pioenvs/nodemcu-32s/libc3f/FS/FS.cpp.o new file mode 100644 index 0000000..4131516 Binary files /dev/null and b/.pioenvs/nodemcu-32s/libc3f/FS/FS.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/libc3f/FS/vfs_api.cpp.o b/.pioenvs/nodemcu-32s/libc3f/FS/vfs_api.cpp.o new file mode 100644 index 0000000..58e3c5c Binary files /dev/null and b/.pioenvs/nodemcu-32s/libc3f/FS/vfs_api.cpp.o differ diff --git a/.pioenvs/nodemcu-32s/libc3f/libFS.a b/.pioenvs/nodemcu-32s/libc3f/libFS.a new file mode 100644 index 0000000..2fa3e7f Binary files /dev/null and b/.pioenvs/nodemcu-32s/libc3f/libFS.a differ diff --git a/.pioenvs/nodemcu-32s/partitions.bin b/.pioenvs/nodemcu-32s/partitions.bin new file mode 100644 index 0000000..1d3d5b9 Binary files /dev/null and b/.pioenvs/nodemcu-32s/partitions.bin differ diff --git a/.pioenvs/nodemcu-32s/spiffs.bin b/.pioenvs/nodemcu-32s/spiffs.bin new file mode 100644 index 0000000..327d849 Binary files /dev/null and b/.pioenvs/nodemcu-32s/spiffs.bin differ diff --git a/.pioenvs/nodemcu-32s/src/BatteryAnalyzer.ino.cpp.o b/.pioenvs/nodemcu-32s/src/BatteryAnalyzer.ino.cpp.o new file mode 100644 index 0000000..b853db0 Binary files /dev/null and b/.pioenvs/nodemcu-32s/src/BatteryAnalyzer.ino.cpp.o differ diff --git a/.pioenvs/structure.hash b/.pioenvs/structure.hash new file mode 100644 index 0000000..8451214 --- /dev/null +++ b/.pioenvs/structure.hash @@ -0,0 +1 @@ +ed29a20d4d5440e7bc8a8339197df0ad37810755 \ No newline at end of file diff --git a/.piolibdeps/ArduinoINA219_ID411/.gitignore b/.piolibdeps/ArduinoINA219_ID411/.gitignore new file mode 100644 index 0000000..085e8ba --- /dev/null +++ b/.piolibdeps/ArduinoINA219_ID411/.gitignore @@ -0,0 +1 @@ +Thumbs.db diff --git a/.piolibdeps/ArduinoINA219_ID411/.library.json b/.piolibdeps/ArduinoINA219_ID411/.library.json new file mode 100644 index 0000000..a0887f8 --- /dev/null +++ b/.piolibdeps/ArduinoINA219_ID411/.library.json @@ -0,0 +1,64 @@ +{ + "description": "INA219 hi-side i2c current/power sensor Library", + "repository": { + "url": "https://github.com/flav1972/ArduinoINA219", + "type": "git" + }, + "platforms": [ + "atmelavr", + "atmelsam", + "espressif8266", + "intel_arc32", + "microchippic32", + "nordicnrf51", + "teensy", + "timsp430" + ], + "export": { + "exclude": [ + "extras", + "docs", + "tests", + "test", + "*.doxyfile", + "*.pdf" + ], + "include": null + }, + "authors": [ + { + "maintainer": false, + "name": "DeCristofaro John", + "url": null, + "email": "johngineer@yahoo.com" + }, + { + "maintainer": false, + "name": "Jukka-Pekka Sarjanen", + "url": null, + "email": "jukka-Pekka.sarjanen@kone.com" + }, + { + "maintainer": false, + "name": "gandy92", + "url": null, + "email": "gandy92@googlemail.com" + }, + { + "maintainer": true, + "name": "Flavius Bindea", + "url": null, + "email": "flav@flav.com" + } + ], + "keywords": [ + "sensors" + ], + "id": 411, + "name": "ArduinoINA219", + "frameworks": [ + "arduino" + ], + "version": "1.0.1", + "homepage": null +} \ No newline at end of file diff --git a/.piolibdeps/ArduinoINA219_ID411/INA219.cpp b/.piolibdeps/ArduinoINA219_ID411/INA219.cpp new file mode 100644 index 0000000..b3f4e0d --- /dev/null +++ b/.piolibdeps/ArduinoINA219_ID411/INA219.cpp @@ -0,0 +1,298 @@ +/****************************************************************************** +* TI INA219 hi-side i2c current/power monitor Library +* +* http://www.ti.com/product/ina219 +* +* 6 May 2012 by John De Cristofaro +* +* +* Tested at standard i2c 100kbps signaling rate. +* +* This library does not handle triggered conversion modes. It uses the INA219 +* in continuous conversion mode. All reads are from continous conversions. +* +* A note about the gain (PGA) setting: +* The gain of the ADC pre-amplifier is programmable in the INA219, and can +* be set between 1/8x (default) and unity. This allows a shunt voltage +* range of +/-320mV to +/-40mV respectively. Something to keep in mind, +* however, is that this change in gain DOES NOT affect the resolution +* of the ADC, which is fixed at 1uV. What it does do is increase noise +* immunity by exploiting the integrative nature of the delta-sigma ADC. +* For the best possible reading, you should set the gain to the range +* of voltages that you expect to see in your particular circuit. See +* page 15 in the datasheet for more info about the PGA. +* +* Known bugs: +* * may return unreliable values if not connected to a bus or at +* bus currents below 10uA. +* +* Arduino 1.0 compatible as of 6/6/2012 +* +* Dependencies: +* * Arduino Wire library +* +* MIT license +******************************************************************************/ + +#include "INA219.h" +#define _delay_ms(ms) delayMicroseconds((ms) * 1000) +namespace{ +// config. register bit labels +const uint8_t RST = 15; +const uint8_t BRNG = 13; +const uint8_t PG1 = 12; +const uint8_t PG0 = 11; +const uint8_t BADC4 = 10; +const uint8_t BADC3 = 9; +const uint8_t BADC2 = 8; +const uint8_t BADC1 = 7; +const uint8_t SADC4 = 6; +const uint8_t SADC3 = 5; +const uint8_t SADC2 = 4; +const uint8_t SADC1 = 3; +const uint8_t MODE3 = 2; +const uint8_t MODE2 = 1; +const uint8_t MODE1 = 0; +}; + +#define CNVR_B 1 // conversion ready bit in bus voltage register V_BUS_R +#define OVF_B 0 // math overflow bit in bus voltage register V_BUS_R + +INA219::INA219(t_i2caddr addr): i2c_address(addr) { +} + +void INA219::begin() { + Wire.begin(); + configure(); + calibrate(); +} + +void INA219::calibrate(float shunt_val, float v_shunt_max, float v_bus_max, float i_max_expected) { + uint16_t digits; + float i_max_possible, min_lsb, max_lsb, swap; + +#if (INA219_DEBUG == 1) + float max_current,max_before_overflow,max_shunt_v,max_shunt_v_before_overflow,max_power; +#endif + r_shunt = shunt_val; + + i_max_possible = v_shunt_max / r_shunt; + min_lsb = i_max_expected / 32767; + max_lsb = i_max_expected / 4096; + + current_lsb = min_lsb; + digits=0; + + /* From datasheet: This value was selected to be a round number near the Minimum_LSB. + * This selection allows for good resolution with a rounded LSB. + * eg. 0.000610 -> 0.000700 + */ + while( current_lsb > 0.0 ){//If zero there is something weird... + if( (uint16_t)current_lsb / 1){ + current_lsb = (uint16_t) current_lsb + 1; + current_lsb /= pow(10,digits); + break; + } + else{ + digits++; + current_lsb *= 10.0; + } + }; + + swap = (0.04096)/(current_lsb*r_shunt); + cal = (uint16_t)swap; + power_lsb = current_lsb * 20; + +#if (INA219_DEBUG == 1) + max_current = current_lsb*32767; + max_before_overflow = max_current > i_max_possible?i_max_possible:max_current; + + max_shunt_v = max_before_overflow*r_shunt; + max_shunt_v_before_overflow = max_shunt_v > v_shunt_max?v_shunt_max:max_shunt_v; + + max_power = v_bus_max * max_before_overflow; + Serial.print("v_bus_max: "); Serial.println(v_bus_max, 8); + Serial.print("v_shunt_max: "); Serial.println(v_shunt_max, 8); + Serial.print("i_max_possible: "); Serial.println(i_max_possible, 8); + Serial.print("i_max_expected: "); Serial.println(i_max_expected, 8); + Serial.print("min_lsb: "); Serial.println(min_lsb, 12); + Serial.print("max_lsb: "); Serial.println(max_lsb, 12); + Serial.print("current_lsb: "); Serial.println(current_lsb, 12); + Serial.print("power_lsb: "); Serial.println(power_lsb, 8); + Serial.println(" "); + Serial.print("cal: "); Serial.println(cal); + Serial.print("r_shunt: "); Serial.println(r_shunt, 6); + Serial.print("max_before_overflow: "); Serial.println(max_before_overflow,8); + Serial.print("max_shunt_v_before_overflow: "); Serial.println(max_shunt_v_before_overflow,8); + Serial.print("max_power: "); Serial.println(max_power,8); + Serial.println(" "); +#endif + write16(CAL_R, cal); +} + +void INA219::configure( t_range range, t_gain gain, t_adc bus_adc, t_adc shunt_adc, t_mode mode) { + config = 0; + + config |= (range << BRNG | gain << PG0 | bus_adc << BADC1 | shunt_adc << SADC1 | mode); +#if (INA219_DEBUG == 1) + Serial.print("Config: 0x"); Serial.println(config,HEX); +#endif + write16(CONFIG_R, config); +} + +#define INA_RESET 0xFFFF // send to CONFIG_R to reset unit +void INA219::reset(){ + write16(CONFIG_R, INA_RESET); + _delay_ms(5); +} + +int16_t INA219::shuntVoltageRaw() const { + return read16(V_SHUNT_R); +} + +float INA219::shuntVoltage() const { + float temp; + temp = read16(V_SHUNT_R); + return (temp / 100000); +} + +int16_t INA219::busVoltageRaw() { + _bus_voltage_register = read16(V_BUS_R); + _overflow = bitRead(_bus_voltage_register, OVF_B); // overflow bit + _ready = bitRead(_bus_voltage_register, CNVR_B); // ready bit + return _bus_voltage_register; +} + + +float INA219::busVoltage() { + int16_t temp; + temp = busVoltageRaw(); + temp >>= 3; + return (temp * 0.004); +} + +int16_t INA219::shuntCurrentRaw() const { + return (read16(I_SHUNT_R)); +} + +float INA219::shuntCurrent() const { + return (read16(I_SHUNT_R) * current_lsb); +} + +float INA219::busPower() const { + return (read16(P_BUS_R) * power_lsb); +} + +/**************************************************************************/ +/*! + @brief Rewrites the last config register +*/ +/**************************************************************************/ +void INA219::reconfig() const { +#if (INA219_DEBUG == 1) + Serial.print("Reconfigure with Config: 0x"); Serial.println(config,HEX); +#endif + write16(CONFIG_R, config); +} + +/**************************************************************************/ +/*! + @brief Rewrites the last calibration +*/ +/**************************************************************************/ +void INA219::recalibrate() const { +#if (INA219_DEBUG == 1) + Serial.print("Recalibrate with cal: "); Serial.println(cal); +#endif + write16(CAL_R, cal); +} + +/**************************************************************************/ +/*! + @brief returns conversion ready bite from last bus voltage read + + @note page 30: + Although the data from the last conversion can be read at any time, + the INA219 Conversion Ready bit (CNVR) indicates when data from + a conversion is available in the data output registers. + The CNVR bit is set after all conversions, averaging, + and multiplications are complete. + CNVR will clear under the following conditions: + 1.) Writing a new mode into the Operating Mode bits in the + Configuration Register (except for Power-Down or Disable) + 2.) Reading the Power Register + + page 15: + The Conversion Ready bit clears under these + conditions: + 1. Writing to the Configuration Register, except + when configuring the MODE bits for Power Down + or ADC off (Disable) modes; + 2. Reading the Status Register; + 3. Triggering a single-shot conversion with the + Convert pin. +*/ +/**************************************************************************/ +bool INA219::ready() const { + return _ready; +} + +/**************************************************************************/ +/*! + @brief returns overflow bite from last bus voltage read + + @note The Math Overflow Flag (OVF) is set when the Power or + Current calculations are out of range. It indicates that + current and power data may be meaningless. +*/ +/**************************************************************************/ +bool INA219::overflow() const { + return _overflow; +} + +/********************************************************************** +* INTERNAL I2C FUNCTIONS * +**********************************************************************/ + +void INA219::write16(t_reg a, uint16_t d) const { + uint8_t temp; + temp = (uint8_t)d; + d >>= 8; + Wire.beginTransmission(i2c_address); // start transmission to device + + #if ARDUINO >= 100 + Wire.write(a); // sends register address to read from + Wire.write((uint8_t)d); // write data hibyte + Wire.write(temp); // write data lobyte; + #else + Wire.send(a); // sends register address to read from + Wire.send((uint8_t)d); // write data hibyte + Wire.send(temp); // write data lobyte; + #endif + + Wire.endTransmission(); // end transmission + delay(1); +} + +int16_t INA219::read16(t_reg a) const { + uint16_t ret; + + // move the pointer to reg. of interest, null argument + write16(a, 0); + + Wire.requestFrom((int)i2c_address, 2); // request 2 data bytes + + #if ARDUINO >= 100 + ret = Wire.read(); // rx hi byte + ret <<= 8; + ret |= Wire.read(); // rx lo byte + #else + ret = Wire.receive(); // rx hi byte + ret <<= 8; + ret |= Wire.receive(); // rx lo byte + #endif + + Wire.endTransmission(); // end transmission + + return ret; +} diff --git a/.piolibdeps/ArduinoINA219_ID411/INA219.h b/.piolibdeps/ArduinoINA219_ID411/INA219.h new file mode 100644 index 0000000..fcfda17 --- /dev/null +++ b/.piolibdeps/ArduinoINA219_ID411/INA219.h @@ -0,0 +1,215 @@ +/****************************************************************************** +* TI INA219 hi-side i2c current/power monitor Library +* +* http://www.ti.com/product/ina219 +* +* 6 May 2012 by John De Cristofaro +* +* +* Tested at standard i2c 100kbps signaling rate. +* +* This library does not handle triggered conversion modes. It uses the INA219 +* in continuous conversion mode. All reads are from continous conversions. +* +* A note about the gain (PGA) setting: +* The gain of the ADC pre-amplifier is programmable in the INA219, and can +* be set between 1/8x (default) and unity. This allows a shunt voltage +* range of +/-320mV to +/-40mV respectively. Something to keep in mind, +* however, is that this change in gain DOES NOT affect the resolution +* of the ADC, which is fixed at 1uV. What it does do is increase noise +* immunity by exploiting the integrative nature of the delta-sigma ADC. +* For the best possible reading, you should set the gain to the range +* of voltages that you expect to see in your particular circuit. See +* page 15 in the datasheet for more info about the PGA. +* +* Known bugs: +* * may return unreliable values if not connected to a bus or at +* bus currents below 10uA. +* +* Arduino 1.0 compatible as of 6/6/2012 +* +* Dependencies: +* * Arduino Wire library +* +* MIT license +******************************************************************************/ + +#ifndef ina219_h +#define ina219_h + + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +#include + +#define INA219_DEBUG 0 + + +// default values + +#define D_SHUNT 0.1 +#define D_V_BUS_MAX 32 +#define D_V_SHUNT_MAX 0.2 +#define D_I_MAX_EXPECTED 2 + + +class INA219 +{ + public: + + ///I2C address definations. + /// Adafruit breakout board have two jumppers to set unique + /// address for each board ( in case there is more than one in Your system) + /// base address is 0x40, no jumpers needed) + enum t_i2caddr{ + I2C_ADDR_40 = 0x40, ///< address 0x40 no jumpers required. + I2C_ADDR_41 = 0x41, ///< address 0x41 bridge A0. + I2C_ADDR_44 = 0x44, ///< address 0x44 bridge A1. + I2C_ADDR_45 = 0x45 ///< address 0x45 bridge A0 & A1. + + }; + + ///Sets PGA gain and range. Note that the PGA defaults to ÷8 (320mV range). + ///Configuration reister bits 11, 12 are used for this. + enum t_gain{ + GAIN_1_40MV = 0, + GAIN_2_80MV = 1, + GAIN_4_160MV = 2, + GAIN_8_320MV = 3 + }; + + ///Bus voltage range. + ///Configuration register bit 13 defines this. + enum t_range{ + RANGE_16V = 0, ///< Range 0-16 volts + RANGE_32V = 1 ///< Range 0-32 volts + }; + + ///ADC resolution (9-, 10-, 11-, or 12-bit) or set the number of samples. + ///Same definations are used both shunt and bus adc. + ///Configuration register bits 3–6 are used for averaging results for the Shunt Voltage Register (01h). + ///And bits 7-10 are used averaging results for the Bus Voltage Register (02h). + /// \see t_reg + enum t_adc{ + ADC_9BIT = 0, ///< 9bit converion time 84us. + ADC_10BIT = 1, ///< 10bit converion time 148us. + ADC_11BIT = 2, ///< 11bit converion time 2766us. + ADC_12BIT = 3, ///< 12bit converion time 532us. + ADC_2SAMP = 9, ///< 2 samples converion time 1.06ms. + ADC_4SAMP = 10, ///< 4 samples converion time 2.13ms. + ADC_8SAMP = 11, ///< 8 samples converion time 4.26ms. + ADC_16SAMP = 12, ///< 16 samples converion time 8.51ms + ADC_32SAMP = 13, ///< 32 samples converion time 17.02ms. + ADC_64SAMP = 14, ///< 64 samples converion time 34.05ms. + ADC_128SAMP = 15, ///< 128 samples converion time 68.10ms. + }; + + ///Selects continuous, triggered, or power-down mode of operation. These bits default to continuous shunt and bus measurement mode. + ///\n Please note: Thus library does not support triggered converion I have comment out values to set trigged mode.\n + ///Configuration register bits 0-2 are used for this. \see t_reg + enum t_mode{ + PWR_DOWN = 0, + /* + TRIG_SH = 1, + TRIG_BUS = 2, + TRIG_SH_BUS = 3, + */ + ADC_OFF = 4, + CONT_SH = 5, /// +#include + +INA219 monitor; +``` + +In the `setup()` function initialise the INA219: +``` +monitor.begin(); +``` + +Then in the `loop()` function make calls to different functions that are returning the values: +``` + Serial.print("raw shunt voltage: "); + Serial.println(monitor.shuntVoltageRaw()); + + Serial.print("raw bus voltage: "); + Serial.println(monitor.busVoltageRaw()); + + Serial.println("--"); + + Serial.print("shunt voltage: "); + Serial.print(monitor.shuntVoltage() * 1000, 4); + Serial.println(" mV"); + + Serial.print("shunt current: "); + Serial.print(monitor.shuntCurrent() * 1000, 4); + Serial.println(" mA"); + + Serial.print("bus voltage: "); + Serial.print(monitor.busVoltage(), 4); + Serial.println(" V"); + + Serial.print("bus power: "); + Serial.print(monitor.busPower() * 1000, 4); + Serial.println(" mW"); +``` +## Enhanced setup +If you want to use a different setup or if you do not use the Adafruit's breakout then in the `setup()` function you need to call `configure()` and `calibrate()`. + +An exemple is in [ina219_test_nondefault](examples/ina219_test_nondefault/ina219_test_nondefault.ino). + +Extract of the `setup()` function: +``` + monitor.begin(); + + // setting up our configuration + monitor.configure(INA219::RANGE_16V, INA219::GAIN_2_80MV, INA219::ADC_64SAMP, INA219::ADC_64SAMP, INA219::CONT_SH_BUS); + + // calibrate with our values + monitor.calibrate(SHUNT_R, SHUNT_MAX_V, BUS_MAX_V, MAX_CURRENT); +``` + +## functions +All the functions are well comented in [INA219.h](INA219.h) and [INA219.cpp](INA219.cpp) +* `begin()` +starts the communication with the INA219 and does the default setup. +* `configure()` +setups the INA219 mode. + + The args are: `(range, gain, bus_adc, shunt_adc, mode)` + * range : Range for bus voltage + * RANGE_16V : Range 0-16 volts + * RANGE_32V (default): Range 0-32 volts + * gain : Set Gain for shunt voltage (choose the lowest possible depending on your hardware) + * GAIN_1_40MV : 40mV + * GAIN_2_80MV : 80mV + * GAIN_4_160MV : 160mV + * GAIN_8_320MV (default): 320mV + * bus_adc : Configure bus voltage conversion + * ADC_9BIT : 9bit, converion time 84us. + * ADC_10BIT : 10bit, converion time 148us. + * ADC_11BIT : 11bit, converion time 2766us. + * ADC_12BIT (default): 12bit converion time 532us. + * ADC_2SAMP : 2 samples converion time 1.06ms. + * ADC_4SAMP : 4 samples converion time 2.13ms. + * ADC_8SAMP : 8 samples converion time 4.26ms. + * ADC_16SAMP : 16 samples converion time 8.51ms + * ADC_32SAMP : 32 samples converion time 17.02ms. + * ADC_64SAMP : 64 samples converion time 34.05ms. + * ADC_128SAMP : 128 samples converion time 68.10ms. + * shunt_adc: Configure shun voltage conversion. Same values as for bus_adc + * mode: Sets operation mode. + * PWR_DOWN : Power Down + * ADC_OFF + * CONT_SH : Shunt Continuous + * CONT_BUS : Bus Continuous + * CONT_SH_BUS (default): Shunt and Bus, Continuous. +* `calibrate(r_shunt, v_shunt_max, v_bus_max, i_max_expected)` function is doing the calculations as described in the INA219 datasheet and sets up the calibration registers. + + The args are: + * r_shunt : Value of shunt in Ohms. + * v_shunt_max : Maximum value of voltage across shunt. + * v_bus_max : Maximum voltage of bus. + * i_max_expected : Maximum current draw of bus + shunt. +* `reconfig()` rewrites the last used config to the INA219. +* `recalibrate()` rewrites the calibration registers as they were calculated by `calibrate()` function. +* `ready()` returns the value of the ready bit. It's value is updated according to the last call to `busVoltageRaw()` or `busVoltage()`. + + based on the datasheet page 30: + Although the data from the last conversion can be read at any time, + the INA219 Conversion Ready bit (CNVR) indicates when data from + a conversion is available in the data output registers. + The CNVR bit is set after all conversions, averaging, + and multiplications are complete. + CNVR will clear under the following conditions: + 1.) Writing a new mode into the Operating Mode bits in the + Configuration Register (except for Power-Down or Disable) + 2.) Reading the Power Register + + page 15: + The Conversion Ready bit clears under these + conditions: + 1. Writing to the Configuration Register, except + when configuring the MODE bits for Power Down + or ADC off (Disable) modes; + 2. Reading the Status Register; + 3. Triggering a single-shot conversion with the + Convert pin. +* `overflow()` returns the value of the overflow bit. It's value is updated according to the last call to `busVoltageRaw()` or `busVoltage()`. +* `reset()` : Resets the INA219. +* `shuntVoltageRaw()` : Returns the raw binary value of the shunt voltage +* `busVoltageRaw()` : Returns raw bus voltage binary value. +* `shuntCurrentRaw()` : Returns raw bus voltage binary value. +* `shuntVoltage()` : Returns the shunt voltage in volts. +* `busVoltage()` : Returns the bus voltage in volts. +* `shuntCurrent()` : Returns the shunt current in amps. +* `busPower()` : Returns the bus power in watts + +# Licence +MIT license diff --git a/.piolibdeps/ArduinoINA219_ID411/examples/hardware_mounting/ina219_hardware_mount.fzz b/.piolibdeps/ArduinoINA219_ID411/examples/hardware_mounting/ina219_hardware_mount.fzz new file mode 100644 index 0000000..e811476 Binary files /dev/null and b/.piolibdeps/ArduinoINA219_ID411/examples/hardware_mounting/ina219_hardware_mount.fzz differ diff --git a/.piolibdeps/ArduinoINA219_ID411/examples/hardware_mounting/ina219_hardware_mount_bb.png b/.piolibdeps/ArduinoINA219_ID411/examples/hardware_mounting/ina219_hardware_mount_bb.png new file mode 100644 index 0000000..a4d1b27 Binary files /dev/null and b/.piolibdeps/ArduinoINA219_ID411/examples/hardware_mounting/ina219_hardware_mount_bb.png differ diff --git a/.piolibdeps/ArduinoINA219_ID411/examples/ina219_morechecks/ina219_morechecks.ino b/.piolibdeps/ArduinoINA219_ID411/examples/ina219_morechecks/ina219_morechecks.ino new file mode 100644 index 0000000..c7c2ba8 --- /dev/null +++ b/.piolibdeps/ArduinoINA219_ID411/examples/ina219_morechecks/ina219_morechecks.ino @@ -0,0 +1,123 @@ +/* + * Baterry Monitor + * by Flavius Bindea + * this sketch uses averaging and check of ready status + */ +#include +#include + +// Current sensor and shunt used +INA219 ina219; + +#define R_SHUNT 0.00375 +#define V_SHUNT_MAX 0.075 +#define V_BUS_MAX 16 +#define I_MAX_EXPECTED 20 + +boolean led=true; + +// current and voltage readings +float shuntvoltage = 0; +float busvoltage = 0; +float current_A = 0; +float batvoltage = 0; +float power = 0; +float Ah = 0; +unsigned long lastread = 0; // used to calculate Ah +unsigned long tick; // current read time - last read + +// different intervals for each Task +int intervalReadData = 50; +int intervalDisplay = 1000; + +// last taks call +unsigned long previousMillisReadData = 0; +unsigned long previousMillisDisplay = 0; + +void setup() { + // put your setup code here, to run once: + Serial.begin(57600); + Serial.println("Hello - Arduino_INA219 !"); + + ina219.begin(); + // configure INA219 for averaging at 16 samples (8.51ms) + ina219.configure(INA219::RANGE_16V, INA219::GAIN_2_80MV, INA219::ADC_16SAMP, INA219::ADC_16SAMP, INA219::CONT_SH_BUS); + // configure INA219 for averaging at 128 samples + //ina219.configure(INA219::RANGE_16V, INA219::GAIN_2_80MV, INA219::ADC_128SAMP, INA219::ADC_128SAMP, INA219::CONT_SH_BUS); + lastread = millis(); + // calibrate INA219 with out shunt values + ina219.calibrate(R_SHUNT, V_SHUNT_MAX, V_BUS_MAX, I_MAX_EXPECTED); + + // for led blinking + pinMode(13, OUTPUT); + +/* + // use timer 1 to launch current reading + // this is a test only and not sure it works + Timer1.initialize(READFREQ); // 100ms reading interval + Timer1.attachInterrupt(readCurrent); +*/ + delay(1000); +} + +void loop() { + + // get current time stamp + // only need one for both if-statements + unsigned long currentMillis = millis(); + + if ((unsigned long)(currentMillis - previousMillisReadData) >= intervalReadData) { + previousMillisReadData = millis(); + readCurrent(); + Serial.print("tick: "); Serial.print(tick); Serial.println(" ms"); + } + if ((unsigned long)(currentMillis - previousMillisDisplay) >= intervalDisplay) { + previousMillisDisplay = millis(); + // displays data + Serial.print("Bus Voltage: "); Serial.print(busvoltage,3); Serial.println(" V"); + Serial.print("Shunt Voltage: "); Serial.print(shuntvoltage,3); Serial.println(" mV"); + Serial.print("Bat Voltage: "); Serial.print(batvoltage,3); Serial.println(" V"); + Serial.print("Current: "); Serial.print(current_A,3); Serial.println(" A"); + Serial.print("Power: "); Serial.print(power,3); Serial.println(" W"); + Serial.print("Ah: "); Serial.print(Ah,3); Serial.println(" Ah"); + Serial.println(""); + } + // blink led + digitalWrite(13, led); + led = !led; + + delay(10); +} + +void readCurrent() { + uint32_t count = 0; + unsigned long newtime; + +// Serial.println("waiting data ready"); + + // reads busVoltage + busvoltage = ina219.busVoltage(); + // waits for conversion ready + while(!ina219.ready() && count < 500) { + count++; + delay(1); + busvoltage = ina219.busVoltage(); + } + +// Serial.print("Count: "); Serial.println(count); + + // read the other values + shuntvoltage = ina219.shuntVoltage() * 1000; + current_A = ina219.shuntCurrent(); + batvoltage = busvoltage + (shuntvoltage / 1000); + power = ina219.busPower(); + newtime = millis(); + tick = newtime - lastread; + Ah += (current_A * tick)/3600000.0; + lastread = newtime; + + // prepare for next read -- this is security just in case the ina219 is reset by transient curent + ina219.recalibrate(); + ina219.reconfig(); +} + diff --git a/.piolibdeps/ArduinoINA219_ID411/examples/ina219_test/ina219_test.ino b/.piolibdeps/ArduinoINA219_ID411/examples/ina219_test/ina219_test.ino new file mode 100644 index 0000000..ffce3d3 --- /dev/null +++ b/.piolibdeps/ArduinoINA219_ID411/examples/ina219_test/ina219_test.ino @@ -0,0 +1,63 @@ +/********************************************** +* INA219 library example +* 10 May 2012 by johngineer +* +* 9 January 2016 Flavius Bindea: changed default values and begin() +* +* this code is public domain. +**********************************************/ + + +#include +#include + +INA219 monitor; + + +void setup() +{ + Serial.begin(9600); + monitor.begin(); + // begin calls: + // configure() with default values RANGE_32V, GAIN_8_320MV, ADC_12BIT, ADC_12BIT, CONT_SH_BUS + // calibrate() with default values D_SHUNT=0.1, D_V_BUS_MAX=32, D_V_SHUNT_MAX=0.2, D_I_MAX_EXPECTED=2 + // in order to work directly with ADAFruit's INA219B breakout +} + +void loop() +{ + + Serial.println("******************"); + + Serial.print("raw shunt voltage: "); + Serial.println(monitor.shuntVoltageRaw()); + + Serial.print("raw bus voltage: "); + Serial.println(monitor.busVoltageRaw()); + + Serial.println("--"); + + Serial.print("shunt voltage: "); + Serial.print(monitor.shuntVoltage() * 1000, 4); + Serial.println(" mV"); + + Serial.print("shunt current: "); + Serial.print(monitor.shuntCurrent() * 1000, 4); + Serial.println(" mA"); + + Serial.print("bus voltage: "); + Serial.print(monitor.busVoltage(), 4); + Serial.println(" V"); + + Serial.print("bus power: "); + Serial.print(monitor.busPower() * 1000, 4); + Serial.println(" mW"); + + Serial.println(" "); + Serial.println(" "); + + delay(10000); + +} + + diff --git a/.piolibdeps/ArduinoINA219_ID411/examples/ina219_test_nondefault/ina219_test_nondefault.ino b/.piolibdeps/ArduinoINA219_ID411/examples/ina219_test_nondefault/ina219_test_nondefault.ino new file mode 100644 index 0000000..19c50cc --- /dev/null +++ b/.piolibdeps/ArduinoINA219_ID411/examples/ina219_test_nondefault/ina219_test_nondefault.ino @@ -0,0 +1,66 @@ +/********************************************** +* INA219 library example +* 9 January 2016 by Flavius Bindea +* +* this code is public domain. +**********************************************/ + + +#include +#include + +#define SHUNT_MAX_V 0.04 /* Rated max for our shunt is 75mv for 50 A current: + we will mesure only up to 20A so max is about 75mV*20/50 lets put some more*/ +#define BUS_MAX_V 16.0 /* with 12v lead acid battery this should be enough*/ +#define MAX_CURRENT 20 /* In our case this is enaugh even shunt is capable to 50 A*/ +#define SHUNT_R 0.015 /* Shunt resistor in ohm */ + +INA219 monitor; + +void setup() +{ + Serial.begin(57600); + monitor.begin(); + // setting up our configuration + // default values are RANGE_32V, GAIN_8_320MV, ADC_12BIT, ADC_12BIT, CONT_SH_BUS + monitor.configure(INA219::RANGE_16V, INA219::GAIN_2_80MV, INA219::ADC_64SAMP, INA219::ADC_64SAMP, INA219::CONT_SH_BUS); + + // calibrate with our values + monitor.calibrate(SHUNT_R, SHUNT_MAX_V, BUS_MAX_V, MAX_CURRENT); +} + +void loop() +{ + Serial.println("******************"); + + Serial.print("raw shunt voltage: "); + Serial.println(monitor.shuntVoltageRaw()); + + Serial.print("raw bus voltage: "); + Serial.println(monitor.busVoltageRaw()); + + Serial.println("--"); + + Serial.print("shunt voltage: "); + Serial.print(monitor.shuntVoltage() * 1000, 4); + Serial.println(" mV"); + + Serial.print("shunt current: "); + Serial.print(monitor.shuntCurrent() * 1000, 4); + Serial.println(" mA"); + + Serial.print("bus voltage: "); + Serial.print(monitor.busVoltage(), 4); + Serial.println(" V"); + + Serial.print("bus power: "); + Serial.print(monitor.busPower() * 1000, 4); + Serial.println(" mW"); + + Serial.println(" "); + Serial.println(" "); + + delay(1000); +} + + diff --git a/.piolibdeps/ArduinoINA219_ID411/keywords.txt b/.piolibdeps/ArduinoINA219_ID411/keywords.txt new file mode 100644 index 0000000..9009469 --- /dev/null +++ b/.piolibdeps/ArduinoINA219_ID411/keywords.txt @@ -0,0 +1,16 @@ +INA219 KEYWORD1 +begin KEYWORD2 +calibrate KEYWORD2 +configure KEYWORD2 +reset KEYWORD2 +shuntVoltageRaw KEYWORD2 +busVoltageRaw KEYWORD2 +shuntCurrentRaw KEYWORD2 +shuntVoltage KEYWORD2 +busVoltage KEYWORD2 +shuntCurrent KEYWORD2 +busPower KEYWORD2 +reconfig KEYWORD2 +recalibrate KEYWORD2 +ready KEYWORD2 +overflow KEYWORD2 diff --git a/.piolibdeps/ArduinoINA219_ID411/library.json b/.piolibdeps/ArduinoINA219_ID411/library.json new file mode 100644 index 0000000..370a4cc --- /dev/null +++ b/.piolibdeps/ArduinoINA219_ID411/library.json @@ -0,0 +1,12 @@ +{ + "name": "INA219", + "keywords": "i2c, driver, sensor, battery, monitor, current, voltage", + "description": "TI INA219 hi-side i2c current/power monitor Library", + "repository": + { + "type": "git", + "url": "https://github.com/johngineer/ArduinoINA219" + }, + "frameworks": "arduino", + "platforms": "atmelavr" +} diff --git a/.piolibdeps/ArduinoINA219_ID411/library.properties b/.piolibdeps/ArduinoINA219_ID411/library.properties new file mode 100644 index 0000000..be72074 --- /dev/null +++ b/.piolibdeps/ArduinoINA219_ID411/library.properties @@ -0,0 +1,9 @@ +name=ArduinoINA219 +version=1.0.1 +author=DeCristofaro John , Jukka-Pekka Sarjanen , gandy92 , Flavius Bindea +maintainer=Flavius Bindea +sentence=INA219 hi-side i2c current/power sensor Library +paragraph=INA219 hi-side i2c current/power sensor Library +category=Sensors +url=https://github.com/flav1972/ArduinoINA219 +architectures=* \ No newline at end of file diff --git a/.piolibdeps/AsyncTCP_ID1826/.library.json b/.piolibdeps/AsyncTCP_ID1826/.library.json new file mode 100644 index 0000000..0724c15 --- /dev/null +++ b/.piolibdeps/AsyncTCP_ID1826/.library.json @@ -0,0 +1,32 @@ +{ + "license": "LGPL-3.0", + "name": "AsyncTCP", + "repository": { + "url": "https://github.com/me-no-dev/AsyncTCP.git", + "type": "git" + }, + "platforms": [ + "espressif32" + ], + "frameworks": [ + "arduino" + ], + "version": "1.0.1", + "build": { + "libCompatMode": 2 + }, + "authors": [ + { + "url": null, + "maintainer": true, + "email": null, + "name": "Hristo Gochkov" + } + ], + "keywords": [ + "async", + "tcp" + ], + "id": 1826, + "description": "Asynchronous TCP Library for ESP32" +} \ No newline at end of file diff --git a/.piolibdeps/AsyncTCP_ID1826/.travis.yml b/.piolibdeps/AsyncTCP_ID1826/.travis.yml new file mode 100644 index 0000000..8980880 --- /dev/null +++ b/.piolibdeps/AsyncTCP_ID1826/.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/.piolibdeps/AsyncTCP_ID1826/LICENSE b/.piolibdeps/AsyncTCP_ID1826/LICENSE new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/.piolibdeps/AsyncTCP_ID1826/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/.piolibdeps/AsyncTCP_ID1826/README.md b/.piolibdeps/AsyncTCP_ID1826/README.md new file mode 100644 index 0000000..023849b --- /dev/null +++ b/.piolibdeps/AsyncTCP_ID1826/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/.piolibdeps/AsyncTCP_ID1826/component.mk b/.piolibdeps/AsyncTCP_ID1826/component.mk new file mode 100644 index 0000000..bb5bb16 --- /dev/null +++ b/.piolibdeps/AsyncTCP_ID1826/component.mk @@ -0,0 +1,3 @@ +COMPONENT_ADD_INCLUDEDIRS := src +COMPONENT_SRCDIRS := src +CXXFLAGS += -fno-rtti diff --git a/.piolibdeps/AsyncTCP_ID1826/library.json b/.piolibdeps/AsyncTCP_ID1826/library.json new file mode 100644 index 0000000..e7ad4e2 --- /dev/null +++ b/.piolibdeps/AsyncTCP_ID1826/library.json @@ -0,0 +1,22 @@ +{ + "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.1", + "license": "LGPL-3.0", + "frameworks": "arduino", + "platforms": "espressif32", + "build": { + "libCompatMode": 2 + } +} diff --git a/.piolibdeps/AsyncTCP_ID1826/library.properties b/.piolibdeps/AsyncTCP_ID1826/library.properties new file mode 100644 index 0000000..d9e8e47 --- /dev/null +++ b/.piolibdeps/AsyncTCP_ID1826/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/.piolibdeps/AsyncTCP_ID1826/src/AsyncTCP.cpp b/.piolibdeps/AsyncTCP_ID1826/src/AsyncTCP.cpp new file mode 100644 index 0000000..ba1929b --- /dev/null +++ b/.piolibdeps/AsyncTCP_ID1826/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/.piolibdeps/AsyncTCP_ID1826/src/AsyncTCP.h b/.piolibdeps/AsyncTCP_ID1826/src/AsyncTCP.h new file mode 100644 index 0000000..1c24ac9 --- /dev/null +++ b/.piolibdeps/AsyncTCP_ID1826/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/.piolibdeps/AsyncTCP_ID1826/travis/common.sh b/.piolibdeps/AsyncTCP_ID1826/travis/common.sh new file mode 100644 index 0000000..57bede3 --- /dev/null +++ b/.piolibdeps/AsyncTCP_ID1826/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/.piolibdeps/ESP Async WebServer_ID306/.library.json b/.piolibdeps/ESP Async WebServer_ID306/.library.json new file mode 100644 index 0000000..48bc5af --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/.library.json @@ -0,0 +1,42 @@ +{ + "license": "LGPL-3.0", + "name": "ESP Async WebServer", + "repository": { + "url": "https://github.com/me-no-dev/ESPAsyncWebServer.git", + "type": "git" + }, + "platforms": [ + "espressif32", + "espressif8266" + ], + "frameworks": [ + "arduino" + ], + "version": "1.2.0", + "dependencies": [ + { + "platforms": "espressif8266", + "name": "ESPAsyncTCP" + }, + { + "platforms": "espressif32", + "name": "AsyncTCP" + } + ], + "authors": [ + { + "url": null, + "maintainer": true, + "email": null, + "name": "Hristo Gochkov" + } + ], + "keywords": [ + "async", + "websocket", + "http", + "webserver" + ], + "id": 306, + "description": "Asynchronous HTTP and WebSocket Server Library for ESP8266 and ESP32" +} \ No newline at end of file diff --git a/.piolibdeps/ESP Async WebServer_ID306/.travis.yml b/.piolibdeps/ESP Async WebServer_ID306/.travis.yml new file mode 100644 index 0000000..ce0228f --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/.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/.piolibdeps/ESP Async WebServer_ID306/README.md b/.piolibdeps/ESP Async WebServer_ID306/README.md new file mode 100644 index 0000000..7ec5424 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/README.md @@ -0,0 +1,1412 @@ +# 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-) + - [Installation](#installation) + - [Using PlatformIO](#using-platformio) + - [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) + - [JSON body handling with ArduinoJson](#json-body-handling-with-arduinojson) + - [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) + +## Installation + +### Using PlatformIO + +[PlatformIO](http://platformio.org) is an open source ecosystem for IoT development with cross platform build system, library manager and full support for Espressif ESP8266/ESP32 development. It works on the popular host OS: Mac OS X, Windows, Linux 32/64, Linux ARM (like Raspberry Pi, BeagleBone, CubieBoard). + +1. Install [PlatformIO IDE](http://platformio.org/platformio-ide) +2. Create new project using "PlatformIO Home > New Project" +3. Update dev/platform to staging version: + - [Instruction for Espressif 8266](http://docs.platformio.org/en/latest/platforms/espressif8266.html#using-arduino-framework-with-staging-version) + - [Instruction for Espressif 32](http://docs.platformio.org/en/latest/platforms/espressif32.html#using-arduino-framework-with-staging-version) + 4. Add "ESP Async WebServer" to project using [Project Configuration File `platformio.ini`](http://docs.platformio.org/page/projectconf.html) and [lib_deps](http://docs.platformio.org/page/projectconf/section_env_library.html#lib-deps) option: +```ini +[env:myboard] +platform = espressif... +board = ... +framework = arduino + +# using the latest stable version +lib_deps = ESP Async WebServer + +# or using GIT Url (the latest development version) +lib_deps = https://github.com/me-no-dev/ESPAsyncWebServer.git +``` + 5. Happy coding with PlatformIO! + +## 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; i(); + // ... +}); +server.addHandler(handler); +``` + +## Responses +### Redirect to another URL +```cpp +//to local url +request->redirect("/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("application/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.printfAll_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, "application/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/.piolibdeps/ESP Async WebServer_ID306/component.mk b/.piolibdeps/ESP Async WebServer_ID306/component.mk new file mode 100644 index 0000000..bb5bb16 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/component.mk @@ -0,0 +1,3 @@ +COMPONENT_ADD_INCLUDEDIRS := src +COMPONENT_SRCDIRS := src +CXXFLAGS += -fno-rtti diff --git a/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/.esp31b.skip b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/.esp31b.skip new file mode 100644 index 0000000..e69de29 diff --git a/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino new file mode 100644 index 0000000..b350dc3 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino @@ -0,0 +1,211 @@ +#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/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/.exclude.files b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/.exclude.files new file mode 100644 index 0000000..955397f --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/.exclude.files @@ -0,0 +1,2 @@ +/*.js.gz +/.exclude.files diff --git a/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/ace.js.gz b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/ace.js.gz new file mode 100644 index 0000000..7b175c1 Binary files /dev/null and b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/ace.js.gz differ diff --git a/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz new file mode 100644 index 0000000..cf5b49f Binary files /dev/null and b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz differ diff --git a/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/favicon.ico b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/favicon.ico new file mode 100644 index 0000000..71b25fe Binary files /dev/null and b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/favicon.ico differ diff --git a/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/index.htm b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/index.htm new file mode 100644 index 0000000..28f47e9 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/index.htm @@ -0,0 +1,131 @@ + + + + + + WebSocketTester + + + + +

+    
+ $ +
+ + diff --git a/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz new file mode 100644 index 0000000..ebd6fe9 Binary files /dev/null and b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz differ diff --git a/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz new file mode 100644 index 0000000..26b5353 Binary files /dev/null and b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz differ diff --git a/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz new file mode 100644 index 0000000..c0451c1 Binary files /dev/null and b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz differ diff --git a/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz new file mode 100644 index 0000000..ec8aa87 Binary files /dev/null and b/.piolibdeps/ESP Async WebServer_ID306/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz differ diff --git a/.piolibdeps/ESP Async WebServer_ID306/examples/simple_server/simple_server.ino b/.piolibdeps/ESP Async WebServer_ID306/examples/simple_server/simple_server.ino new file mode 100644 index 0000000..6fee6f7 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/examples/simple_server/simple_server.ino @@ -0,0 +1,72 @@ +// +// A simple server implementation showing how to: +// * serve static messages +// * read GET and POST parameters +// * handle missing pages / 404s +// + +#include +#include +#include +#include +#include + +AsyncWebServer server(80); + +const char* ssid = "YOUR_SSID"; +const char* password = "YOUR_PASSWORD"; + +const char* PARAM_MESSAGE = "message"; + +void notFound(AsyncWebServerRequest *request) { + request->send(404, "text/plain", "Not found"); +} + +void setup() { + + Serial.begin(115200); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.printf("WiFi Failed!\n"); + return; + } + + Serial.print("IP Address: "); + Serial.println(WiFi.localIP()); + Serial.print("Hostname: "); + Serial.println(WiFi.hostname()); + + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/plain", "Hello, world"); + }); + + // Send a GET request to /get?message= + server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) { + String message; + if (request->hasParam(PARAM_MESSAGE)) { + message = request->getParam(PARAM_MESSAGE)->value(); + } else { + message = "No message sent"; + } + request->send(200, "text/plain", "Hello, GET: " + message); + }); + + // Send a POST request to /post with a form field message set to + server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){ + String message; + if (request->hasParam(PARAM_MESSAGE, true)) { + message = request->getParam(PARAM_MESSAGE, true)->value(); + } else { + message = "No message sent"; + } + request->send(200, "text/plain", "Hello, POST: " + message); + }); + + server.onNotFound(notFound); + + server.begin(); +} + +void loop() { +} \ No newline at end of file diff --git a/.piolibdeps/ESP Async WebServer_ID306/keywords.txt b/.piolibdeps/ESP Async WebServer_ID306/keywords.txt new file mode 100644 index 0000000..c391e6c --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/keywords.txt @@ -0,0 +1,3 @@ +JsonArray KEYWORD1 +add KEYWORD2 +createArray KEYWORD3 diff --git a/.piolibdeps/ESP Async WebServer_ID306/library.json b/.piolibdeps/ESP Async WebServer_ID306/library.json new file mode 100644 index 0000000..a2f961a --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/library.json @@ -0,0 +1,29 @@ +{ + "name":"ESP Async WebServer", + "description":"Asynchronous HTTP and WebSocket Server Library for ESP8266 and 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.2.0", + "license": "LGPL-3.0", + "frameworks": "arduino", + "platforms": ["espressif8266", "espressif32"], + "dependencies": [ + { + "name": "ESPAsyncTCP", + "platforms": "espressif8266" + }, + { + "name": "AsyncTCP", + "platforms": "espressif32" + } + ] +} diff --git a/.piolibdeps/ESP Async WebServer_ID306/library.properties b/.piolibdeps/ESP Async WebServer_ID306/library.properties new file mode 100644 index 0000000..5c144dc --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/library.properties @@ -0,0 +1,9 @@ +name=ESP Async WebServer +version=1.2.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/.piolibdeps/ESP Async WebServer_ID306/src/AsyncEventSource.cpp b/.piolibdeps/ESP Async WebServer_ID306/src/AsyncEventSource.cpp new file mode 100644 index 0000000..ecb6b01 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/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/.piolibdeps/ESP Async WebServer_ID306/src/AsyncEventSource.h b/.piolibdeps/ESP Async WebServer_ID306/src/AsyncEventSource.h new file mode 100644 index 0000000..1b4d604 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/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/.piolibdeps/ESP Async WebServer_ID306/src/AsyncJson.h b/.piolibdeps/ESP Async WebServer_ID306/src/AsyncJson.h new file mode 100644 index 0000000..062cbbc --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/src/AsyncJson.h @@ -0,0 +1,163 @@ +// AsyncJson.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); + }); + + -------------------- + + Async Request to use with ArduinoJson and AsyncWebServer + Written by Arsène von Wyss (avonwyss) + + Example + + AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint"); + handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) { + JsonObject& jsonObj = json.as(); + // ... + }); + server.addHandler(handler); + +*/ +#ifndef ASYNC_JSON_H_ +#define ASYNC_JSON_H_ +#include + +const char* JSON_MIMETYPE = "application/json"; + +/* + * 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 = JSON_MIMETYPE; + 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; + } +}; + +typedef std::function ArJsonRequestHandlerFunction; + +class AsyncCallbackJsonWebHandler: public AsyncWebHandler { +private: +protected: + const String _uri; + WebRequestMethodComposite _method; + ArJsonRequestHandlerFunction _onRequest; + int _contentLength; + int _maxContentLength; +public: + AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest) : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {} + void setMethod(WebRequestMethodComposite method){ _method = method; } + void setMaxContentLength(int maxContentLength){ _maxContentLength = maxContentLength; } + void onRequest(ArJsonRequestHandlerFunction fn){ _onRequest = 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; + + if (!request->contentType().equalsIgnoreCase(JSON_MIMETYPE)) + return false; + + request->addInterestingHeader("ANY"); + return true; + } + + virtual void handleRequest(AsyncWebServerRequest *request) override final { + if(_onRequest) { + if (request->_tempObject != NULL) { + DynamicJsonBuffer jsonBuffer; + JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject)); + if (json.success()) { + _onRequest(request, json); + return; + } + } + request->send(_contentLength > _maxContentLength ? 413 : 400); + } 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 { + } + virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final { + if (_onRequest) { + _contentLength = total; + if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) { + request->_tempObject = malloc(total); + } + if (request->_tempObject != NULL) { + memcpy((uint8_t*)(request->_tempObject) + index, data, len); + } + } + } + virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;} +}; +#endif diff --git a/.piolibdeps/ESP Async WebServer_ID306/src/AsyncWebSocket.cpp b/.piolibdeps/ESP Async WebServer_ID306/src/AsyncWebSocket.cpp new file mode 100644 index 0000000..8641a48 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/src/AsyncWebSocket.cpp @@ -0,0 +1,1218 @@ +/* + 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[_len + 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[_len + 1]; + + 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 *pbuf, size_t plen){ + _lastMessageTime = millis(); + uint8_t *data = (uint8_t*)pbuf; + while(plen > 0){ + if(!_pstate){ + const uint8_t *fdata = data; + _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 -= 2; + if(_pinfo.len == 126){ + _pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8; + data += 2; + 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 -= 8; + } + + if(_pinfo.masked){ + memcpy(_pinfo.mask, data, 4); + data += 4; + plen -= 4; + } + } + + const size_t datalen = std::min((size_t)(_pinfo.len - _pinfo.index), plen); + const auto datalast = data[datalen]; + + if(_pinfo.masked){ + for(size_t i=0;i_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, datalen); + + _pinfo.index += datalen; + } else if((datalen + _pinfo.index) == _pinfo.len){ + _pstate = 0; + if(_pinfo.opcode == WS_DISCONNECT){ + if(datalen){ + 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, datalen)); + } + } else if(_pinfo.opcode == WS_PING){ + _queueControl(new AsyncWebSocketControl(WS_PONG, data, datalen)); + } else if(_pinfo.opcode == WS_PONG){ + if(datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) + _server->_handleEvent(this, WS_EVT_PONG, NULL, data, datalen); + } else if(_pinfo.opcode < 8){//continuation or text/binary frame + _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen); + } + } else { + //os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len); + //what should we do? + break; + } + + // restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0; + if (datalen > 0) + data[datalen] = datalast; + + data += datalen; + plen -= datalen; + } +} + +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); + 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(AsyncWebSocketMessageBuffer * c: _buffers){ + if(c && 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/.piolibdeps/ESP Async WebServer_ID306/src/AsyncWebSocket.h b/.piolibdeps/ESP Async WebServer_ID306/src/AsyncWebSocket.h new file mode 100644 index 0000000..37c8d11 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/src/AsyncWebSocket.h @@ -0,0 +1,321 @@ +/* + 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 { + /** Message type as defined by enum AwsFrameType. + * Note: Applications will only see WS_TEXT and WS_BINARY. + * All other types are handled by the library. */ + uint8_t message_opcode; + /** Frame number of a fragmented message. */ + uint32_t num; + /** Is this the last frame in a fragmented message ?*/ + uint8_t final; + /** Is this frame masked? */ + uint8_t masked; + /** Message type as defined by enum AwsFrameType. + * This value is the same as message_opcode for non-fragmented + * messages, but may also be WS_CONTINUATION in a fragmented message. */ + uint8_t opcode; + /** Length of the current frame. + * This equals the total length of the message if num == 0 && final == true */ + uint64_t len; + /** Mask key */ + uint8_t mask[4]; + /** Offset of the data inside the current frame. */ + 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; } + AsyncWebSocket *server(){ return _server; } + AwsFrameInfo const &pinfo() const { return _pinfo; } + + 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 *pbuf, 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/.piolibdeps/ESP Async WebServer_ID306/src/ESPAsyncWebServer.h b/.piolibdeps/ESP Async WebServer_ID306/src/ESPAsyncWebServer.h new file mode 100644 index 0000000..de6f0cd --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/src/ESPAsyncWebServer.h @@ -0,0 +1,457 @@ +/* + 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; + +#ifndef WEBSERVER_H +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; +#endif + +//if this value is returned when asked for data, packet will not be sent and you will be asked for data again +#define RESPONSE_TRY_AGAIN 0xFFFFFFFF + +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))){} + virtual bool isRequestHandlerTrivial(){return true;} +}; + +/* + * 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/.piolibdeps/ESP Async WebServer_ID306/src/SPIFFSEditor.cpp b/.piolibdeps/ESP Async WebServer_ID306/src/SPIFFSEditor.cpp new file mode 100644 index 0000000..a84fa87 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/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, "application/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/.piolibdeps/ESP Async WebServer_ID306/src/SPIFFSEditor.h b/.piolibdeps/ESP Async WebServer_ID306/src/SPIFFSEditor.h new file mode 100644 index 0000000..3586429 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/src/SPIFFSEditor.h @@ -0,0 +1,24 @@ +#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; + virtual bool isRequestHandlerTrivial() override final {return false;} +}; + +#endif diff --git a/.piolibdeps/ESP Async WebServer_ID306/src/StringArray.h b/.piolibdeps/ESP Async WebServer_ID306/src/StringArray.h new file mode 100644 index 0000000..4c0aa70 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/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/.piolibdeps/ESP Async WebServer_ID306/src/WebAuthentication.cpp b/.piolibdeps/ESP Async WebServer_ID306/src/WebAuthentication.cpp new file mode 100644 index 0000000..2feca54 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/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/.piolibdeps/ESP Async WebServer_ID306/src/WebAuthentication.h b/.piolibdeps/ESP Async WebServer_ID306/src/WebAuthentication.h new file mode 100644 index 0000000..ff68265 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/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/.piolibdeps/ESP Async WebServer_ID306/src/WebHandlerImpl.h b/.piolibdeps/ESP Async WebServer_ID306/src/WebHandlerImpl.h new file mode 100644 index 0000000..b2da6fe --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/src/WebHandlerImpl.h @@ -0,0 +1,110 @@ +/* + 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); + } + virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;} +}; + +#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */ diff --git a/.piolibdeps/ESP Async WebServer_ID306/src/WebHandlers.cpp b/.piolibdeps/ESP Async WebServer_ID306/src/WebHandlers.cpp new file mode 100644 index 0000000..1f435e6 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/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/.piolibdeps/ESP Async WebServer_ID306/src/WebRequest.cpp b/.piolibdeps/ESP Async WebServer_ID306/src/WebRequest.cpp new file mode 100644 index 0000000..dbcb927 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/src/WebRequest.cpp @@ -0,0 +1,998 @@ +/* + 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){ + // A handler should be already attached at this point in _parseLine function. + // If handler does nothing (_onRequest is NULL), we don't need to really parse the body. + const bool needParse = _handler && !_handler->isRequestHandlerTrivial(); + if(_isMultipart){ + if(needParse){ + 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_P(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/.piolibdeps/ESP Async WebServer_ID306/src/WebResponseImpl.h b/.piolibdeps/ESP Async WebServer_ID306/src/WebResponseImpl.h new file mode 100644 index 0000000..9a64e3a --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/src/WebResponseImpl.h @@ -0,0 +1,136 @@ +/* + 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; + // Data is inserted into cache at begin(). + // This is inefficient with vector, but if we use some other container, + // we won't be able to access it as contiguous array of bytes when reading from it, + // so by gaining performance in one place, we'll lose it in another. + 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; } +}; + +#ifndef TEMPLATE_PLACEHOLDER +#define TEMPLATE_PLACEHOLDER '%' +#endif + +#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/.piolibdeps/ESP Async WebServer_ID306/src/WebResponses.cpp b/.piolibdeps/ESP Async WebServer_ID306/src/WebResponses.cpp new file mode 100644 index 0000000..cca6b53 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/src/WebResponses.cpp @@ -0,0 +1,696 @@ +/* + 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){ + memcpy(buf, _head.c_str(), _head.length()); + } + + 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); + if(readLen == RESPONSE_TRY_AGAIN){ + free(buf); + return 0; + } + 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 { + readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen); + if(readLen == RESPONSE_TRY_AGAIN){ + free(buf); + return 0; + } + outLen = readLen + headLen; + } + + if(headLen){ + _head = String(); + } + + 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; + // 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 + _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); + 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.begin(), 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)) { + _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]); + //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(_cache.begin(), 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 = "application/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); + if(ret != RESPONSE_TRY_AGAIN){ + _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); + if(ret != RESPONSE_TRY_AGAIN){ + _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/.piolibdeps/ESP Async WebServer_ID306/src/WebServer.cpp b/.piolibdeps/ESP Async WebServer_ID306/src/WebServer.cpp new file mode 100644 index 0000000..e6cd0c6 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/src/WebServer.cpp @@ -0,0 +1,188 @@ +/* + 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.setNoDelay(true); + _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/.piolibdeps/ESP Async WebServer_ID306/src/edit.htm b/.piolibdeps/ESP Async WebServer_ID306/src/edit.htm new file mode 100644 index 0000000..43d4984 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/src/edit.htm @@ -0,0 +1,627 @@ + + + + +ESP Editor + + + + + + +
+
+
+
+ + + + diff --git a/.piolibdeps/ESP Async WebServer_ID306/travis/common.sh b/.piolibdeps/ESP Async WebServer_ID306/travis/common.sh new file mode 100644 index 0000000..57bede3 --- /dev/null +++ b/.piolibdeps/ESP Async WebServer_ID306/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/.piolibdeps/ESP8266FtpServer_ID992/.library.json b/.piolibdeps/ESP8266FtpServer_ID992/.library.json new file mode 100644 index 0000000..910ea2a --- /dev/null +++ b/.piolibdeps/ESP8266FtpServer_ID992/.library.json @@ -0,0 +1,40 @@ +{ + "description": "Very Simple FTP server for SPIFFS on esp8266", + "repository": { + "url": "https://github.com/nailbuster/esp8266FTPServer", + "type": "git" + }, + "platforms": [ + "espressif32", + "espressif8266" + ], + "export": { + "exclude": [ + "extras", + "docs", + "tests", + "test", + "*.doxyfile", + "*.pdf" + ], + "include": null + }, + "authors": [ + { + "maintainer": true, + "name": "david@paivahome.com", + "url": null, + "email": null + } + ], + "keywords": [ + "communication" + ], + "id": 992, + "name": "ESP8266FtpServer", + "frameworks": [ + "arduino" + ], + "version": "1.0.1", + "homepage": null +} \ No newline at end of file diff --git a/.piolibdeps/ESP8266FtpServer_ID992/ESP8266FtpServer.cpp b/.piolibdeps/ESP8266FtpServer_ID992/ESP8266FtpServer.cpp new file mode 100644 index 0000000..7d25e9a --- /dev/null +++ b/.piolibdeps/ESP8266FtpServer_ID992/ESP8266FtpServer.cpp @@ -0,0 +1,1015 @@ +/* + * FTP Serveur for ESP8266 + * based on FTP Serveur for Arduino Due and Ethernet shield (W5100) or WIZ820io (W5200) + * based on Jean-Michel Gallego's work + * modified to work with esp8266 SPIFFS by David Paiva david@nailbuster.com + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 program. If not, see . + */ + +#include "ESP8266FtpServer.h" +#ifdef ESP8266 +#include +#elif defined ESP32 +#include +#include "SPIFFS.h" +#endif +#include +#include + + + + +WiFiServer ftpServer( FTP_CTRL_PORT ); +WiFiServer dataServer( FTP_DATA_PORT_PASV ); + +void FtpServer::begin(String uname, String pword) +{ + // Tells the ftp server to begin listening for incoming connection + _FTP_USER=uname; + _FTP_PASS = pword; + + ftpServer.begin(); + delay(10); + dataServer.begin(); + delay(10); + millisTimeOut = (uint32_t)FTP_TIME_OUT * 60 * 1000; + millisDelay = 0; + cmdStatus = 0; + iniVariables(); +} + +void FtpServer::iniVariables() +{ + // Default for data port + dataPort = FTP_DATA_PORT_PASV; + + // Default Data connection is Active + dataPassiveConn = true; + + // Set the root directory + strcpy( cwdName, "/" ); + + rnfrCmd = false; + transferStatus = 0; + +} + +void FtpServer::handleFTP() +{ + if((int32_t) ( millisDelay - millis() ) > 0 ) + return; + + if (ftpServer.hasClient()) { + client.stop(); + client = ftpServer.available(); + } + + if( cmdStatus == 0 ) + { + if( client.connected()) + disconnectClient(); + cmdStatus = 1; + } + else if( cmdStatus == 1 ) // Ftp server waiting for connection + { + abortTransfer(); + iniVariables(); + #ifdef FTP_DEBUG + Serial.println("Ftp server waiting for connection on port "+ String(FTP_CTRL_PORT)); + #endif + cmdStatus = 2; + } + else if( cmdStatus == 2 ) // Ftp server idle + { + + if( client.connected() ) // A client connected + { + clientConnected(); + millisEndConnection = millis() + 10 * 1000 ; // wait client id during 10 s. + cmdStatus = 3; + } + } + else if( readChar() > 0 ) // got response + { + if( cmdStatus == 3 ) // Ftp server waiting for user identity + if( userIdentity() ) + cmdStatus = 4; + else + cmdStatus = 0; + else if( cmdStatus == 4 ) // Ftp server waiting for user registration + if( userPassword() ) + { + cmdStatus = 5; + millisEndConnection = millis() + millisTimeOut; + } + else + cmdStatus = 0; + else if( cmdStatus == 5 ) // Ftp server waiting for user command + if( ! processCommand()) + cmdStatus = 0; + else + millisEndConnection = millis() + millisTimeOut; + } + else if (!client.connected() || !client) + { + cmdStatus = 1; + #ifdef FTP_DEBUG + Serial.println("client disconnected"); + #endif + } + + if( transferStatus == 1 ) // Retrieve data + { + if( ! doRetrieve()) + transferStatus = 0; + } + else if( transferStatus == 2 ) // Store data + { + if( ! doStore()) + transferStatus = 0; + } + else if( cmdStatus > 2 && ! ((int32_t) ( millisEndConnection - millis() ) > 0 )) + { + client.println("530 Timeout"); + millisDelay = millis() + 200; // delay of 200 ms + cmdStatus = 0; + } +} + +void FtpServer::clientConnected() +{ + #ifdef FTP_DEBUG + Serial.println("Client connected!"); + #endif + client.println( "220--- Welcome to FTP for ESP8266/ESP32 ---"); + client.println( "220--- By David Paiva ---"); + client.println( "220 -- Version "+ String(FTP_SERVER_VERSION) +" --"); + iCL = 0; +} + +void FtpServer::disconnectClient() +{ + #ifdef FTP_DEBUG + Serial.println(" Disconnecting client"); + #endif + abortTransfer(); + client.println("221 Goodbye"); + client.stop(); +} + +boolean FtpServer::userIdentity() +{ + if( strcmp( command, "USER" )) + client.println( "500 Syntax error"); + if( strcmp( parameters, _FTP_USER.c_str() )) + client.println( "530 user not found"); + else + { + client.println( "331 OK. Password required"); + strcpy( cwdName, "/" ); + return true; + } + millisDelay = millis() + 100; // delay of 100 ms + return false; +} + +boolean FtpServer::userPassword() +{ + if( strcmp( command, "PASS" )) + client.println( "500 Syntax error"); + else if( strcmp( parameters, _FTP_PASS.c_str() )) + client.println( "530 "); + else + { + #ifdef FTP_DEBUG + Serial.println( "OK. Waiting for commands."); + #endif + client.println( "230 OK."); + return true; + } + millisDelay = millis() + 100; // delay of 100 ms + return false; +} + +boolean FtpServer::processCommand() +{ + /////////////////////////////////////// + // // + // ACCESS CONTROL COMMANDS // + // // + /////////////////////////////////////// + + // + // CDUP - Change to Parent Directory + // + if( ! strcmp( command, "CDUP" )) + { + client.println("250 Ok. Current directory is " + String(cwdName)); + } + // + // CWD - Change Working Directory + // + else if( ! strcmp( command, "CWD" )) + { + char path[ FTP_CWD_SIZE ]; + if( strcmp( parameters, "." ) == 0 ) // 'CWD .' is the same as PWD command + client.println( "257 \"" + String(cwdName) + "\" is your current directory"); + else + { + client.println( "250 Ok. Current directory is " + String(cwdName) ); + } + + } + // + // PWD - Print Directory + // + else if( ! strcmp( command, "PWD" )) + client.println( "257 \"" + String(cwdName) + "\" is your current directory"); + // + // QUIT + // + else if( ! strcmp( command, "QUIT" )) + { + disconnectClient(); + return false; + } + + /////////////////////////////////////// + // // + // TRANSFER PARAMETER COMMANDS // + // // + /////////////////////////////////////// + + // + // MODE - Transfer Mode + // + else if( ! strcmp( command, "MODE" )) + { + if( ! strcmp( parameters, "S" )) + client.println( "200 S Ok"); + // else if( ! strcmp( parameters, "B" )) + // client.println( "200 B Ok\r\n"; + else + client.println( "504 Only S(tream) is suported"); + } + // + // PASV - Passive Connection management + // + else if( ! strcmp( command, "PASV" )) + { + if (data.connected()) data.stop(); + //dataServer.begin(); + //dataIp = Ethernet.localIP(); + dataIp = client.localIP(); + dataPort = FTP_DATA_PORT_PASV; + //data.connect( dataIp, dataPort ); + //data = dataServer.available(); + #ifdef FTP_DEBUG + Serial.println("Connection management set to passive"); + Serial.println( "Data port set to " + String(dataPort)); + #endif + client.println( "227 Entering Passive Mode ("+ String(dataIp[0]) + "," + String(dataIp[1])+","+ String(dataIp[2])+","+ String(dataIp[3])+","+String( dataPort >> 8 ) +","+String ( dataPort & 255 )+")."); + dataPassiveConn = true; + } + // + // PORT - Data Port + // + else if( ! strcmp( command, "PORT" )) + { + if (data) data.stop(); + // get IP of data client + dataIp[ 0 ] = atoi( parameters ); + char * p = strchr( parameters, ',' ); + for( uint8_t i = 1; i < 4; i ++ ) + { + dataIp[ i ] = atoi( ++ p ); + p = strchr( p, ',' ); + } + // get port of data client + dataPort = 256 * atoi( ++ p ); + p = strchr( p, ',' ); + dataPort += atoi( ++ p ); + if( p == NULL ) + client.println( "501 Can't interpret parameters"); + else + { + + client.println("200 PORT command successful"); + dataPassiveConn = false; + } + } + // + // STRU - File Structure + // + else if( ! strcmp( command, "STRU" )) + { + if( ! strcmp( parameters, "F" )) + client.println( "200 F Ok"); + // else if( ! strcmp( parameters, "R" )) + // client.println( "200 B Ok\r\n"; + else + client.println( "504 Only F(ile) is suported"); + } + // + // TYPE - Data Type + // + else if( ! strcmp( command, "TYPE" )) + { + if( ! strcmp( parameters, "A" )) + client.println( "200 TYPE is now ASII"); + else if( ! strcmp( parameters, "I" )) + client.println( "200 TYPE is now 8-bit binary"); + else + client.println( "504 Unknow TYPE"); + } + + /////////////////////////////////////// + // // + // FTP SERVICE COMMANDS // + // // + /////////////////////////////////////// + + // + // ABOR - Abort + // + else if( ! strcmp( command, "ABOR" )) + { + abortTransfer(); + client.println( "226 Data connection closed"); + } + // + // DELE - Delete a File + // + else if( ! strcmp( command, "DELE" )) + { + char path[ FTP_CWD_SIZE ]; + if( strlen( parameters ) == 0 ) + client.println( "501 No file name"); + else if( makePath( path )) + { + if( ! SPIFFS.exists( path )) + client.println( "550 File " + String(parameters) + " not found"); + else + { + if( SPIFFS.remove( path )) + client.println( "250 Deleted " + String(parameters) ); + else + client.println( "450 Can't delete " + String(parameters)); + } + } + } + // + // LIST - List + // + else if( ! strcmp( command, "LIST" )) + { + if( ! dataConnect()) + client.println( "425 No data connection"); + else + { + client.println( "150 Accepted data connection"); + uint16_t nm = 0; +#ifdef ESP8266 + Dir dir=SPIFFS.openDir(cwdName); + // if( !SPIFFS.exists(cwdName)) + // client.println( "550 Can't open directory " + String(cwdName) ); + // else + { + while( dir.next()) + { + String fn, fs; + fn = dir.fileName(); + fn.remove(0, 1); + fs = String(dir.fileSize()); + data.println( "+r,s" + fs); + data.println( ",\t" + fn ); + nm ++; + } + client.println( "226 " + String(nm) + " matches total"); + } +#elif defined ESP32 + File root = SPIFFS.open(cwdName); + if(!root){ + client.println( "550 Can't open directory " + String(cwdName) ); + // return; + } else { + // if(!root.isDirectory()){ + // Serial.println("Not a directory"); + // return; + // } + + File file = root.openNextFile(); + while(file){ + if(file.isDirectory()){ + data.println( "+r,s " + String(file.name())); + // Serial.print(" DIR : "); + // Serial.println(file.name()); + // if(levels){ + // listDir(fs, file.name(), levels -1); + // } + } else { + String fn, fs; + fn = file.name(); + // fn.remove(0, 1); + fs = String(file.size()); + data.println( "+r,s" + fs); + data.println( ",\t" + fn ); + nm ++; + } + file = root.openNextFile(); + } + client.println( "226 " + String(nm) + " matches total"); + } +#endif + data.stop(); + } + } + // + // MLSD - Listing for Machine Processing (see RFC 3659) + // + else if( ! strcmp( command, "MLSD" )) + { + if( ! dataConnect()) + client.println( "425 No data connection MLSD"); + else + { + client.println( "150 Accepted data connection"); + uint16_t nm = 0; +#ifdef ESP8266 + Dir dir= SPIFFS.openDir(cwdName); + char dtStr[ 15 ]; + // if(!SPIFFS.exists(cwdName)) + // client.println( "550 Can't open directory " +String(parameters)+ ); + // else + { + while( dir.next()) + { + String fn,fs; + fn = dir.fileName(); + fn.remove(0, 1); + fs = String(dir.fileSize()); + data.println( "Type=file;Size=" + fs + ";"+"modify=20000101160656;" +" " + fn); + nm ++; + } + client.println( "226-options: -a -l"); + client.println( "226 " + String(nm) + " matches total"); + } +#elif defined ESP32 + File root = SPIFFS.open(cwdName); + // if(!root){ + // client.println( "550 Can't open directory " + String(cwdName) ); + // // return; + // } else { + // if(!root.isDirectory()){ + // Serial.println("Not a directory"); + // return; + // } + + File file = root.openNextFile(); + while(file){ + // if(file.isDirectory()){ + // data.println( "+r,s " + String(file.name())); + // // Serial.print(" DIR : "); + // // Serial.println(file.name()); + // // if(levels){ + // // listDir(fs, file.name(), levels -1); + // // } + // } else { + String fn, fs; + fn = file.name(); + fn.remove(0, 1); + fs = String(file.size()); + data.println( "Type=file;Size=" + fs + ";"+"modify=20000101160656;" +" " + fn); + nm ++; + // } + file = root.openNextFile(); + } + client.println( "226-options: -a -l"); + client.println( "226 " + String(nm) + " matches total"); + // } +#endif + data.stop(); + } + } + // + // NLST - Name List + // + else if( ! strcmp( command, "NLST" )) + { + if( ! dataConnect()) + client.println( "425 No data connection"); + else + { + client.println( "150 Accepted data connection"); + uint16_t nm = 0; +#ifdef ESP8266 + Dir dir=SPIFFS.openDir(cwdName); + // if( !SPIFFS.exists( cwdName )) + // client.println( "550 Can't open directory " + String(parameters)); + // else + { + while( dir.next()) + { + data.println( dir.fileName()); + nm ++; + } + client.println( "226 " + String(nm) + " matches total"); + } +#elif defined ESP32 + File root = SPIFFS.open(cwdName); + if(!root){ + client.println( "550 Can't open directory " + String(cwdName) ); + } else { + + File file = root.openNextFile(); + while(file){ + data.println( file.name()); + nm ++; + file = root.openNextFile(); + } + client.println( "226 " + String(nm) + " matches total"); + } +#endif + data.stop(); + } + } + // + // NOOP + // + else if( ! strcmp( command, "NOOP" )) + { + // dataPort = 0; + client.println( "200 Zzz..."); + } + // + // RETR - Retrieve + // + else if( ! strcmp( command, "RETR" )) + { + char path[ FTP_CWD_SIZE ]; + if( strlen( parameters ) == 0 ) + client.println( "501 No file name"); + else if( makePath( path )) + { + file = SPIFFS.open(path, "r"); + if( !file) + client.println( "550 File " +String(parameters)+ " not found"); + else if( !file ) + client.println( "450 Can't open " +String(parameters)); + else if( ! dataConnect()) + client.println( "425 No data connection"); + else + { + #ifdef FTP_DEBUG + Serial.println("Sending " + String(parameters)); + #endif + client.println( "150-Connected to port "+ String(dataPort)); + client.println( "150 " + String(file.size()) + " bytes to download"); + millisBeginTrans = millis(); + bytesTransfered = 0; + transferStatus = 1; + } + } + } + // + // STOR - Store + // + else if( ! strcmp( command, "STOR" )) + { + char path[ FTP_CWD_SIZE ]; + if( strlen( parameters ) == 0 ) + client.println( "501 No file name"); + else if( makePath( path )) + { + file = SPIFFS.open(path, "w"); + if( !file) + client.println( "451 Can't open/create " +String(parameters) ); + else if( ! dataConnect()) + { + client.println( "425 No data connection"); + file.close(); + } + else + { + #ifdef FTP_DEBUG + Serial.println( "Receiving " +String(parameters)); + #endif + client.println( "150 Connected to port " + String(dataPort)); + millisBeginTrans = millis(); + bytesTransfered = 0; + transferStatus = 2; + } + } + } + // + // MKD - Make Directory + // + else if( ! strcmp( command, "MKD" )) + { + client.println( "550 Can't create \"" + String(parameters)); //not support on espyet + } + // + // RMD - Remove a Directory + // + else if( ! strcmp( command, "RMD" )) + { + client.println( "501 Can't delete \"" +String(parameters)); + + } + // + // RNFR - Rename From + // + else if( ! strcmp( command, "RNFR" )) + { + buf[ 0 ] = 0; + if( strlen( parameters ) == 0 ) + client.println( "501 No file name"); + else if( makePath( buf )) + { + if( ! SPIFFS.exists( buf )) + client.println( "550 File " +String(parameters)+ " not found"); + else + { + #ifdef FTP_DEBUG + Serial.println("Renaming " + String(buf)); + #endif + client.println( "350 RNFR accepted - file exists, ready for destination"); + rnfrCmd = true; + } + } + } + // + // RNTO - Rename To + // + else if( ! strcmp( command, "RNTO" )) + { + char path[ FTP_CWD_SIZE ]; + char dir[ FTP_FIL_SIZE ]; + if( strlen( buf ) == 0 || ! rnfrCmd ) + client.println( "503 Need RNFR before RNTO"); + else if( strlen( parameters ) == 0 ) + client.println( "501 No file name"); + else if( makePath( path )) + { + if( SPIFFS.exists( path )) + client.println( "553 " +String(parameters)+ " already exists"); + else + { + #ifdef FTP_DEBUG + Serial.println("Renaming " + String(buf) + " to " + String(path)); + #endif + if( SPIFFS.rename( buf, path )) + client.println( "250 File successfully renamed or moved"); + else + client.println( "451 Rename/move failure"); + + } + } + rnfrCmd = false; + } + + /////////////////////////////////////// + // // + // EXTENSIONS COMMANDS (RFC 3659) // + // // + /////////////////////////////////////// + + // + // FEAT - New Features + // + else if( ! strcmp( command, "FEAT" )) + { + client.println( "211-Extensions suported:"); + client.println( " MLSD"); + client.println( "211 End."); + } + // + // MDTM - File Modification Time (see RFC 3659) + // + else if (!strcmp(command, "MDTM")) + { + client.println("550 Unable to retrieve time"); + } + + // + // SIZE - Size of the file + // + else if( ! strcmp( command, "SIZE" )) + { + char path[ FTP_CWD_SIZE ]; + if( strlen( parameters ) == 0 ) + client.println( "501 No file name"); + else if( makePath( path )) + { + file = SPIFFS.open(path, "r"); + if(!file) + client.println( "450 Can't open " +String(parameters) ); + else + { + client.println( "213 " + String(file.size())); + file.close(); + } + } + } + // + // SITE - System command + // + else if( ! strcmp( command, "SITE" )) + { + client.println( "500 Unknow SITE command " +String(parameters) ); + } + // + // Unrecognized commands ... + // + else + client.println( "500 Unknow command"); + + return true; +} + +boolean FtpServer::dataConnect() +{ + unsigned long startTime = millis(); + //wait 5 seconds for a data connection + if (!data.connected()) + { + while (!dataServer.hasClient() && millis() - startTime < 10000) + { + //delay(100); + yield(); + } + if (dataServer.hasClient()) { + data.stop(); + data = dataServer.available(); + #ifdef FTP_DEBUG + Serial.println("ftpdataserver client...."); + #endif + + } + } + + return data.connected(); + +} + +boolean FtpServer::doRetrieve() +{ +if (data.connected()) +{ + int16_t nb = file.readBytes(buf, FTP_BUF_SIZE); + if (nb > 0) + { + data.write((uint8_t*)buf, nb); + bytesTransfered += nb; + return true; + } +} +closeTransfer(); +return false; +} + +boolean FtpServer::doStore() +{ + if( data.connected() ) + { + // Avoid blocking by never reading more bytes than are available + int navail = data.available(); + if (navail <= 0) return true; + // And be sure not to overflow buf. + if (navail > FTP_BUF_SIZE) navail = FTP_BUF_SIZE; + int16_t nb = data.read((uint8_t*) buf, navail ); + // int16_t nb = data.readBytes((uint8_t*) buf, FTP_BUF_SIZE ); + if( nb > 0 ) + { + // Serial.println( millis() << " " << nb << endl; + file.write((uint8_t*) buf, nb ); + bytesTransfered += nb; + } + return true; + } + closeTransfer(); + return false; +} + +void FtpServer::closeTransfer() +{ + uint32_t deltaT = (int32_t) ( millis() - millisBeginTrans ); + if( deltaT > 0 && bytesTransfered > 0 ) + { + client.println( "226-File successfully transferred"); + client.println( "226 " + String(deltaT) + " ms, "+ String(bytesTransfered / deltaT) + " kbytes/s"); + } + else + client.println( "226 File successfully transferred"); + + file.close(); + data.stop(); +} + +void FtpServer::abortTransfer() +{ + if( transferStatus > 0 ) + { + file.close(); + data.stop(); + client.println( "426 Transfer aborted" ); + #ifdef FTP_DEBUG + Serial.println( "Transfer aborted!") ; + #endif + } + transferStatus = 0; +} + +// Read a char from client connected to ftp server +// +// update cmdLine and command buffers, iCL and parameters pointers +// +// return: +// -2 if buffer cmdLine is full +// -1 if line not completed +// 0 if empty line received +// length of cmdLine (positive) if no empty line received + +int8_t FtpServer::readChar() +{ + int8_t rc = -1; + + if( client.available()) + { + char c = client.read(); + // char c; + // client.readBytes((uint8_t*) c, 1); + #ifdef FTP_DEBUG + Serial.print( c); + #endif + if( c == '\\' ) + c = '/'; + if( c != '\r' ) + if( c != '\n' ) + { + if( iCL < FTP_CMD_SIZE ) + cmdLine[ iCL ++ ] = c; + else + rc = -2; // Line too long + } + else + { + cmdLine[ iCL ] = 0; + command[ 0 ] = 0; + parameters = NULL; + // empty line? + if( iCL == 0 ) + rc = 0; + else + { + rc = iCL; + // search for space between command and parameters + parameters = strchr( cmdLine, ' ' ); + if( parameters != NULL ) + { + if( parameters - cmdLine > 4 ) + rc = -2; // Syntax error + else + { + strncpy( command, cmdLine, parameters - cmdLine ); + command[ parameters - cmdLine ] = 0; + + while( * ( ++ parameters ) == ' ' ) + ; + } + } + else if( strlen( cmdLine ) > 4 ) + rc = -2; // Syntax error. + else + strcpy( command, cmdLine ); + iCL = 0; + } + } + if( rc > 0 ) + for( uint8_t i = 0 ; i < strlen( command ); i ++ ) + command[ i ] = toupper( command[ i ] ); + if( rc == -2 ) + { + iCL = 0; + client.println( "500 Syntax error"); + } + } + return rc; +} + +// Make complete path/name from cwdName and parameters +// +// 3 possible cases: parameters can be absolute path, relative path or only the name +// +// parameters: +// fullName : where to store the path/name +// +// return: +// true, if done + +boolean FtpServer::makePath( char * fullName ) +{ + return makePath( fullName, parameters ); +} + +boolean FtpServer::makePath( char * fullName, char * param ) +{ + if( param == NULL ) + param = parameters; + + // Root or empty? + if( strcmp( param, "/" ) == 0 || strlen( param ) == 0 ) + { + strcpy( fullName, "/" ); + return true; + } + // If relative path, concatenate with current dir + if( param[0] != '/' ) + { + strcpy( fullName, cwdName ); + if( fullName[ strlen( fullName ) - 1 ] != '/' ) + strncat( fullName, "/", FTP_CWD_SIZE ); + strncat( fullName, param, FTP_CWD_SIZE ); + } + else + strcpy( fullName, param ); + // If ends with '/', remove it + uint16_t strl = strlen( fullName ) - 1; + if( fullName[ strl ] == '/' && strl > 1 ) + fullName[ strl ] = 0; + if( strlen( fullName ) < FTP_CWD_SIZE ) + return true; + + client.println( "500 Command line too long"); + return false; +} + +// Calculate year, month, day, hour, minute and second +// from first parameter sent by MDTM command (YYYYMMDDHHMMSS) +// +// parameters: +// pyear, pmonth, pday, phour, pminute and psecond: pointer of +// variables where to store data +// +// return: +// 0 if parameter is not YYYYMMDDHHMMSS +// length of parameter + space + +uint8_t FtpServer::getDateTime( uint16_t * pyear, uint8_t * pmonth, uint8_t * pday, + uint8_t * phour, uint8_t * pminute, uint8_t * psecond ) +{ + char dt[ 15 ]; + + // Date/time are expressed as a 14 digits long string + // terminated by a space and followed by name of file + if( strlen( parameters ) < 15 || parameters[ 14 ] != ' ' ) + return 0; + for( uint8_t i = 0; i < 14; i++ ) + if( ! isdigit( parameters[ i ])) + return 0; + + strncpy( dt, parameters, 14 ); + dt[ 14 ] = 0; + * psecond = atoi( dt + 12 ); + dt[ 12 ] = 0; + * pminute = atoi( dt + 10 ); + dt[ 10 ] = 0; + * phour = atoi( dt + 8 ); + dt[ 8 ] = 0; + * pday = atoi( dt + 6 ); + dt[ 6 ] = 0 ; + * pmonth = atoi( dt + 4 ); + dt[ 4 ] = 0 ; + * pyear = atoi( dt ); + return 15; +} + +// Create string YYYYMMDDHHMMSS from date and time +// +// parameters: +// date, time +// tstr: where to store the string. Must be at least 15 characters long +// +// return: +// pointer to tstr + +char * FtpServer::makeDateTimeStr( char * tstr, uint16_t date, uint16_t time ) +{ + sprintf( tstr, "%04u%02u%02u%02u%02u%02u", + (( date & 0xFE00 ) >> 9 ) + 1980, ( date & 0x01E0 ) >> 5, date & 0x001F, + ( time & 0xF800 ) >> 11, ( time & 0x07E0 ) >> 5, ( time & 0x001F ) << 1 ); + return tstr; +} + diff --git a/.piolibdeps/ESP8266FtpServer_ID992/ESP8266FtpServer.h b/.piolibdeps/ESP8266FtpServer_ID992/ESP8266FtpServer.h new file mode 100644 index 0000000..00a8057 --- /dev/null +++ b/.piolibdeps/ESP8266FtpServer_ID992/ESP8266FtpServer.h @@ -0,0 +1,106 @@ + +/* +* FTP SERVER FOR ESP8266 + * based on FTP Serveur for Arduino Due and Ethernet shield (W5100) or WIZ820io (W5200) + * based on Jean-Michel Gallego's work + * modified to work with esp8266 SPIFFS by David Paiva (david@nailbuster.com) + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 program. If not, see . + */ + +/******************************************************************************* + ** ** + ** DEFINITIONS FOR FTP SERVER ** + ** ** + *******************************************************************************/ + +// Uncomment to print debugging info to console attached to ESP8266 +//#define FTP_DEBUG + +#ifndef FTP_SERVERESP_H +#define FTP_SERVERESP_H + +//#include "Streaming.h" +#include +#include + +#define FTP_SERVER_VERSION "FTP-2017-10-18" + +#define FTP_CTRL_PORT 21 // Command port on wich server is listening +#define FTP_DATA_PORT_PASV 50009 // Data port in passive mode + +#define FTP_TIME_OUT 5 // Disconnect client after 5 minutes of inactivity +#define FTP_CMD_SIZE 255 + 8 // max size of a command +#define FTP_CWD_SIZE 255 + 8 // max size of a directory name +#define FTP_FIL_SIZE 255 // max size of a file name +//#define FTP_BUF_SIZE 1024 //512 // size of file buffer for read/write +#define FTP_BUF_SIZE 2*1460 //512 // size of file buffer for read/write + +class FtpServer +{ +public: + void begin(String uname, String pword); + void handleFTP(); + +private: + void iniVariables(); + void clientConnected(); + void disconnectClient(); + boolean userIdentity(); + boolean userPassword(); + boolean processCommand(); + boolean dataConnect(); + boolean doRetrieve(); + boolean doStore(); + void closeTransfer(); + void abortTransfer(); + boolean makePath( char * fullname ); + boolean makePath( char * fullName, char * param ); + uint8_t getDateTime( uint16_t * pyear, uint8_t * pmonth, uint8_t * pday, + uint8_t * phour, uint8_t * pminute, uint8_t * second ); + char * makeDateTimeStr( char * tstr, uint16_t date, uint16_t time ); + int8_t readChar(); + + IPAddress dataIp; // IP address of client for data + WiFiClient client; + WiFiClient data; + + File file; + + boolean dataPassiveConn; + uint16_t dataPort; + char buf[ FTP_BUF_SIZE ]; // data buffer for transfers + char cmdLine[ FTP_CMD_SIZE ]; // where to store incoming char from client + char cwdName[ FTP_CWD_SIZE ]; // name of current directory + char command[ 5 ]; // command sent by client + boolean rnfrCmd; // previous command was RNFR + char * parameters; // point to begin of parameters sent by client + uint16_t iCL; // pointer to cmdLine next incoming char + int8_t cmdStatus, // status of ftp command connexion + transferStatus; // status of ftp data transfer + uint32_t millisTimeOut, // disconnect after 5 min of inactivity + millisDelay, + millisEndConnection, // + millisBeginTrans, // store time of beginning of a transaction + bytesTransfered; // + String _FTP_USER; + String _FTP_PASS; + + + +}; + +#endif // FTP_SERVERESP_H + + diff --git a/.piolibdeps/ESP8266FtpServer_ID992/LICENSE b/.piolibdeps/ESP8266FtpServer_ID992/LICENSE new file mode 100644 index 0000000..19e3071 --- /dev/null +++ b/.piolibdeps/ESP8266FtpServer_ID992/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +(This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.) + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + {signature of Ty Coon}, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/.piolibdeps/ESP8266FtpServer_ID992/README.md b/.piolibdeps/ESP8266FtpServer_ID992/README.md new file mode 100644 index 0000000..dd9bc01 --- /dev/null +++ b/.piolibdeps/ESP8266FtpServer_ID992/README.md @@ -0,0 +1,24 @@ +# esp8266FTPServer +Simple FTP Server for using esp8266/esp32 SPIFFs + +Now should support esp32!!! + +I've modified a FTP server from arduino/wifi shield to work with esp8266.... + +This allows you to FTP into your esp8266 and access/modify the spiffs folder/data...it only allows one ftp connection at a time....very simple for now... + +I've tested it with Filezilla, and the basics work (upload/download/rename/delete). There's no create/modify directory support(no directory support in SPIFFS yet). + +You need to setup Filezilla(or other client) to only allow 1 connection.. +To force FileZilla to use the primary connection for data transfers: +Go to File/Site Manager then select your site. +In Transfer Settings, check "Limit number of simultaneous connections" and set the maximum to 1 + +only supports Passive ftp mode.... + +It does NOT support any encryption, so you'll have to disable any form of encryption... + +feel free to try it out (sample provided)....unzip into your arduino library directory (and restart arduino ide). + + +this is the original project on github I worked from: https://github.com/gallegojm/Arduino-Ftp-Server/tree/master/FtpServer diff --git a/.piolibdeps/ESP8266FtpServer_ID992/examples/FTPServerSample/FTPServerSample.ino b/.piolibdeps/ESP8266FtpServer_ID992/examples/FTPServerSample/FTPServerSample.ino new file mode 100644 index 0000000..bb5e7ac --- /dev/null +++ b/.piolibdeps/ESP8266FtpServer_ID992/examples/FTPServerSample/FTPServerSample.ino @@ -0,0 +1,50 @@ +#ifdef ESP8266 +#include +#elif defined ESP32 +#include +#include "SPIFFS.h" +#endif + +#include + +const char* ssid = "YOUR_SSID"; +const char* password = "YOUR_PASS"; + + +FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial + + +void setup(void){ + Serial.begin(115200); + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + + /////FTP Setup, ensure SPIFFS is started before ftp; ///////// + + /////FTP Setup, ensure SPIFFS is started before ftp; ///////// +#ifdef ESP32 //esp32 we send true to format spiffs if cannot mount + if (SPIFFS.begin(true)) { +#elif defined ESP8266 + if (SPIFFS.begin()) { +#endif + Serial.println("SPIFFS opened!"); + ftpSrv.begin("esp8266","esp8266"); //username, password for ftp. set ports in ESP8266FtpServer.h (default 21, 50009 for PASV) + } +} +void loop(void){ + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! + // server.handleClient(); //example if running a webserver you still need to call .handleClient(); + +} diff --git a/.piolibdeps/ESP8266FtpServer_ID992/library.json b/.piolibdeps/ESP8266FtpServer_ID992/library.json new file mode 100644 index 0000000..ff9891f --- /dev/null +++ b/.piolibdeps/ESP8266FtpServer_ID992/library.json @@ -0,0 +1,19 @@ +{ + "name": "esp8266FTPServer", + "description": "Simple FTP Server for using esp8266 SPIFFs", + "keywords": "esp8266, ftp, spiffs", + "authors": + { + "name": "David Paiva", + "email": "david@paivahome.com", + "url": "http://nailbuster.com/" + }, + "repository": + { + "type": "git", + "url": "https://github.com/nailbuster/esp8266FTPServer" + }, + "url": "http://nailbuster.com/", + "frameworks": "Arduino", + "platforms": "*" +} diff --git a/.piolibdeps/ESP8266FtpServer_ID992/library.properties b/.piolibdeps/ESP8266FtpServer_ID992/library.properties new file mode 100644 index 0000000..0e89838 --- /dev/null +++ b/.piolibdeps/ESP8266FtpServer_ID992/library.properties @@ -0,0 +1,9 @@ +name=ESP8266FtpServer +version=1.0.1 +author= +maintainer=david@paivahome.com +sentence=Very Simple FTP server for SPIFFS on esp8266 +paragraph=Very Simple FTP server for SPIFFS on esp8266 +category=Communication +url= +architectures=esp8266,esp32,arduino-esp32 diff --git a/.piolibdeps/ESPAsyncTCP_ID305/.gitignore b/.piolibdeps/ESPAsyncTCP_ID305/.gitignore new file mode 100644 index 0000000..9bea433 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/.gitignore @@ -0,0 +1,2 @@ + +.DS_Store diff --git a/.piolibdeps/ESPAsyncTCP_ID305/.library.json b/.piolibdeps/ESPAsyncTCP_ID305/.library.json new file mode 100644 index 0000000..80f0d3e --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/.library.json @@ -0,0 +1,32 @@ +{ + "license": "LGPL-3.0", + "name": "ESPAsyncTCP", + "repository": { + "url": "https://github.com/me-no-dev/ESPAsyncTCP.git", + "type": "git" + }, + "platforms": [ + "espressif8266" + ], + "frameworks": [ + "arduino" + ], + "version": "1.2.0", + "build": { + "libCompatMode": 2 + }, + "authors": [ + { + "url": null, + "maintainer": true, + "email": null, + "name": "Hristo Gochkov" + } + ], + "keywords": [ + "async", + "tcp" + ], + "id": 305, + "description": "Asynchronous TCP Library for ESP8266" +} \ No newline at end of file diff --git a/.piolibdeps/ESPAsyncTCP_ID305/.travis.yml b/.piolibdeps/ESPAsyncTCP_ID305/.travis.yml new file mode 100644 index 0000000..8980880 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/.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/.piolibdeps/ESPAsyncTCP_ID305/LICENSE.txt b/.piolibdeps/ESPAsyncTCP_ID305/LICENSE.txt new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/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/.piolibdeps/ESPAsyncTCP_ID305/README.md b/.piolibdeps/ESPAsyncTCP_ID305/README.md new file mode 100644 index 0000000..2c48453 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/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/.piolibdeps/ESPAsyncTCP_ID305/examples/ClientServer/Client/Client.ino b/.piolibdeps/ESPAsyncTCP_ID305/examples/ClientServer/Client/Client.ino new file mode 100644 index 0000000..b30d791 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/examples/ClientServer/Client/Client.ino @@ -0,0 +1,62 @@ +#include +#include + +extern "C" { +#include +#include +} + +#include "config.h" + +static os_timer_t intervalTimer; + +static void replyToServer(void* arg) { + AsyncClient* client = reinterpret_cast(arg); + + // send reply + if (client->space() > 32 && client->canSend()) { + char message[32]; + sprintf(message, "this is from %s", WiFi.localIP().toString().c_str()); + client->add(message, strlen(message)); + client->send(); + } +} + +/* event callbacks */ +static void handleData(void* arg, AsyncClient* client, void *data, size_t len) { + Serial.printf("\n data received from %s \n", client->remoteIP().toString().c_str()); + Serial.write((uint8_t*)data, len); + + os_timer_arm(&intervalTimer, 2000, true); // schedule for reply to server at next 2s +} + +void onConnect(void* arg, AsyncClient* client) { + Serial.printf("\n client has been connected to %s on port %d \n", SERVER_HOST_NAME, TCP_PORT); + replyToServer(client); +} + + +void setup() { + Serial.begin(115200); + delay(20); + + // connects to access point + WiFi.mode(WIFI_STA); + WiFi.begin(SSID, PASSWORD); + while (WiFi.status() != WL_CONNECTED) { + Serial.print('.'); + delay(500); + } + + AsyncClient* client = new AsyncClient; + client->onData(&handleData, client); + client->onConnect(&onConnect, client); + client->connect(SERVER_HOST_NAME, TCP_PORT); + + os_timer_disarm(&intervalTimer); + os_timer_setfn(&intervalTimer, &replyToServer, client); +} + +void loop() { + +} diff --git a/.piolibdeps/ESPAsyncTCP_ID305/examples/ClientServer/Client/config.h b/.piolibdeps/ESPAsyncTCP_ID305/examples/ClientServer/Client/config.h new file mode 100644 index 0000000..cf51e91 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/examples/ClientServer/Client/config.h @@ -0,0 +1,23 @@ +#ifndef CONFIG_H +#define CONFIG_H + +/* + * This example demonstrate how to use asynchronous client & server APIs + * in order to establish tcp socket connections in client server manner. + * server is running (on port 7050) on one ESP, acts as AP, and other clients running on + * remaining ESPs acts as STAs. after connection establishment between server and clients + * there is a simple message transfer in every 2s. clients connect to server via it's host name + * (in this case 'esp_server') with help of DNS service running on server side. + * + * Note: default MSS for ESPAsyncTCP is 536 byte and defualt ACK timeout is 5s. +*/ + +#define SSID "ESP-TEST" +#define PASSWORD "123456789" + +#define SERVER_HOST_NAME "esp_server" + +#define TCP_PORT 7050 +#define DNS_PORT 53 + +#endif // CONFIG_H diff --git a/.piolibdeps/ESPAsyncTCP_ID305/examples/ClientServer/Server/Server.ino b/.piolibdeps/ESPAsyncTCP_ID305/examples/ClientServer/Server/Server.ino new file mode 100644 index 0000000..c8c9b7f --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/examples/ClientServer/Server/Server.ino @@ -0,0 +1,73 @@ +#include +#include +#include +#include + +#include "config.h" + +static DNSServer DNS; + +static std::vector clients; // a list to hold all clients + + /* clients events */ +static void handleError(void* arg, AsyncClient* client, int8_t error) { + Serial.printf("\n connection error %s from client %s \n", client->errorToString(error), client->remoteIP().toString().c_str()); +} + +static void handleData(void* arg, AsyncClient* client, void *data, size_t len) { + Serial.printf("\n data received from client %s \n", client->remoteIP().toString().c_str()); + Serial.write((uint8_t*)data, len); + + // reply to client + if (client->space() > 32 && client->canSend()) { + char reply[32]; + sprintf(reply, "this is from %s", SERVER_HOST_NAME); + client->add(reply, strlen(reply)); + client->send(); + } +} + +static void handleDisconnect(void* arg, AsyncClient* client) { + Serial.printf("\n client %s disconnected \n", client->remoteIP().toString().c_str()); +} + +static void handleTimeOut(void* arg, AsyncClient* client, uint32_t time) { + Serial.printf("\n client ACK timeout ip: %s \n", client->remoteIP().toString().c_str()); +} + + +/* server events */ +static void handleNewClient(void* arg, AsyncClient* client) { + Serial.printf("\n new client has been connected to server, ip: %s", client->remoteIP().toString().c_str()); + + // add to list + clients.push_back(client); + + // register events + client->onData(&handleData, NULL); + client->onError(&handleError, NULL); + client->onDisconnect(&handleDisconnect, NULL); + client->onTimeout(&handleTimeOut, NULL); +} + +void setup() { + Serial.begin(115200); + delay(20); + + // create access point + while (!WiFi.softAP(SSID, PASSWORD, 6, false, 15)) { + delay(500); + } + + // start dns server + if (!DNS.start(DNS_PORT, SERVER_HOST_NAME, WiFi.softAPIP())) + Serial.printf("\n failed to start dns service \n"); + + AsyncServer* server = new AsyncServer(TCP_PORT); // start listening on tcp port 7050 + server->onClient(&handleNewClient, server); + server->begin(); +} + +void loop() { + DNS.processNextRequest(); +} diff --git a/.piolibdeps/ESPAsyncTCP_ID305/examples/ClientServer/Server/config.h b/.piolibdeps/ESPAsyncTCP_ID305/examples/ClientServer/Server/config.h new file mode 100644 index 0000000..cf51e91 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/examples/ClientServer/Server/config.h @@ -0,0 +1,23 @@ +#ifndef CONFIG_H +#define CONFIG_H + +/* + * This example demonstrate how to use asynchronous client & server APIs + * in order to establish tcp socket connections in client server manner. + * server is running (on port 7050) on one ESP, acts as AP, and other clients running on + * remaining ESPs acts as STAs. after connection establishment between server and clients + * there is a simple message transfer in every 2s. clients connect to server via it's host name + * (in this case 'esp_server') with help of DNS service running on server side. + * + * Note: default MSS for ESPAsyncTCP is 536 byte and defualt ACK timeout is 5s. +*/ + +#define SSID "ESP-TEST" +#define PASSWORD "123456789" + +#define SERVER_HOST_NAME "esp_server" + +#define TCP_PORT 7050 +#define DNS_PORT 53 + +#endif // CONFIG_H diff --git a/.piolibdeps/ESPAsyncTCP_ID305/examples/SyncClient/.esp31b.skip b/.piolibdeps/ESPAsyncTCP_ID305/examples/SyncClient/.esp31b.skip new file mode 100644 index 0000000..e69de29 diff --git a/.piolibdeps/ESPAsyncTCP_ID305/examples/SyncClient/SyncClient.ino b/.piolibdeps/ESPAsyncTCP_ID305/examples/SyncClient/SyncClient.ino new file mode 100644 index 0000000..6ecc525 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/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/.piolibdeps/ESPAsyncTCP_ID305/library.json b/.piolibdeps/ESPAsyncTCP_ID305/library.json new file mode 100644 index 0000000..8f39000 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/library.json @@ -0,0 +1,22 @@ +{ + "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.2.0", + "license": "LGPL-3.0", + "frameworks": "arduino", + "platforms": "espressif8266", + "build": { + "libCompatMode": 2 + } +} diff --git a/.piolibdeps/ESPAsyncTCP_ID305/library.properties b/.piolibdeps/ESPAsyncTCP_ID305/library.properties new file mode 100644 index 0000000..d8e1f9c --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/library.properties @@ -0,0 +1,9 @@ +name=ESP AsyncTCP +version=1.2.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/.piolibdeps/ESPAsyncTCP_ID305/src/AsyncPrinter.cpp b/.piolibdeps/ESPAsyncTCP_ID305/src/AsyncPrinter.cpp new file mode 100644 index 0000000..dd61e76 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/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/.piolibdeps/ESPAsyncTCP_ID305/src/AsyncPrinter.h b/.piolibdeps/ESPAsyncTCP_ID305/src/AsyncPrinter.h new file mode 100644 index 0000000..1fa0f8b --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/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/.piolibdeps/ESPAsyncTCP_ID305/src/ESPAsyncTCP.cpp b/.piolibdeps/ESPAsyncTCP_ID305/src/ESPAsyncTCP.cpp new file mode 100644 index 0000000..7c3fd97 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/src/ESPAsyncTCP.cpp @@ -0,0 +1,1105 @@ +/* + 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) + , _pb_cb(0) + , _pb_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){ + if(_pcb) + 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 + +err_t AsyncClient::_connected(void* pcb, err_t 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(err_t 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 + +err_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) { +#if ASYNC_TCP_SSL_ENABLED + if (_pcb_secure && !_handshake_done) + return ERR_OK; +#endif + _rx_last_packet = millis(); + _tx_unacked_len -= len; + _tx_acked_len += len; + ASYNC_TCP_DEBUG("_sent: %u (%d %d)\n", len, _tx_unacked_len, _tx_acked_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; +} + +err_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, err_t 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; + pb = b->next; + b->next = NULL; + ASYNC_TCP_DEBUG("_recv: %d\n", b->len); + if(_pb_cb){ + _pb_cb(_pb_cb_arg, this, b); + } else { + 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); + pbuf_free(b); + } + } + return ERR_OK; +} + +err_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; + 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); +} + +err_t AsyncClient::_s_poll(void *arg, struct tcp_pcb *tpcb) { + return reinterpret_cast(arg)->_poll(tpcb); +} + +err_t AsyncClient::_s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, err_t err) { + return reinterpret_cast(arg)->_recv(tpcb, pb, err); +} + +void AsyncClient::_s_error(void *arg, err_t err) { + reinterpret_cast(arg)->_error(err); +} + +err_t AsyncClient::_s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len) { + return reinterpret_cast(arg)->_sent(tpcb, len); +} + +err_t AsyncClient::_s_connected(void* arg, void* tpcb, err_t 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::onPacket(AcPacketHandler cb, void* arg){ + _pb_cb = cb; + _pb_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; +} + +void AsyncClient::ackPacket(struct pbuf * pb){ + if(!pb){ + return; + } + tcp_recved(_pcb, pb->len); + pbuf_free(pb); +} + +const char * AsyncClient::errorToString(int8_t error) { + switch (error) { + case ERR_OK: + return "OK"; + case ERR_MEM: + return "Out of memory error"; + case ERR_BUF: + return "Buffer error"; + case ERR_TIMEOUT: + return "Timeout"; + case ERR_RTE: + return "Routing problem"; + case ERR_INPROGRESS: + return "Operation in progress"; + case ERR_VAL: + return "Illegal value"; + case ERR_WOULDBLOCK: + return "Operation would block"; + case ERR_ABRT: + return "Connection aborted"; + case ERR_RST: + return "Connection reset"; + case ERR_CLSD: + return "Connection closed"; + case ERR_CONN: + return "Not connected"; + case ERR_ARG: + return "Illegal argument"; + case ERR_USE: + return "Address in use"; + case ERR_IF: + return "Low-level netif error"; + case ERR_ISCONN: + return "Connection already established"; + default: + return "Unknown error"; + } +} + +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_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; +} + +err_t AsyncServer::_accept(tcp_pcb* pcb, err_t 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; +} + + err_t AsyncServer::_s_accept(void *arg, tcp_pcb* pcb, err_t err){ + return reinterpret_cast(arg)->_accept(pcb, err); + } + +#if ASYNC_TCP_SSL_ENABLED +err_t 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; +} + +err_t AsyncServer::_recv(struct tcp_pcb *pcb, struct pbuf *pb, err_t 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); +} + +err_t AsyncServer::_s_poll(void *arg, struct tcp_pcb *pcb){ + return reinterpret_cast(arg)->_poll(pcb); +} + +err_t AsyncServer::_s_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, err_t err){ + return reinterpret_cast(arg)->_recv(pcb, pb, err); +} +#endif diff --git a/.piolibdeps/ESPAsyncTCP_ID305/src/ESPAsyncTCP.h b/.piolibdeps/ESPAsyncTCP_ID305/src/ESPAsyncTCP.h new file mode 100644 index 0000000..c1803b4 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/src/ESPAsyncTCP.h @@ -0,0 +1,259 @@ +/* + 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 + +extern "C" { + #include "lwip/init.h" + #include "lwip/err.h" + #include "lwip/pbuf.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. + +struct tcp_pcb; +struct ip_addr; +#if ASYNC_TCP_SSL_ENABLED +struct SSL_; +typedef struct SSL_ SSL; +struct SSL_CTX_; +typedef struct SSL_CTX_ SSL_CTX; +#endif + +typedef std::function AcConnectHandler; +typedef std::function AcAckHandler; +typedef std::function AcErrorHandler; +typedef std::function AcDataHandler; +typedef std::function AcPacketHandler; +typedef std::function AcTimeoutHandler; + +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; + AcPacketHandler _pb_cb; + void* _pb_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(); + err_t _connected(void* pcb, err_t err); + void _error(err_t err); +#if ASYNC_TCP_SSL_ENABLED + void _ssl_error(int8_t err); +#endif + err_t _poll(tcp_pcb* pcb); + err_t _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 err_t _s_poll(void *arg, struct tcp_pcb *tpcb); + static err_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, err_t err); + static void _s_error(void *arg, err_t err); + static err_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len); + static err_t _s_connected(void* arg, void* tpcb, err_t 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 (called if onPacket is not used) + void onPacket(AcPacketHandler 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 + + void ackPacket(struct pbuf * pb); + + const char * errorToString(int8_t error); + const char * stateToString(); + + err_t _recv(tcp_pcb* pcb, pbuf* pb, err_t 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: + err_t _accept(tcp_pcb* newpcb, err_t err); + static err_t _s_accept(void *arg, tcp_pcb* newpcb, err_t err); +#if ASYNC_TCP_SSL_ENABLED + int _cert(const char *filename, uint8_t **buf); + err_t _poll(tcp_pcb* pcb); + err_t _recv(tcp_pcb *pcb, struct pbuf *pb, err_t err); + static int _s_cert(void *arg, const char *filename, uint8_t **buf); + static err_t _s_poll(void *arg, struct tcp_pcb *tpcb); + static err_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, err_t err); +#endif +}; + + +#endif /* ASYNCTCP_H_ */ diff --git a/.piolibdeps/ESPAsyncTCP_ID305/src/ESPAsyncTCPbuffer.cpp b/.piolibdeps/ESPAsyncTCP_ID305/src/ESPAsyncTCPbuffer.cpp new file mode 100644 index 0000000..7bca134 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/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/.piolibdeps/ESPAsyncTCP_ID305/src/ESPAsyncTCPbuffer.h b/.piolibdeps/ESPAsyncTCP_ID305/src/ESPAsyncTCPbuffer.h new file mode 100644 index 0000000..374364e --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/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/.piolibdeps/ESPAsyncTCP_ID305/src/SyncClient.cpp b/.piolibdeps/ESPAsyncTCP_ID305/src/SyncClient.cpp new file mode 100644 index 0000000..df9489f --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/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/.piolibdeps/ESPAsyncTCP_ID305/src/SyncClient.h b/.piolibdeps/ESPAsyncTCP_ID305/src/SyncClient.h new file mode 100644 index 0000000..e46f4e4 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/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/.piolibdeps/ESPAsyncTCP_ID305/src/async_config.h b/.piolibdeps/ESPAsyncTCP_ID305/src/async_config.h new file mode 100644 index 0000000..61cfc25 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/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/.piolibdeps/ESPAsyncTCP_ID305/src/tcp_axtls.c b/.piolibdeps/ESPAsyncTCP_ID305/src/tcp_axtls.c new file mode 100644 index 0000000..1e2f324 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/src/tcp_axtls.c @@ -0,0 +1,587 @@ +/* + 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); + fd_data->tcp_pbuf = NULL; + 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/.piolibdeps/ESPAsyncTCP_ID305/src/tcp_axtls.h b/.piolibdeps/ESPAsyncTCP_ID305/src/tcp_axtls.h new file mode 100644 index 0000000..118e36f --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/src/tcp_axtls.h @@ -0,0 +1,98 @@ +/* + 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 "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/.piolibdeps/ESPAsyncTCP_ID305/ssl/gen_server_cert.sh b/.piolibdeps/ESPAsyncTCP_ID305/ssl/gen_server_cert.sh new file mode 100644 index 0000000..fd749ed --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/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/.piolibdeps/ESPAsyncTCP_ID305/ssl/server.cer b/.piolibdeps/ESPAsyncTCP_ID305/ssl/server.cer new file mode 100644 index 0000000..b5e5f24 Binary files /dev/null and b/.piolibdeps/ESPAsyncTCP_ID305/ssl/server.cer differ diff --git a/.piolibdeps/ESPAsyncTCP_ID305/ssl/server.key b/.piolibdeps/ESPAsyncTCP_ID305/ssl/server.key new file mode 100644 index 0000000..1b7095f Binary files /dev/null and b/.piolibdeps/ESPAsyncTCP_ID305/ssl/server.key differ diff --git a/.piolibdeps/ESPAsyncTCP_ID305/travis/common.sh b/.piolibdeps/ESPAsyncTCP_ID305/travis/common.sh new file mode 100644 index 0000000..57bede3 --- /dev/null +++ b/.piolibdeps/ESPAsyncTCP_ID305/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/.piolibdeps/ESPAsyncWifiManager_ID1438/.library.json b/.piolibdeps/ESPAsyncWifiManager_ID1438/.library.json new file mode 100644 index 0000000..d0296e7 --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/.library.json @@ -0,0 +1,29 @@ +{ + "name": "ESPAsyncWifiManager", + "repository": { + "url": "https://github.com/alanswx/ESPAsyncWiFiManager.git", + "type": "git" + }, + "platforms": [ + "espressif32", + "espressif8266" + ], + "frameworks": [ + "arduino" + ], + "version": "0.16", + "authors": [ + { + "maintainer": false, + "name": "Alan Steremberg", + "url": "https://github.com/alanswx", + "email": null + } + ], + "keywords": [ + "wifi", + "wi-fi" + ], + "id": 1438, + "description": "ESP8266 Async WiFi Connection manager with fallback web configuration portal" +} \ No newline at end of file diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/ESPAsyncWiFiManager.cpp b/.piolibdeps/ESPAsyncWifiManager_ID1438/ESPAsyncWiFiManager.cpp new file mode 100644 index 0000000..00ae269 --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/ESPAsyncWiFiManager.cpp @@ -0,0 +1,1072 @@ +/************************************************************** + AsyncWiFiManager is a library for the ESP8266/Arduino platform + (https://github.com/esp8266/Arduino) to enable easy + configuration and reconfiguration of WiFi credentials using a Captive Portal + inspired by: + http://www.esp8266.com/viewtopic.php?f=29&t=2520 + https://github.com/chriscook8/esp-arduino-apboot + https://github.com/esp8266/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/DNSServer/examples/CaptivePortalAdvanced + Built by AlexT https://github.com/tzapu + Ported to Async Web Server by https://github.com/alanswx + Licensed under MIT license + **************************************************************/ + +#include "ESPAsyncWiFiManager.h" + +AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *custom) { + _id = NULL; + _placeholder = NULL; + _length = 0; + _value = NULL; + + _customHTML = custom; +} + +AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length) { + init(id, placeholder, defaultValue, length, ""); +} + +AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom) { + init(id, placeholder, defaultValue, length, custom); +} + +void AsyncWiFiManagerParameter::init(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom) { + _id = id; + _placeholder = placeholder; + _length = length; + _value = new char[length + 1]; + for (int i = 0; i < length; i++) { + _value[i] = 0; + } + if (defaultValue != NULL) { + strncpy(_value, defaultValue, length); + } + + _customHTML = custom; +} + +const char* AsyncWiFiManagerParameter::getValue() { + return _value; +} +const char* AsyncWiFiManagerParameter::getID() { + return _id; +} +const char* AsyncWiFiManagerParameter::getPlaceholder() { + return _placeholder; +} +int AsyncWiFiManagerParameter::getValueLength() { + return _length; +} +const char* AsyncWiFiManagerParameter::getCustomHTML() { + return _customHTML; +} + +#ifdef USE_EADNS +AsyncWiFiManager::AsyncWiFiManager(AsyncWebServer *server, AsyncDNSServer *dns) :server(server), dnsServer(dns) { +#else +AsyncWiFiManager::AsyncWiFiManager(AsyncWebServer *server, DNSServer *dns) :server(server), dnsServer(dns) { +#endif + wifiSSIDs = NULL; + wifiSSIDscan=true; + _modeless=false; + shouldscan=true; +} + +void AsyncWiFiManager::addParameter(AsyncWiFiManagerParameter *p) { + _params[_paramsCount] = p; + _paramsCount++; + DEBUG_WM("Adding parameter"); + DEBUG_WM(p->getID()); +} + +void AsyncWiFiManager::setupConfigPortal() { + // dnsServer.reset(new DNSServer()); + // server.reset(new ESP8266WebServer(80)); + server->reset(); + + DEBUG_WM(F("")); + _configPortalStart = millis(); + + DEBUG_WM(F("Configuring access point... ")); + DEBUG_WM(_apName); + if (_apPassword != NULL) { + if (strlen(_apPassword) < 8 || strlen(_apPassword) > 63) { + // fail passphrase to short or long! + DEBUG_WM(F("Invalid AccessPoint password. Ignoring")); + _apPassword = NULL; + } + DEBUG_WM(_apPassword); + } + + //optional soft ip config + if (_ap_static_ip) { + DEBUG_WM(F("Custom AP IP/GW/Subnet")); + WiFi.softAPConfig(_ap_static_ip, _ap_static_gw, _ap_static_sn); + } + + if (_apPassword != NULL) { + WiFi.softAP(_apName, _apPassword);//password option + } else { + WiFi.softAP(_apName); + } + + delay(500); // Without delay I've seen the IP address blank + DEBUG_WM(F("AP IP address: ")); + DEBUG_WM(WiFi.softAPIP()); + + /* Setup the DNS server redirecting all the domains to the apIP */ + dnsServer->setErrorReplyCode(DNSReplyCode::NoError); + dnsServer->start(DNS_PORT, "*", WiFi.softAPIP()); + + setInfo(); + + /* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */ + server->on("/", std::bind(&AsyncWiFiManager::handleRoot, this,std::placeholders::_1)).setFilter(ON_AP_FILTER); + server->on("/wifi", std::bind(&AsyncWiFiManager::handleWifi, this, std::placeholders::_1,true)).setFilter(ON_AP_FILTER); + server->on("/0wifi", std::bind(&AsyncWiFiManager::handleWifi, this,std::placeholders::_1, false)).setFilter(ON_AP_FILTER); + server->on("/wifisave", std::bind(&AsyncWiFiManager::handleWifiSave,this,std::placeholders::_1)).setFilter(ON_AP_FILTER); + server->on("/i", std::bind(&AsyncWiFiManager::handleInfo,this, std::placeholders::_1)).setFilter(ON_AP_FILTER); + server->on("/r", std::bind(&AsyncWiFiManager::handleReset, this,std::placeholders::_1)).setFilter(ON_AP_FILTER); + //server->on("/generate_204", std::bind(&AsyncWiFiManager::handle204, this)); //Android/Chrome OS captive portal check. + server->on("/fwlink", std::bind(&AsyncWiFiManager::handleRoot, this,std::placeholders::_1)).setFilter(ON_AP_FILTER); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler. + server->onNotFound (std::bind(&AsyncWiFiManager::handleNotFound,this,std::placeholders::_1)); + server->begin(); // Web server start + DEBUG_WM(F("HTTP server started")); + +} + +static const char HEX_CHAR_ARRAY[17] = "0123456789ABCDEF"; +/** +* convert char array (hex values) to readable string by seperator +* buf: buffer to convert +* length: data length +* strSeperator seperator between each hex value +* return: formated value as String +*/ +static String byteToHexString(uint8_t* buf, uint8_t length, String strSeperator="-") { + String dataString = ""; + for (uint8_t i = 0; i < length; i++) { + byte v = buf[i] / 16; + byte w = buf[i] % 16; + if (i>0) { + dataString += strSeperator; + } + dataString += String(HEX_CHAR_ARRAY[v]); + dataString += String(HEX_CHAR_ARRAY[w]); + } + dataString.toUpperCase(); + return dataString; +} // byteToHexString + +#if !defined(ESP8266) +String getESP32ChipID() { + uint64_t chipid; + chipid=ESP.getEfuseMac();//The chip ID is essentially its MAC address(length: 6 bytes). + int chipid_size = 6; + uint8_t chipid_arr[chipid_size]; + for (uint8_t i=0; i < chipid_size; i++) { + chipid_arr[i] = (chipid >> (8 * i)) & 0xff; + } + return byteToHexString(chipid_arr, chipid_size, ""); +} +#endif + +boolean AsyncWiFiManager::autoConnect() { + String ssid = "ESP"; + #if defined(ESP8266) + ssid += String(ESP.getChipId()); + #else + ssid += getESP32ChipID(); + #endif + return autoConnect(ssid.c_str(), NULL); +} + +boolean AsyncWiFiManager::autoConnect(char const *apName, char const *apPassword) { + DEBUG_WM(F("")); + DEBUG_WM(F("AutoConnect")); + + // read eeprom for ssid and pass + //String ssid = getSSID(); + //String pass = getPassword(); + + // attempt to connect; should it fail, fall back to AP + WiFi.mode(WIFI_STA); + + if (connectWifi("", "") == WL_CONNECTED) { + DEBUG_WM(F("IP Address:")); + DEBUG_WM(WiFi.localIP()); + //connected + return true; + } + + return startConfigPortal(apName, apPassword); +} + + +String AsyncWiFiManager::networkListAsString() +{ + String pager ; + //display networks in page + for (int i = 0; i < wifiSSIDCount; i++) { + if (wifiSSIDs[i].duplicate == true) continue; // skip dups + int quality = getRSSIasQuality(wifiSSIDs[i].RSSI); + + if (_minimumQuality == -1 || _minimumQuality < quality) { + String item = FPSTR(HTTP_ITEM); + String rssiQ; + rssiQ += quality; + item.replace("{v}", wifiSSIDs[i].SSID); + item.replace("{r}", rssiQ); +#if defined(ESP8266) + if (wifiSSIDs[i].encryptionType != ENC_TYPE_NONE) { +#else + if (wifiSSIDs[i].encryptionType != WIFI_AUTH_OPEN) { +#endif + item.replace("{i}", "l"); + } else { + item.replace("{i}", ""); + } + pager += item; + + } else { + DEBUG_WM(F("Skipping due to quality")); + } + + } + return pager; +} + +String AsyncWiFiManager::scanModal() +{ + shouldscan=true; + scan(); + String pager=networkListAsString(); + return pager; +} + +void AsyncWiFiManager::scan() +{ + if (!shouldscan) return; + DEBUG_WM(F("About to scan()")); + if (wifiSSIDscan) + { + delay(100); + } + + if (wifiSSIDscan) + { + int n = WiFi.scanNetworks(); + DEBUG_WM(F("Scan done")); + if (n == 0) { + DEBUG_WM(F("No networks found")); + // page += F("No networks found. Refresh to scan again."); + } else { + + + if (wifiSSIDscan) + { + /* WE SHOULD MOVE THIS IN PLACE ATOMICALLY */ + if (wifiSSIDs) delete [] wifiSSIDs; + wifiSSIDs = new WiFiResult[n]; + wifiSSIDCount = n; + + if (n>0) + shouldscan=false; + + for (int i=0;i wifiSSIDs[i].RSSI) { + std::swap(wifiSSIDs[i], wifiSSIDs[j]); + } + } + } + + + // remove duplicates ( must be RSSI sorted ) + if (_removeDuplicateAPs) { + String cssid; + for (int i = 0; i < n; i++) { + if (wifiSSIDs[i].duplicate == true) continue; + cssid = wifiSSIDs[i].SSID; + for (int j = i + 1; j < n; j++) { + if (cssid == wifiSSIDs[j].SSID) { + DEBUG_WM("DUP AP: " +wifiSSIDs[j].SSID); + wifiSSIDs[j].duplicate=true; // set dup aps to NULL + } + } + } + } + + } + } + } +} + + +void AsyncWiFiManager::startConfigPortalModeless(char const *apName, char const *apPassword) { + + _modeless =true; + _apName = apName; + _apPassword = apPassword; + + /* + AJS - do we want this? + + */ + + //setup AP + WiFi.mode(WIFI_AP_STA); + DEBUG_WM("SET AP STA"); + + // try to connect + if (connectWifi("", "") == WL_CONNECTED) { + DEBUG_WM(F("IP Address:")); + DEBUG_WM(WiFi.localIP()); + //connected + // call the callback! + _savecallback(); + + } + + + + //notify we entered AP mode + if ( _apcallback != NULL) { + _apcallback(this); + } + + connect = false; + setupConfigPortal(); + scannow= -1 ; + +} + +void AsyncWiFiManager::loop(){ + safeLoop(); + criticalLoop(); +} + +void AsyncWiFiManager::setInfo() { + if (needInfo) { + pager = infoAsString(); + wifiStatus = WiFi.status(); + needInfo = false; + } +} + +/** + * Anything that accesses WiFi, ESP or EEPROM goes here + */ +void AsyncWiFiManager::criticalLoop(){ + if (_modeless) + { + + if ( scannow==-1 || millis() > scannow + 60000) + { + + scan(); + scannow= millis() ; + } + if (connect) { + connect = false; + //delay(2000); + DEBUG_WM(F("Connecting to new AP")); + + // using user-provided _ssid, _pass in place of system-stored ssid and pass + if (connectWifi(_ssid, _pass) != WL_CONNECTED) { + DEBUG_WM(F("Failed to connect.")); + } else { + //connected + // alanswx - should we have a config to decide if we should shut down AP? + // WiFi.mode(WIFI_STA); + //notify that configuration has changed and any optional parameters should be saved + if ( _savecallback != NULL) { + //todo: check if any custom parameters actually exist, and check if they really changed maybe + _savecallback(); + } + + return; + } + + if (_shouldBreakAfterConfig) { + //flag set to exit after config after trying to connect + //notify that configuration has changed and any optional parameters should be saved + if ( _savecallback != NULL) { + //todo: check if any custom parameters actually exist, and check if they really changed maybe + _savecallback(); + } + } + } + } +} + +/* + * Anything that doesn't access WiFi, ESP or EEPROM can go here + */ +void AsyncWiFiManager::safeLoop(){ + #ifndef USE_EADNS + dnsServer->processNextRequest(); + #endif +} + +boolean AsyncWiFiManager::startConfigPortal(char const *apName, char const *apPassword) { + //setup AP + WiFi.mode(WIFI_AP_STA); + DEBUG_WM("SET AP STA"); + + _apName = apName; + _apPassword = apPassword; + + //notify we entered AP mode + if ( _apcallback != NULL) { + _apcallback(this); + } + + connect = false; + setupConfigPortal(); + scannow= -1 ; + while (_configPortalTimeout == 0 || millis() < _configPortalStart + _configPortalTimeout) { + //DNS + //dnsServer->processNextRequest(); + + // + // we should do a scan every so often here + // + if ( millis() > scannow + 10000) + { + DEBUG_WM(F("About to scan()")); + shouldscan=true; // since we are modal, we can scan every time + scan(); + scannow= millis() ; + } + + + if (connect) { + connect = false; + delay(2000); + DEBUG_WM(F("Connecting to new AP")); + + // using user-provided _ssid, _pass in place of system-stored ssid and pass + if (connectWifi(_ssid, _pass) != WL_CONNECTED) { + DEBUG_WM(F("Failed to connect.")); + } else { + //connected + WiFi.mode(WIFI_STA); + //notify that configuration has changed and any optional parameters should be saved + if ( _savecallback != NULL) { + //todo: check if any custom parameters actually exist, and check if they really changed maybe + _savecallback(); + } + break; + } + + if (_shouldBreakAfterConfig) { + //flag set to exit after config after trying to connect + //notify that configuration has changed and any optional parameters should be saved + if ( _savecallback != NULL) { + //todo: check if any custom parameters actually exist, and check if they really changed maybe + _savecallback(); + } + break; + } + } + yield(); + } + + server->reset(); + #ifdef USE_EADNS + *dnsServer=AsyncDNSServer(); + #else + *dnsServer=DNSServer(); + #endif + + return WiFi.status() == WL_CONNECTED; +} + + +int AsyncWiFiManager::connectWifi(String ssid, String pass) { + DEBUG_WM(F("Connecting as wifi client...")); + + // check if we've got static_ip settings, if we do, use those. + if (_sta_static_ip) { + DEBUG_WM(F("Custom STA IP/GW/Subnet")); + WiFi.config(_sta_static_ip, _sta_static_gw, _sta_static_sn); + DEBUG_WM(WiFi.localIP()); + } + //fix for auto connect racing issue + // if (WiFi.status() == WL_CONNECTED) { + // DEBUG_WM("Already connected. Bailing out."); + // return WL_CONNECTED; + // } + //check if we have ssid and pass and force those, if not, try with last saved values + if (ssid != "") { + #if defined(ESP8266) + //trying to fix connection in progress hanging + ETS_UART_INTR_DISABLE(); + wifi_station_disconnect(); + ETS_UART_INTR_ENABLE(); + #else + WiFi.disconnect(false); + #endif + + WiFi.begin(ssid.c_str(), pass.c_str()); + } else { + if (WiFi.SSID().length() > 0) { + DEBUG_WM("Using last saved values, should be faster"); +#if defined(ESP8266) + //trying to fix connection in progress hanging + ETS_UART_INTR_DISABLE(); + wifi_station_disconnect(); + ETS_UART_INTR_ENABLE(); +#else + WiFi.disconnect(false); +#endif + + WiFi.begin(); + } else { + DEBUG_WM("Try to connect with saved credentials"); + WiFi.begin(); + } + } + + int connRes = waitForConnectResult(); + DEBUG_WM ("Connection result: "); + DEBUG_WM ( connRes ); + //not connected, WPS enabled, no pass - first attempt + if (_tryWPS && connRes != WL_CONNECTED && pass == "") { + startWPS(); + //should be connected at the end of WPS + connRes = waitForConnectResult(); + } + + needInfo = true; + setInfo(); + return connRes; +} + +uint8_t AsyncWiFiManager::waitForConnectResult() { + if (_connectTimeout == 0) { + return WiFi.waitForConnectResult(); + } else { + DEBUG_WM (F("Waiting for connection result with time out")); + unsigned long start = millis(); + boolean keepConnecting = true; + uint8_t status; + while (keepConnecting) { + status = WiFi.status(); + if (millis() > start + _connectTimeout) { + keepConnecting = false; + DEBUG_WM (F("Connection timed out")); + } + if (status == WL_CONNECTED || status == WL_CONNECT_FAILED) { + keepConnecting = false; + } + delay(100); + } + return status; + } +} + +void AsyncWiFiManager::startWPS() { + DEBUG_WM("START WPS"); +#if defined(ESP8266) + WiFi.beginWPSConfig(); +#else + //esp_wps_config_t config = WPS_CONFIG_INIT_DEFAULT(ESP_WPS_MODE); + esp_wps_config_t config = {}; + config.wps_type = ESP_WPS_MODE; + config.crypto_funcs = &g_wifi_default_wps_crypto_funcs; + strcpy(config.factory_info.manufacturer,"ESPRESSIF"); + strcpy(config.factory_info.model_number, "ESP32"); + strcpy(config.factory_info.model_name, "ESPRESSIF IOT"); + strcpy(config.factory_info.device_name,"ESP STATION"); + + esp_wifi_wps_enable(&config); + esp_wifi_wps_start(0); +#endif + DEBUG_WM("END WPS"); + +} +/* +String AsyncWiFiManager::getSSID() { +if (_ssid == "") { +DEBUG_WM(F("Reading SSID")); +_ssid = WiFi.SSID(); +DEBUG_WM(F("SSID: ")); +DEBUG_WM(_ssid); +} +return _ssid; +} + +String AsyncWiFiManager::getPassword() { +if (_pass == "") { +DEBUG_WM(F("Reading Password")); +_pass = WiFi.psk(); +DEBUG_WM("Password: " + _pass); +//DEBUG_WM(_pass); +} +return _pass; +} +*/ +String AsyncWiFiManager::getConfigPortalSSID() { + return _apName; +} + +void AsyncWiFiManager::resetSettings() { + DEBUG_WM(F("settings invalidated")); + DEBUG_WM(F("THIS MAY CAUSE AP NOT TO START UP PROPERLY. YOU NEED TO COMMENT IT OUT AFTER ERASING THE DATA.")); + WiFi.disconnect(true); + //delay(200); +} +void AsyncWiFiManager::setTimeout(unsigned long seconds) { + setConfigPortalTimeout(seconds); +} + +void AsyncWiFiManager::setConfigPortalTimeout(unsigned long seconds) { + _configPortalTimeout = seconds * 1000; +} + +void AsyncWiFiManager::setConnectTimeout(unsigned long seconds) { + _connectTimeout = seconds * 1000; +} + +void AsyncWiFiManager::setDebugOutput(boolean debug) { + _debug = debug; +} + +void AsyncWiFiManager::setAPStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn) { + _ap_static_ip = ip; + _ap_static_gw = gw; + _ap_static_sn = sn; +} + +void AsyncWiFiManager::setSTAStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn) { + _sta_static_ip = ip; + _sta_static_gw = gw; + _sta_static_sn = sn; +} + +void AsyncWiFiManager::setMinimumSignalQuality(int quality) { + _minimumQuality = quality; +} + +void AsyncWiFiManager::setBreakAfterConfig(boolean shouldBreak) { + _shouldBreakAfterConfig = shouldBreak; +} + +/** Handle root or redirect to captive portal */ +void AsyncWiFiManager::handleRoot(AsyncWebServerRequest *request) { + // AJS - maybe we should set a scan when we get to the root??? + // and only scan on demand? timer + on demand? plus a link to make it happen? + shouldscan=true; + scannow= -1 ; + DEBUG_WM(F("Handle root")); + if (captivePortal(request)) { // If captive portal redirect instead of displaying the page. + return; + } + + String page = FPSTR(WFM_HTTP_HEAD); + page.replace("{v}", "Options"); + page += FPSTR(HTTP_SCRIPT); + page += FPSTR(HTTP_STYLE); + page += _customHeadElement; + page += FPSTR(HTTP_HEAD_END); + page += "

"; + page += _apName; + page += "

"; + page += F("

AsyncWiFiManager

"); + page += FPSTR(HTTP_PORTAL_OPTIONS); + page += FPSTR(HTTP_END); + + request->send(200, "text/html", page); + +} + +/** Wifi config page handler */ +void AsyncWiFiManager::handleWifi(AsyncWebServerRequest *request,boolean scan) { + shouldscan=true; + scannow= -1 ; + + String page = FPSTR(WFM_HTTP_HEAD); + page.replace("{v}", "Config ESP"); + page += FPSTR(HTTP_SCRIPT); + page += FPSTR(HTTP_STYLE); + page += _customHeadElement; + page += FPSTR(HTTP_HEAD_END); + + if (scan) { + wifiSSIDscan=false; + + + + DEBUG_WM(F("Scan done")); + if (wifiSSIDCount==0) { + DEBUG_WM(F("No networks found")); + page += F("No networks found. Refresh to scan again."); + } else { + + + //display networks in page + String pager = networkListAsString(); + + page += pager; + page += "
"; + } + + } + wifiSSIDscan=true; + + page += FPSTR(HTTP_FORM_START); + char parLength[2]; + // add the extra parameters to the form + for (int i = 0; i < _paramsCount; i++) { + if (_params[i] == NULL) { + break; + } + + String pitem = FPSTR(HTTP_FORM_PARAM); + if (_params[i]->getID() != NULL) { + pitem.replace("{i}", _params[i]->getID()); + pitem.replace("{n}", _params[i]->getID()); + pitem.replace("{p}", _params[i]->getPlaceholder()); + snprintf(parLength, 2, "%d", _params[i]->getValueLength()); + pitem.replace("{l}", parLength); + pitem.replace("{v}", _params[i]->getValue()); + pitem.replace("{c}", _params[i]->getCustomHTML()); + } else { + pitem = _params[i]->getCustomHTML(); + } + + page += pitem; + } + if (_params[0] != NULL) { + page += "
"; + } + + if (_sta_static_ip) { + + String item = FPSTR(HTTP_FORM_PARAM); + item.replace("{i}", "ip"); + item.replace("{n}", "ip"); + item.replace("{p}", "Static IP"); + item.replace("{l}", "15"); + item.replace("{v}", _sta_static_ip.toString()); + + page += item; + + item = FPSTR(HTTP_FORM_PARAM); + item.replace("{i}", "gw"); + item.replace("{n}", "gw"); + item.replace("{p}", "Static Gateway"); + item.replace("{l}", "15"); + item.replace("{v}", _sta_static_gw.toString()); + + page += item; + + item = FPSTR(HTTP_FORM_PARAM); + item.replace("{i}", "sn"); + item.replace("{n}", "sn"); + item.replace("{p}", "Subnet"); + item.replace("{l}", "15"); + item.replace("{v}", _sta_static_sn.toString()); + + page += item; + + page += "
"; + } + + page += FPSTR(HTTP_FORM_END); + page += FPSTR(HTTP_SCAN_LINK); + + page += FPSTR(HTTP_END); + + request->send(200, "text/html", page); + + + DEBUG_WM(F("Sent config page")); +} + +/** Handle the WLAN save form and redirect to WLAN config page again */ +void AsyncWiFiManager::handleWifiSave(AsyncWebServerRequest *request) { + DEBUG_WM(F("WiFi save")); + + //SAVE/connect here + needInfo = true; + _ssid = request->arg("s").c_str(); + _pass = request->arg("p").c_str(); + + //parameters + for (int i = 0; i < _paramsCount; i++) { + if (_params[i] == NULL) { + break; + } + //read parameter + String value = request->arg(_params[i]->getID()).c_str(); + //store it in array + value.toCharArray(_params[i]->_value, _params[i]->_length); + DEBUG_WM(F("Parameter")); + DEBUG_WM(_params[i]->getID()); + DEBUG_WM(value); + } + + if (request->hasArg("ip")) { + DEBUG_WM(F("static ip")); + DEBUG_WM(request->arg("ip")); + //_sta_static_ip.fromString(request->arg("ip")); + String ip = request->arg("ip"); + optionalIPFromString(&_sta_static_ip, ip.c_str()); + } + if (request->hasArg("gw")) { + DEBUG_WM(F("static gateway")); + DEBUG_WM(request->arg("gw")); + String gw = request->arg("gw"); + optionalIPFromString(&_sta_static_gw, gw.c_str()); + } + if (request->hasArg("sn")) { + DEBUG_WM(F("static netmask")); + DEBUG_WM(request->arg("sn")); + String sn = request->arg("sn"); + optionalIPFromString(&_sta_static_sn, sn.c_str()); + } + + String page = FPSTR(WFM_HTTP_HEAD); + page.replace("{v}", "Credentials Saved"); + page += FPSTR(HTTP_SCRIPT); + page += FPSTR(HTTP_STYLE); + page += _customHeadElement; + page += F(""); + page += FPSTR(HTTP_HEAD_END); + page += FPSTR(HTTP_SAVED); + page += FPSTR(HTTP_END); + + request->send(200, "text/html", page); + + DEBUG_WM(F("Sent wifi save page")); + + connect = true; //signal ready to connect/reset +} + +/** Handle the info page */ +String AsyncWiFiManager::infoAsString() +{ + String page; + page += F("
Chip ID
"); +#if defined(ESP8266) + page += ESP.getChipId(); +#else + page += getESP32ChipID(); +#endif + page += F("
"); + page += F("
Flash Chip ID
"); +#if defined(ESP8266) + page += ESP.getFlashChipId(); +#else + page += F("N/A for ESP32"); +#endif + page += F("
"); + page += F("
IDE Flash Size
"); + page += ESP.getFlashChipSize(); + page += F(" bytes
"); + page += F("
Real Flash Size
"); +#if defined(ESP8266) + page += ESP.getFlashChipRealSize(); +#else + page += F("N/A for ESP32"); +#endif + page += F(" bytes
"); + page += F("
Soft AP IP
"); + page += WiFi.softAPIP().toString(); + page += F("
"); + page += F("
Soft AP MAC
"); + page += WiFi.softAPmacAddress(); + page += F("
"); + page += F("
Station SSID
"); + page += WiFi.SSID(); + page += F("
"); + page += F("
Station IP
"); + page += WiFi.localIP().toString(); + page += F("
"); + page += F("
Station MAC
"); + page += WiFi.macAddress(); + page += F("
"); + page += F(""); + return page; +} + +void AsyncWiFiManager::handleInfo(AsyncWebServerRequest *request) { + DEBUG_WM(F("Info")); + + String page = FPSTR(WFM_HTTP_HEAD); + page.replace("{v}", "Info"); + page += FPSTR(HTTP_SCRIPT); + page += FPSTR(HTTP_STYLE); + page += _customHeadElement; + if (connect==true) + page += F(""); + page += FPSTR(HTTP_HEAD_END); + page += F("
"); + if (connect==true) + { + page += F("
Trying to connect
"); + page += wifiStatus; + page += F("
"); + } + + page +=pager; + page += FPSTR(HTTP_END); + + request->send(200, "text/html", page); + + DEBUG_WM(F("Sent info page")); +} + +/** Handle the reset page */ +void AsyncWiFiManager::handleReset(AsyncWebServerRequest *request) { + DEBUG_WM(F("Reset")); + + String page = FPSTR(WFM_HTTP_HEAD); + page.replace("{v}", "Info"); + page += FPSTR(HTTP_SCRIPT); + page += FPSTR(HTTP_STYLE); + page += _customHeadElement; + page += FPSTR(HTTP_HEAD_END); + page += F("Module will reset in a few seconds."); + page += FPSTR(HTTP_END); + request->send(200, "text/html", page); + + DEBUG_WM(F("Sent reset page")); + delay(5000); + #if defined(ESP8266) + ESP.reset(); + #else + ESP.restart(); + #endif + delay(2000); +} + + + +//removed as mentioned here https://github.com/tzapu/AsyncWiFiManager/issues/114 +/*void AsyncWiFiManager::handle204(AsyncWebServerRequest *request) { +DEBUG_WM(F("204 No Response")); +request->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); +request->sendHeader("Pragma", "no-cache"); +request->sendHeader("Expires", "-1"); +request->send ( 204, "text/plain", ""); + +}*/ + +void AsyncWiFiManager::handleNotFound(AsyncWebServerRequest *request) { + if (captivePortal(request)) { // If captive portal redirect instead of displaying the error page. + return; + } + String message = "File Not Found\n\n"; + message += "URI: "; + message += request->url(); + message += "\nMethod: "; + message += ( request->method() == HTTP_GET ) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for ( uint8_t i = 0; i < request->args(); i++ ) { + message += " " + request->argName ( i ) + ": " + request->arg ( i ) + "\n"; + } + AsyncWebServerResponse *response = request->beginResponse(404,"text/plain",message); + response->addHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response->addHeader("Pragma", "no-cache"); + response->addHeader("Expires", "-1"); + request->send (response ); +} + + +/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */ +boolean AsyncWiFiManager::captivePortal(AsyncWebServerRequest *request) { + if (!isIp(request->host()) ) { + DEBUG_WM(F("Request redirected to captive portal")); + AsyncWebServerResponse *response = request->beginResponse(302,"text/plain",""); + response->addHeader("Location", String("http://") + toStringIp(request->client()->localIP())); + request->send ( response); + return true; + } + return false; +} + +//start up config portal callback +void AsyncWiFiManager::setAPCallback( void (*func)(AsyncWiFiManager* myAsyncWiFiManager) ) { + _apcallback = func; +} + +//start up save config callback +void AsyncWiFiManager::setSaveConfigCallback( void (*func)(void) ) { + _savecallback = func; +} + +//sets a custom element to add to head, like a new style tag +void AsyncWiFiManager::setCustomHeadElement(const char* element) { + _customHeadElement = element; +} + +//if this is true, remove duplicated Access Points - defaut true +void AsyncWiFiManager::setRemoveDuplicateAPs(boolean removeDuplicates) { + _removeDuplicateAPs = removeDuplicates; +} + + + +template +void AsyncWiFiManager::DEBUG_WM(Generic text) { + if (_debug) { + Serial.print("*WM: "); + Serial.println(text); + } +} + +int AsyncWiFiManager::getRSSIasQuality(int RSSI) { + int quality = 0; + + if (RSSI <= -100) { + quality = 0; + } else if (RSSI >= -50) { + quality = 100; + } else { + quality = 2 * (RSSI + 100); + } + return quality; +} + +/** Is this an IP? */ +boolean AsyncWiFiManager::isIp(String str) { + for (int i = 0; i < str.length(); i++) { + int c = str.charAt(i); + if (c != '.' && (c < '0' || c > '9')) { + return false; + } + } + return true; +} + +/** IP to String? */ +String AsyncWiFiManager::toStringIp(IPAddress ip) { + String res = ""; + for (int i = 0; i < 3; i++) { + res += String((ip >> (8 * i)) & 0xFF) + "."; + } + res += String(((ip >> 8 * 3)) & 0xFF); + return res; +} diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/ESPAsyncWiFiManager.h b/.piolibdeps/ESPAsyncWifiManager_ID1438/ESPAsyncWiFiManager.h new file mode 100644 index 0000000..b51cddd --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/ESPAsyncWiFiManager.h @@ -0,0 +1,259 @@ +/************************************************************** + WiFiManager is a library for the ESP8266/Arduino platform + (https://github.com/esp8266/Arduino) to enable easy + configuration and reconfiguration of WiFi credentials using a Captive Portal + inspired by: + http://www.esp8266.com/viewtopic.php?f=29&t=2520 + https://github.com/chriscook8/esp-arduino-apboot + https://github.com/esp8266/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/DNSServer/examples/CaptivePortalAdvanced + Built by AlexT https://github.com/tzapu + Ported to Async Web Server by https://github.com/alanswx + Licensed under MIT license + **************************************************************/ + +#ifndef ESPAsyncWiFiManager_h +#define ESPAsyncWiFiManager_h + +#if defined(ESP8266) +#include //https://github.com/esp8266/Arduino +#else +#include +#include "esp_wps.h" +#define ESP_WPS_MODE WPS_TYPE_PBC +#endif +#include + +//#define USE_EADNS //Uncomment to use ESPAsyncDNSServer +#ifdef USE_EADNS +#include //https://github.com/devyte/ESPAsyncDNSServer + //https://github.com/me-no-dev/ESPAsyncUDP +#else +#include +#endif +#include + +#if defined(ESP8266) +extern "C" { + #include "user_interface.h" +} +#else +#include +#endif + +const char WFM_HTTP_HEAD[] PROGMEM = "{v}"; +const char HTTP_STYLE[] PROGMEM = ""; +const char HTTP_SCRIPT[] PROGMEM = ""; +const char HTTP_HEAD_END[] PROGMEM = "
"; +const char HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; +const char HTTP_ITEM[] PROGMEM = "
{v} {r}%
"; +const char HTTP_FORM_START[] PROGMEM = "


"; +const char HTTP_FORM_PARAM[] PROGMEM = "
"; +const char HTTP_FORM_END[] PROGMEM = "
"; +const char HTTP_SCAN_LINK[] PROGMEM = "
"; +const char HTTP_SAVED[] PROGMEM = "
Credentials Saved
Trying to connect ESP to network.
If it fails reconnect to AP to try again
"; +const char HTTP_END[] PROGMEM = "
"; + +#define WIFI_MANAGER_MAX_PARAMS 10 + +class AsyncWiFiManagerParameter { +public: + AsyncWiFiManagerParameter(const char *custom); + AsyncWiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length); + AsyncWiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom); + + const char *getID(); + const char *getValue(); + const char *getPlaceholder(); + int getValueLength(); + const char *getCustomHTML(); +private: + const char *_id; + const char *_placeholder; + char *_value; + int _length; + const char *_customHTML; + + void init(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom); + + friend class AsyncWiFiManager; +}; + + +class WiFiResult +{ +public: + bool duplicate; + String SSID; + uint8_t encryptionType; + int32_t RSSI; + uint8_t* BSSID; + int32_t channel; + bool isHidden; + + WiFiResult() + { + } + + +}; + +class AsyncWiFiManager +{ +public: + #ifdef USE_EADNS + AsyncWiFiManager(AsyncWebServer * server, AsyncDNSServer *dns); + #else + AsyncWiFiManager(AsyncWebServer * server, DNSServer *dns); + #endif + + void scan(); + String scanModal(); + void loop(); + void safeLoop(); + void criticalLoop(); + String infoAsString(); + + boolean autoConnect(); + boolean autoConnect(char const *apName, char const *apPassword = NULL); + + //if you want to always start the config portal, without trying to connect first + boolean startConfigPortal(char const *apName, char const *apPassword = NULL); + void startConfigPortalModeless(char const *apName, char const *apPassword); + + // get the AP name of the config portal, so it can be used in the callback + String getConfigPortalSSID(); + + void resetSettings(); + + //sets timeout before webserver loop ends and exits even if there has been no setup. + //usefully for devices that failed to connect at some point and got stuck in a webserver loop + //in seconds setConfigPortalTimeout is a new name for setTimeout + void setConfigPortalTimeout(unsigned long seconds); + void setTimeout(unsigned long seconds); + + //sets timeout for which to attempt connecting, usefull if you get a lot of failed connects + void setConnectTimeout(unsigned long seconds); + + + void setDebugOutput(boolean debug); + //defaults to not showing anything under 8% signal quality if called + void setMinimumSignalQuality(int quality = 8); + //sets a custom ip /gateway /subnet configuration + void setAPStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn); + //sets config for a static IP + void setSTAStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn); + //called when AP mode and config portal is started + void setAPCallback( void (*func)(AsyncWiFiManager*) ); + //called when settings have been changed and connection was successful + void setSaveConfigCallback( void (*func)(void) ); + //adds a custom parameter + void addParameter(AsyncWiFiManagerParameter *p); + //if this is set, it will exit after config, even if connection is unsucessful. + void setBreakAfterConfig(boolean shouldBreak); + //if this is set, try WPS setup when starting (this will delay config portal for up to 2 mins) + //TODO + //if this is set, customise style + void setCustomHeadElement(const char* element); + //if this is true, remove duplicated Access Points - defaut true + void setRemoveDuplicateAPs(boolean removeDuplicates); + +private: + #ifdef USE_EADNS + AsyncDNSServer *dnsServer; + #else + DNSServer *dnsServer; + #endif + AsyncWebServer *server; + + + boolean _modeless; + int scannow; + int shouldscan; + boolean needInfo = true; + + //const int WM_DONE = 0; + //const int WM_WAIT = 10; + + //const String HTTP_HEAD = "{v}"; + + void setupConfigPortal(); + void startWPS(); + + String pager; + wl_status_t wifiStatus; + const char* _apName = "no-net"; + const char* _apPassword = NULL; + String _ssid = ""; + String _pass = ""; + unsigned long _configPortalTimeout = 0; + unsigned long _connectTimeout = 0; + unsigned long _configPortalStart = 0; + + IPAddress _ap_static_ip; + IPAddress _ap_static_gw; + IPAddress _ap_static_sn; + IPAddress _sta_static_ip; + IPAddress _sta_static_gw; + IPAddress _sta_static_sn; + + int _paramsCount = 0; + int _minimumQuality = -1; + boolean _removeDuplicateAPs = true; + boolean _shouldBreakAfterConfig = false; + boolean _tryWPS = false; + + const char* _customHeadElement = ""; + + //String getEEPROMString(int start, int len); + //void setEEPROMString(int start, int len, String string); + + int status = WL_IDLE_STATUS; + int connectWifi(String ssid, String pass); + uint8_t waitForConnectResult(); + void setInfo(); + + String networkListAsString(); + + void handleRoot(AsyncWebServerRequest *); + void handleWifi(AsyncWebServerRequest*,boolean scan); + void handleWifiSave(AsyncWebServerRequest*); + void handleInfo(AsyncWebServerRequest*); + void handleReset(AsyncWebServerRequest*); + void handleNotFound(AsyncWebServerRequest*); + void handle204(AsyncWebServerRequest*); + boolean captivePortal(AsyncWebServerRequest*); + + // DNS server + const byte DNS_PORT = 53; + + //helpers + int getRSSIasQuality(int RSSI); + boolean isIp(String str); + String toStringIp(IPAddress ip); + + boolean connect; + boolean _debug = true; + + WiFiResult *wifiSSIDs; + int wifiSSIDCount; + boolean wifiSSIDscan; + + void (*_apcallback)(AsyncWiFiManager*) = NULL; + void (*_savecallback)(void) = NULL; + + AsyncWiFiManagerParameter* _params[WIFI_MANAGER_MAX_PARAMS]; + + template + void DEBUG_WM(Generic text); + + template + auto optionalIPFromString(T *obj, const char *s) -> decltype( obj->fromString(s) ) { + return obj->fromString(s); + } + auto optionalIPFromString(...) -> bool { + DEBUG_WM("NO fromString METHOD ON IPAddress, you need ESP8266 core 2.1.0 or newer for Custom IP configuration to work."); + return false; + } +}; + +#endif diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/LICENSE b/.piolibdeps/ESPAsyncWifiManager_ID1438/LICENSE new file mode 100644 index 0000000..1dabff5 --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 tzapu + +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/.piolibdeps/ESPAsyncWifiManager_ID1438/README.md b/.piolibdeps/ESPAsyncWifiManager_ID1438/README.md new file mode 100644 index 0000000..6be8edb --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/README.md @@ -0,0 +1,373 @@ +# AsyncWiFiManager +ESP8266 Async WiFi Connection manager with fallback web configuration portal + +[![Build Status](https://travis-ci.org/tzapu/WiFiManager.svg?branch=master)](https://travis-ci.org/tzapu/WiFiManager) + +The configuration portal is of the captive variety, so on various devices it will present the configuration dialogue as soon as you connect to the created access point. + +First attempt at a library. Lots more changes and fixes to do. Contributions are welcome. + +#### This works with the ESP8266 Arduino platform with a recent stable release(2.0.0 or newer) https://github.com/esp8266/Arduino + +## Contents + - [How it works](#how-it-works) + - [Wishlist](#wishlist) + - [Quick start](#quick-start) + - Installing + - [Through Library Manager](#install-through-library-manager) + - [From Github](#checkout-from-github) + - [Using](#using) + - [Documentation](#documentation) + - [Access Point Password](#password-protect-the-configuration-access-point) + - [Callbacks](#callbacks) + - [Configuration Portal Timeout](#configuration-portal-timeout) + - [On Demand Configuration](#on-demand-configuration-portal) + - [Custom Parameters](#custom-parameters) + - [Custom IP Configuration](#custom-ip-configuration) + - [Filter Low Quality Networks](#filter-networks) + - [Debug Output](#debug) + - [Troubleshooting](#troubleshooting) + - [Releases](#releases) + - [Contributors](#contributions-and-thanks) + + +## How It Works +- when your ESP starts up, it sets it up in Station mode and tries to connect to a previously saved Access Point +- if this is unsuccessful (or no previous network saved) it moves the ESP into Access Point mode and spins up a DNS and WebServer (default ip 192.168.4.1) +- using any wifi enabled device with a browser (computer, phone, tablet) connect to the newly created Access Point +- because of the Captive Portal and the DNS server you will either get a 'Join to network' type of popup or get any domain you try to access redirected to the configuration portal +- choose one of the access points scanned, enter password, click save +- ESP will try to connect. If successful, it relinquishes control back to your app. If not, reconnect to AP and reconfigure. + +## How It Looks +![ESP8266 WiFi Captive Portal Homepage](http://i.imgur.com/YPvW9eql.png) ![ESP8266 WiFi Captive Portal Configuration](http://i.imgur.com/oicWJ4gl.png) + +## Wishlist +- ~~remove dependency on EEPROM library~~ +- ~~move HTML Strings to PROGMEM~~ +- ~~cleanup and streamline code~~ (although this is ongoing) +- if timeout is set, extend it when a page is fetched in AP mode +- ~~add ability to configure more parameters than ssid/password~~ +- ~~maybe allow setting ip of ESP after reboot~~ +- ~~add to Arduino Library Manager~~ +- ~~add to PlatformIO~~ +- add multiple sets of network credentials +- ~~allow users to customize CSS~~ + +## Quick Start + +### Installing +You can either install through the Arduino Library Manager or checkout the latest changes or a release from github + +#### Install through Library Manager +__Currently version 0.8+ works with release 2.0.0 or newer of the [ESP8266 core for Arduino](https://github.com/esp8266/Arduino)__ + - in Arduino IDE got to Sketch/Include Library/Manage Libraries + ![Manage Libraries](http://i.imgur.com/9BkEBkR.png) + + - search for WiFiManager + ![WiFiManager package](http://i.imgur.com/18yIai8.png) + + - click Install and start [using it](#using) + +#### Checkout from github +__Github version works with release 2.0.0 or newer of the [ESP8266 core for Arduino](https://github.com/esp8266/Arduino)__ +- Checkout library to your Arduino libraries folder + +### Using +- Include in your sketch +```cpp +#include //ESP8266 Core WiFi Library (you most likely already have this in your sketch) + +#include //Local DNS Server used for redirecting all requests to the configuration portal +#include //Local WebServer used to serve the configuration portal +#include //https://github.com/tzapu/WiFiManager WiFi Configuration Magic +``` + +- Initialize library, in your setup function add +```cpp +WiFiManager wifiManager; +``` + +- Also in the setup function add +```cpp +//first parameter is name of access point, second is the password +wifiManager.autoConnect("AP-NAME", "AP-PASSWORD"); +``` +if you just want an unsecured access point +```cpp +wifiManager.autoConnect("AP-NAME"); +``` +or if you want to use and auto generated name from 'ESP' and the esp's Chip ID use +```cpp +wifiManager.autoConnect(); +``` + +After you write your sketch and start the ESP, it will try to connect to WiFi. If it fails it starts in Access Point mode. +While in AP mode, connect to it then open a browser to the gateway IP, default 192.168.4.1, configure wifi, save and it should reboot and connect. + +Also see [examples](https://github.com/tzapu/WiFiManager/tree/master/examples). + +## Documentation + +#### Password protect the configuration Access Point +You can and should password protect the configuration access point. Simply add the password as a second parameter to `autoConnect`. +A short password seems to have unpredictable results so use one that's around 8 characters or more in length. +The guidelines are that a wifi password must consist of 8 to 63 ASCII-encoded characters in the range of 32 to 126 (decimal) +```cpp +wifiManager.autoConnect("AutoConnectAP", "password") +``` + +#### Callbacks +##### Enter Config mode +Use this if you need to do something when your device enters configuration mode on failed WiFi connection attempt. +Before `autoConnect()` +```cpp +wifiManager.setAPCallback(configModeCallback); +``` +`configModeCallback` declaration and example +```cpp +void configModeCallback (WiFiManager *myWiFiManager) { + Serial.println("Entered config mode"); + Serial.println(WiFi.softAPIP()); + + Serial.println(myWiFiManager->getConfigPortalSSID()); +} +``` + +##### Save settings +This gets called when custom parameters have been set **AND** a connection has been established. Use it to set a flag, so when all the configuration finishes, you can save the extra parameters somewhere. + +See [AutoConnectWithFSParameters Example](https://github.com/tzapu/WiFiManager/tree/master/examples/AutoConnectWithFSParameters). +```cpp +wifiManager.setSaveConfigCallback(saveConfigCallback); +``` +`saveConfigCallback` declaration and example +```cpp +//flag for saving data +bool shouldSaveConfig = false; + +//callback notifying us of the need to save config +void saveConfigCallback () { + Serial.println("Should save config"); + shouldSaveConfig = true; +} +``` + +#### Configuration Portal Timeout +If you need to set a timeout so the ESP doesn't hang waiting to be configured, for instance after a power failure, you can add +```cpp +wifiManager.setConfigPortalTimeout(180); +``` +which will wait 3 minutes (180 seconds). When the time passes, the autoConnect function will return, no matter the outcome. +Check for connection and if it's still not established do whatever is needed (on some modules I restart them to retry, on others I enter deep sleep) + +#### On Demand Configuration Portal +If you would rather start the configuration portal on demand rather than automatically on a failed connection attempt, then this is for you. + +Instead of calling `autoConnect()` which does all the connecting and failover configuration portal setup for you, you need to use `startConfigPortal()`. __Do not use BOTH.__ + +Example usage +```cpp +void loop() { + // is configuration portal requested? + if ( digitalRead(TRIGGER_PIN) == LOW ) { + WiFiManager wifiManager; + wifiManager.startConfigPortal("OnDemandAP"); + Serial.println("connected...yeey :)"); + } +} +``` +See example for a more complex version. [OnDemandConfigPortal](https://github.com/tzapu/WiFiManager/tree/master/examples/OnDemandConfigPortal) + +#### Custom Parameters +You can use WiFiManager to collect more parameters than just SSID and password. +This could be helpful for configuring stuff like MQTT host and port, [blynk](http://www.blynk.cc) or [emoncms](http://emoncms.org) tokens, just to name a few. +**You are responsible for saving and loading these custom values.** The library just collects and displays the data for you as a convenience. +Usage scenario would be: +- load values from somewhere (EEPROM/FS) or generate some defaults +- add the custom parameters to WiFiManager using + ```cpp + // id/name, placeholder/prompt, default, length + WiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40); + wifiManager.addParameter(&custom_mqtt_server); + + ``` +- if connection to AP fails, configuration portal starts and you can set /change the values (or use on demand configuration portal) +- once configuration is done and connection is established [save config callback]() is called +- once WiFiManager returns control to your application, read and save the new values using the `WiFiManagerParameter` object. + ```cpp + mqtt_server = custom_mqtt_server.getValue(); + ``` +This feature is a lot more involved than all the others, so here are some examples to fully show how it is done. +You should also take a look at adding custom HTML to your form. + +- Save and load custom parameters to file system in json form [AutoConnectWithFSParameters](https://github.com/tzapu/WiFiManager/tree/master/examples/AutoConnectWithFSParameters) +- *Save and load custom parameters to EEPROM* (not done yet) + +#### Custom IP Configuration +You can set a custom IP for both AP (access point, config mode) and STA (station mode, client mode, normal project state) + +##### Custom Access Point IP Configuration +This will set your captive portal to a specific IP should you need/want such a feature. Add the following snippet before `autoConnect()` +```cpp +//set custom ip for portal +wifiManager.setAPStaticIPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); +``` + +##### Custom Station (client) Static IP Configuration +This will make use the specified IP configuration instead of using DHCP in station mode. +```cpp +wifiManager.setSTAStaticIPConfig(IPAddress(192,168,0,99), IPAddress(192,168,0,1), IPAddress(255,255,255,0)); +``` +There are a couple of examples in the examples folder that show you how to set a static IP and even how to configure it through the web configuration portal. + +#### Custom HTML, CSS, Javascript +There are various ways in which you can inject custom HTML, CSS or Javascript into the configuration portal. +The options are: +- inject custom head element +You can use this to any html bit to the head of the configuration portal. If you add a `"); +``` +- inject a custom bit of html in the configuration form +```cpp +WiFiManagerParameter custom_text("

This is just a text paragraph

"); +wifiManager.addParameter(&custom_text); +``` +- inject a custom bit of html in a configuration form element +Just add the bit you want added as the last parameter to the custom parameter constructor. +```cpp +WiFiManagerParameter custom_mqtt_server("server", "mqtt server", "iot.eclipse", 40, " readonly"); +``` + +#### Filter Networks +You can filter networks based on signal quality and show/hide duplicate networks. + +- If you would like to filter low signal quality networks you can tell WiFiManager to not show networks below an arbitrary quality %; +```cpp +wifiManager.setMinimumSignalQuality(10); +``` +will not show networks under 10% signal quality. If you omit the parameter it defaults to 8%; + +- You can also remove or show duplicate networks (default is remove). +Use this function to show (or hide) all networks. +```cpp +wifiManager.setRemoveDuplicateAPs(false); +``` + +#### Debug +Debug is enabled by default on Serial. To disable add before autoConnect +```cpp +wifiManager.setDebugOutput(false); +``` + +## Troubleshooting +If you get compilation errors, more often than not, you may need to install a newer version of the ESP8266 core for Arduino. + +Changes added on 0.8 should make the latest trunk work without compilation errors. Tested down to ESP8266 core 2.0.0. **Please update to version 0.8** + +I am trying to keep releases working with release versions of the core, so they can be installed through boards manager, but if you checkout the latest version directly from github, sometimes, the library will only work if you update the ESP8266 core to the latest version because I am using some newly added function. + +If you connect to the created configuration Access Point but the configuration portal does not show up, just open a browser and type in the IP of the web portal, by default `192.168.4.1`. + +If trying to connect ends up in an endless loop, try to add `setConnectTimeout(60)` before `autoConnect();`. The parameter is timeout to try connecting in seconds. + +## Releases +##### 0.15 +- fixed: connection with saved credentials + +##### 0.12 +- removed 204 header response +- fixed incompatibility with other libs using isnan and other std:: functions without namespace + +##### 0.11 +- a lot more reliable reconnecting to networks +- custom html in custom parameters (for read only params) +- custom html in custom parameter form (like labels) +- custom head element (like custom css) +- sort networks based on signal quality +- remove duplicate networks + +##### 0.10 +- some css changes +- bug fixes and speed improvements +- added an alternative to waitForConnectResult() for debugging +- changed `setTimeout(seconds)` to `setConfigPortalTimeout(seconds)` + +##### 0.9 + - fixed support for encoded characters in ssid/pass + +##### 0.8 + - made it compile on older versions of ESP8266 core as well, tested down to 2.0.0 + - added simple example for Custom IP + +##### 0.7 + - added static IP in station mode + - added example of persisting custom IP to FS config.json + - more option on portal homepage + - added on PlatformIO + +##### 0.6 + - custom parameters + - prettier + - on demand config portal + - commit #100 :D + +##### 0.5 + - Added to Arduino Boards Manager - Thanks Max + - moved most stuff to PROGMEM + - added signal quality and a nice little padlock to show which networks are encrypted + +##### v0.4 - all of it user contributed changes - Thank you + - added ability to password protect the configuration Access Point + - callback for enter configuration mode + - memory allocation improvements + +##### v0.3 + - removed the need for EEPROM and works with the 2.0.0 and above stable release of the ESP8266 for Arduino IDE package + - removed restart on save of credentials + - updated examples + +##### v0.2 +needs the latest staging version (or at least a recent release of the staging version) to work + +##### v0.1 +works with the staging release ver. 1.6.5-1044-g170995a, built on Aug 10, 2015 of the ESP8266 Arduino library. + + +### Contributions and thanks +The support and help I got from the community has been nothing short of phenomenal. I can't thank you guys enough. This is my first real attept in developing open source stuff and I must say, now I understand why people are so dedicated to it, it is because of all the wonderful people involved. + +__THANK YOU__ +[tzapu](https://github.com/tzapu/) + +[Shawn A](https://github.com/tablatronix) + +[Maximiliano Duarte](https://github.com/domonetic) + +[alltheblinkythings](https://github.com/alltheblinkythings) + +[Niklas Wall](https://github.com/niklaswall) + +[Jakub Piasecki](https://github.com/zaporylie) + +[Peter Allan](https://github.com/alwynallan) + +[John Little](https://github.com/j0hnlittle) + +[markaswift](https://github.com/markaswift) + +[franklinvv](https://github.com/franklinvv) + +[Alberto Ricci Bitti](https://github.com/riccibitti) + +[SebiPanther](https://github.com/SebiPanther) + +[jonathanendersby](https://github.com/jonathanendersby) + +[walthercarsten](https://github.com/walthercarsten) + +Sorry if i have missed anyone. + +#### Inspiration +- http://www.esp8266.com/viewtopic.php?f=29&t=2520 diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnect/AutoConnect.ino b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnect/AutoConnect.ino new file mode 100644 index 0000000..438a2ad --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnect/AutoConnect.ino @@ -0,0 +1,38 @@ +#if defined(ESP8266) +#include //https://github.com/esp8266/Arduino +#else +#include +#endif + +//needed for library +#include +#include //https://github.com/tzapu/WiFiManager + +AsyncWebServer server(80); +DNSServer dns; + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + AsyncWiFiManager wifiManager(&server,&dns); + //reset saved settings + //wifiManager.resetSettings(); + //set custom ip for portal + //wifiManager.setAPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); + //fetches ssid and pass from eeprom and tries to connect + //if it does not connect it starts an access point with the specified name + //here "AutoConnectAP" + //and goes into a blocking loop awaiting configuration + wifiManager.autoConnect("AutoConnectAP"); + //or use this for auto generated name ESP + ChipID + //wifiManager.autoConnect(); + //if you get here you have connected to the WiFi + Serial.println("connected...yeey :)"); +} + +void loop() { + // put your main code here, to run repeatedly: +} diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithFSParameters/AutoConnectWithFSParameters.ino b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithFSParameters/AutoConnectWithFSParameters.ino new file mode 100644 index 0000000..e869fd9 --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithFSParameters/AutoConnectWithFSParameters.ino @@ -0,0 +1,162 @@ +#include //this needs to be first, or it all crashes and burns... + +#if defined(ESP8266) +#include //https://github.com/esp8266/Arduino +#else +#include +#endif + +//needed for library +#include +#include //https://github.com/tzapu/WiFiManager + +#include //https://github.com/bblanchon/ArduinoJson + +//define your default values here, if there are different values in config.json, they are overwritten. +char mqtt_server[40]; +char mqtt_port[6] = "8080"; +char blynk_token[34] = "YOUR_BLYNK_TOKEN"; + +//flag for saving data +bool shouldSaveConfig = false; + +//callback notifying us of the need to save config +void saveConfigCallback () { + Serial.println("Should save config"); + shouldSaveConfig = true; +} + +AsyncWebServer server(80); +DNSServer dns; + + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + Serial.println(); + + //clean FS, for testing + //SPIFFS.format(); + + //read configuration from FS json + Serial.println("mounting FS..."); + + if (SPIFFS.begin()) { + Serial.println("mounted file system"); + if (SPIFFS.exists("/config.json")) { + //file exists, reading and loading + Serial.println("reading config file"); + File configFile = SPIFFS.open("/config.json", "r"); + if (configFile) { + Serial.println("opened config file"); + size_t size = configFile.size(); + // Allocate a buffer to store contents of the file. + std::unique_ptr buf(new char[size]); + + configFile.readBytes(buf.get(), size); + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.parseObject(buf.get()); + json.printTo(Serial); + if (json.success()) { + Serial.println("\nparsed json"); + + strcpy(mqtt_server, json["mqtt_server"]); + strcpy(mqtt_port, json["mqtt_port"]); + strcpy(blynk_token, json["blynk_token"]); + + } else { + Serial.println("failed to load json config"); + } + } + } + } else { + Serial.println("failed to mount FS"); + } + //end read + + + + // The extra parameters to be configured (can be either global or just in the setup) + // After connecting, parameter.getValue() will get you the configured value + // id/name placeholder/prompt default length + AsyncWiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40); + AsyncWiFiManagerParameter custom_mqtt_port("port", "mqtt port", mqtt_port, 5); + AsyncWiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 32); + + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + AsyncWiFiManager wifiManager(&server,&dns); + + //set config save notify callback + wifiManager.setSaveConfigCallback(saveConfigCallback); + + //set static ip + wifiManager.setSTAStaticIPConfig(IPAddress(10,0,1,99), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); + + //add all your parameters here + wifiManager.addParameter(&custom_mqtt_server); + wifiManager.addParameter(&custom_mqtt_port); + wifiManager.addParameter(&custom_blynk_token); + + //reset settings - for testing + //wifiManager.resetSettings(); + + //set minimu quality of signal so it ignores AP's under that quality + //defaults to 8% + //wifiManager.setMinimumSignalQuality(); + + //sets timeout until configuration portal gets turned off + //useful to make it all retry or go to sleep + //in seconds + //wifiManager.setTimeout(120); + + //fetches ssid and pass and tries to connect + //if it does not connect it starts an access point with the specified name + //here "AutoConnectAP" + //and goes into a blocking loop awaiting configuration + if (!wifiManager.autoConnect("AutoConnectAP", "password")) { + Serial.println("failed to connect and hit timeout"); + delay(3000); + //reset and try again, or maybe put it to deep sleep + ESP.reset(); + delay(5000); + } + + //if you get here you have connected to the WiFi + Serial.println("connected...yeey :)"); + + //read updated parameters + strcpy(mqtt_server, custom_mqtt_server.getValue()); + strcpy(mqtt_port, custom_mqtt_port.getValue()); + strcpy(blynk_token, custom_blynk_token.getValue()); + + //save the custom parameters to FS + if (shouldSaveConfig) { + Serial.println("saving config"); + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + json["mqtt_server"] = mqtt_server; + json["mqtt_port"] = mqtt_port; + json["blynk_token"] = blynk_token; + + File configFile = SPIFFS.open("/config.json", "w"); + if (!configFile) { + Serial.println("failed to open config file for writing"); + } + + json.printTo(Serial); + json.printTo(configFile); + configFile.close(); + //end save + } + + Serial.println("local ip"); + Serial.println(WiFi.localIP()); + +} + +void loop() { + // put your main code here, to run repeatedly: + + +} diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithFSParametersAndCustomIP/AutoConnectWithFSParametersAndCustomIP.ino b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithFSParametersAndCustomIP/AutoConnectWithFSParametersAndCustomIP.ino new file mode 100644 index 0000000..b95b1d0 --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithFSParametersAndCustomIP/AutoConnectWithFSParametersAndCustomIP.ino @@ -0,0 +1,194 @@ +#include //this needs to be first, or it all crashes and burns... + +#if defined(ESP8266) +#include //https://github.com/esp8266/Arduino +#else +#include +#endif + +//needed for library +#include +#include //https://github.com/tzapu/WiFiManager + +#include //https://github.com/bblanchon/ArduinoJson + +//define your default values here, if there are different values in config.json, they are overwritten. +//length should be max size + 1 +char mqtt_server[40]; +char mqtt_port[6] = "8080"; +char blynk_token[33] = "YOUR_BLYNK_TOKEN"; +//default custom static IP +char static_ip[16] = "10.0.1.56"; +char static_gw[16] = "10.0.1.1"; +char static_sn[16] = "255.255.255.0"; + +//flag for saving data +bool shouldSaveConfig = false; + +//callback notifying us of the need to save config +void saveConfigCallback () { + Serial.println("Should save config"); + shouldSaveConfig = true; +} + +AsyncWebServer server(80); +DNSServer dns; + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + Serial.println(); + + //clean FS, for testing + //SPIFFS.format(); + + //read configuration from FS json + Serial.println("mounting FS..."); + + if (SPIFFS.begin()) { + Serial.println("mounted file system"); + if (SPIFFS.exists("/config.json")) { + //file exists, reading and loading + Serial.println("reading config file"); + File configFile = SPIFFS.open("/config.json", "r"); + if (configFile) { + Serial.println("opened config file"); + size_t size = configFile.size(); + // Allocate a buffer to store contents of the file. + std::unique_ptr buf(new char[size]); + + configFile.readBytes(buf.get(), size); + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.parseObject(buf.get()); + json.printTo(Serial); + if (json.success()) { + Serial.println("\nparsed json"); + + strcpy(mqtt_server, json["mqtt_server"]); + strcpy(mqtt_port, json["mqtt_port"]); + strcpy(blynk_token, json["blynk_token"]); + + if(json["ip"]) { + Serial.println("setting custom ip from config"); + //static_ip = json["ip"]; + strcpy(static_ip, json["ip"]); + strcpy(static_gw, json["gateway"]); + strcpy(static_sn, json["subnet"]); + //strcat(static_ip, json["ip"]); + //static_gw = json["gateway"]; + //static_sn = json["subnet"]; + Serial.println(static_ip); +/* Serial.println("converting ip"); + IPAddress ip = ipFromCharArray(static_ip); + Serial.println(ip);*/ + } else { + Serial.println("no custom ip in config"); + } + } else { + Serial.println("failed to load json config"); + } + } + } + } else { + Serial.println("failed to mount FS"); + } + //end read + Serial.println(static_ip); + Serial.println(blynk_token); + Serial.println(mqtt_server); + + + // The extra parameters to be configured (can be either global or just in the setup) + // After connecting, parameter.getValue() will get you the configured value + // id/name placeholder/prompt default length + AsyncWiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40); + AsyncWiFiManagerParameter custom_mqtt_port("port", "mqtt port", mqtt_port, 5); + AsyncWiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 34); + + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + AsyncWiFiManager wifiManager(&server,&dns); + + //set config save notify callback + wifiManager.setSaveConfigCallback(saveConfigCallback); + + //set static ip + IPAddress _ip,_gw,_sn; + _ip.fromString(static_ip); + _gw.fromString(static_gw); + _sn.fromString(static_sn); + + wifiManager.setSTAStaticIPConfig(_ip, _gw, _sn); + + //add all your parameters here + wifiManager.addParameter(&custom_mqtt_server); + wifiManager.addParameter(&custom_mqtt_port); + wifiManager.addParameter(&custom_blynk_token); + + //reset settings - for testing + //wifiManager.resetSettings(); + + //set minimu quality of signal so it ignores AP's under that quality + //defaults to 8% + wifiManager.setMinimumSignalQuality(); + + //sets timeout until configuration portal gets turned off + //useful to make it all retry or go to sleep + //in seconds + //wifiManager.setTimeout(120); + + //fetches ssid and pass and tries to connect + //if it does not connect it starts an access point with the specified name + //here "AutoConnectAP" + //and goes into a blocking loop awaiting configuration + if (!wifiManager.autoConnect("AutoConnectAP", "password")) { + Serial.println("failed to connect and hit timeout"); + delay(3000); + //reset and try again, or maybe put it to deep sleep + ESP.reset(); + delay(5000); + } + + //if you get here you have connected to the WiFi + Serial.println("connected...yeey :)"); + + //read updated parameters + strcpy(mqtt_server, custom_mqtt_server.getValue()); + strcpy(mqtt_port, custom_mqtt_port.getValue()); + strcpy(blynk_token, custom_blynk_token.getValue()); + + //save the custom parameters to FS + if (shouldSaveConfig) { + Serial.println("saving config"); + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + json["mqtt_server"] = mqtt_server; + json["mqtt_port"] = mqtt_port; + json["blynk_token"] = blynk_token; + + json["ip"] = WiFi.localIP().toString(); + json["gateway"] = WiFi.gatewayIP().toString(); + json["subnet"] = WiFi.subnetMask().toString(); + + File configFile = SPIFFS.open("/config.json", "w"); + if (!configFile) { + Serial.println("failed to open config file for writing"); + } + + json.prettyPrintTo(Serial); + json.printTo(configFile); + configFile.close(); + //end save + } + + Serial.println("local ip"); + Serial.println(WiFi.localIP()); + Serial.println(WiFi.gatewayIP()); + Serial.println(WiFi.subnetMask()); +} + +void loop() { + // put your main code here, to run repeatedly: + + +} diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithFeedback/AutoConnectWithFeedback.ino b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithFeedback/AutoConnectWithFeedback.ino new file mode 100644 index 0000000..60a97d3 --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithFeedback/AutoConnectWithFeedback.ino @@ -0,0 +1,53 @@ +#if defined(ESP8266) +#include //https://github.com/esp8266/Arduino +#else +#include +#endif + +//needed for library +#include +#include //https://github.com/tzapu/WiFiManager + +void configModeCallback (AsyncWiFiManager *myWiFiManager) { + Serial.println("Entered config mode"); + Serial.println(WiFi.softAPIP()); + //if you used auto generated SSID, print it + Serial.println(myWiFiManager->getConfigPortalSSID()); +} + +AsyncWebServer server(80); +DNSServer dns; + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + AsyncWiFiManager wifiManager(&server,&dns); + //reset settings - for testing + //wifiManager.resetSettings(); + + //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode + wifiManager.setAPCallback(configModeCallback); + + //fetches ssid and pass and tries to connect + //if it does not connect it starts an access point with the specified name + //here "AutoConnectAP" + //and goes into a blocking loop awaiting configuration + if(!wifiManager.autoConnect()) { + Serial.println("failed to connect and hit timeout"); + //reset and try again, or maybe put it to deep sleep + ESP.reset(); + delay(1000); + } + + //if you get here you have connected to the WiFi + Serial.println("connected...yeey :)"); + +} + +void loop() { + // put your main code here, to run repeatedly: + +} diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithFeedbackLED/AutoConnectWithFeedbackLED.ino b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithFeedbackLED/AutoConnectWithFeedbackLED.ino new file mode 100644 index 0000000..78762dc --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithFeedbackLED/AutoConnectWithFeedbackLED.ino @@ -0,0 +1,74 @@ +#if defined(ESP8266) +#include //https://github.com/esp8266/Arduino +#else +#include +#endif + +//needed for library +#include +#include //https://github.com/tzapu/WiFiManager + +//for LED status +#include +Ticker ticker; + +void tick() +{ + //toggle state + int state = digitalRead(BUILTIN_LED); // get the current state of GPIO1 pin + digitalWrite(BUILTIN_LED, !state); // set pin to the opposite state +} + +//gets called when WiFiManager enters configuration mode +void configModeCallback (AsyncWiFiManager *myWiFiManager) { + Serial.println("Entered config mode"); + Serial.println(WiFi.softAPIP()); + //if you used auto generated SSID, print it + Serial.println(myWiFiManager->getConfigPortalSSID()); + //entered config mode, make led toggle faster + ticker.attach(0.2, tick); +} + +AsyncWebServer server(80); +DNSServer dns; + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + + //set led pin as output + pinMode(BUILTIN_LED, OUTPUT); + // start ticker with 0.5 because we start in AP mode and try to connect + ticker.attach(0.6, tick); + + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + AsyncWiFiManager wifiManager(&server,&dns); + //reset settings - for testing + //wifiManager.resetSettings(); + + //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode + wifiManager.setAPCallback(configModeCallback); + + //fetches ssid and pass and tries to connect + //if it does not connect it starts an access point with the specified name + //here "AutoConnectAP" + //and goes into a blocking loop awaiting configuration + if (!wifiManager.autoConnect()) { + Serial.println("failed to connect and hit timeout"); + //reset and try again, or maybe put it to deep sleep + ESP.reset(); + delay(1000); + } + + //if you get here you have connected to the WiFi + Serial.println("connected...yeey :)"); + ticker.detach(); + //keep LED on + digitalWrite(BUILTIN_LED, LOW); +} + +void loop() { + // put your main code here, to run repeatedly: + +} diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithReset/AutoConnectWithReset.ino b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithReset/AutoConnectWithReset.ino new file mode 100644 index 0000000..b6579fb --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithReset/AutoConnectWithReset.ino @@ -0,0 +1,55 @@ +#include //this needs to be first, or it all crashes and burns... + +#if defined(ESP8266) +#include //https://github.com/esp8266/Arduino +#else +#include +#endif + +//needed for library +#include +#include //https://github.com/tzapu/WiFiManager + +AsyncWebServer server(80); +DNSServer dns; + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + Serial.println(); + + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + AsyncWiFiManager wifiManager(&server,&dns); + + //exit after config instead of connecting + wifiManager.setBreakAfterConfig(true); + + //reset settings - for testing + //wifiManager.resetSettings(); + + + //tries to connect to last known settings + //if it does not connect it starts an access point with the specified name + //here "AutoConnectAP" with password "password" + //and goes into a blocking loop awaiting configuration + if (!wifiManager.autoConnect("AutoConnectAP", "password")) { + Serial.println("failed to connect, we should reset as see if it connects"); + delay(3000); + ESP.reset(); + delay(5000); + } + + //if you get here you have connected to the WiFi + Serial.println("connected...yeey :)"); + + + Serial.println("local ip"); + Serial.println(WiFi.localIP()); +} + +void loop() { + // put your main code here, to run repeatedly: + + +} diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithStaticIP/AutoConnectWithStaticIP.ino b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithStaticIP/AutoConnectWithStaticIP.ino new file mode 100644 index 0000000..1d5c831 --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithStaticIP/AutoConnectWithStaticIP.ino @@ -0,0 +1,81 @@ +#include //this needs to be first, or it all crashes and burns... + +#if defined(ESP8266) +#include //https://github.com/esp8266/Arduino +#else +#include +#endif + +//needed for library +#include +#include //https://github.com/tzapu/WiFiManager + +AsyncWebServer server(80); +DNSServer dns; + + +/************************************************************************************** + * this example shows how to set a static IP configuration for the ESP + * although the IP shows in the config portal, the changes will revert + * to the IP set in the source file. + * if you want the ability to configure and persist the new IP configuration + * look at the FS examples, which save the config to file + *************************************************************************************/ + + + +//default custom static IP +//char static_ip[16] = "10.0.1.59"; +//char static_gw[16] = "10.0.1.1"; +//char static_sn[16] = "255.255.255.0"; + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + Serial.println(); + + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + AsyncWiFiManager wifiManager(&server,&dns); + + //reset settings - for testing + //wifiManager.resetSettings(); + + //set static ip + //the commented bit only works for ESP8266 core 2.1.0 or newer + /*IPAddress _ip,_gw,_sn; + _ip.fromString(static_ip); + _gw.fromString(static_gw); + _sn.fromString(static_sn); +*/ + IPAddress _ip = IPAddress(10, 0, 1, 78); + IPAddress _gw = IPAddress(10, 0, 1, 1); + IPAddress _sn = IPAddress(255, 255, 255, 0); + + wifiManager.setSTAStaticIPConfig(_ip, _gw, _sn); + + + //tries to connect to last known settings + //if it does not connect it starts an access point with the specified name + //here "AutoConnectAP" with password "password" + //and goes into a blocking loop awaiting configuration + if (!wifiManager.autoConnect("AutoConnectAP", "password")) { + Serial.println("failed to connect, we should reset as see if it connects"); + delay(3000); + ESP.reset(); + delay(5000); + } + + //if you get here you have connected to the WiFi + Serial.println("connected...yeey :)"); + + + Serial.println("local ip"); + Serial.println(WiFi.localIP()); +} + +void loop() { + // put your main code here, to run repeatedly: + + +} diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithTimeout/AutoConnectWithTimeout.ino b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithTimeout/AutoConnectWithTimeout.ino new file mode 100644 index 0000000..74e321d --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/AutoConnectWithTimeout/AutoConnectWithTimeout.ino @@ -0,0 +1,51 @@ +#if defined(ESP8266) +#include //https://github.com/esp8266/Arduino +#else +#include +#endif + +//needed for library +#include +#include //https://github.com/tzapu/WiFiManager + +AsyncWebServer server(80); +DNSServer dns; + + + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + AsyncWiFiManager wifiManager(&server,&dns); + //reset settings - for testing + //wifiManager.resetSettings(); + + //sets timeout until configuration portal gets turned off + //useful to make it all retry or go to sleep + //in seconds + wifiManager.setTimeout(180); + + //fetches ssid and pass and tries to connect + //if it does not connect it starts an access point with the specified name + //here "AutoConnectAP" + //and goes into a blocking loop awaiting configuration + if(!wifiManager.autoConnect("AutoConnectAP")) { + Serial.println("failed to connect and hit timeout"); + delay(3000); + //reset and try again, or maybe put it to deep sleep + ESP.reset(); + delay(5000); + } + + //if you get here you have connected to the WiFi + Serial.println("connected...yeey :)"); + +} + +void loop() { + // put your main code here, to run repeatedly: + +} diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/ModelessConnect/ModelessConnect.ino b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/ModelessConnect/ModelessConnect.ino new file mode 100644 index 0000000..0bfb225 --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/ModelessConnect/ModelessConnect.ino @@ -0,0 +1,46 @@ +#if defined(ESP8266) +#include //https://github.com/esp8266/Arduino +#else +#include +#endif + +//needed for library +#include +#include //https://github.com/tzapu/WiFiManager + +AsyncWebServer server(80); +DNSServer dns; +AsyncWiFiManager wifiManager(&server,&dns); + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + //reset saved settings + //wifiManager.resetSettings(); + + //set custom ip for portal + //wifiManager.setAPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); + + //fetches ssid and pass from eeprom and tries to connect + //if it does not connect it starts an access point with the specified name + //here "AutoConnectAP" + //and goes into a blocking loop awaiting configuration + //wifiManager.autoConnect("AutoConnectAP"); + //or use this for auto generated name ESP + ChipID + //wifiManager.autoConnect(); + wifiManager.startConfigPortalModeless("ModelessAP", "Password"); + + + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/plain", "Hello World"); + }); + +} + +void loop() { + // put your main code here, to run repeatedly: + wifiManager.loop(); +} diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/ModelessWithInterrupts/ModelessWithInterrupts.ino b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/ModelessWithInterrupts/ModelessWithInterrupts.ino new file mode 100644 index 0000000..5c000e8 --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/ModelessWithInterrupts/ModelessWithInterrupts.ino @@ -0,0 +1,85 @@ +#if defined(ESP8266) +#include //https://github.com/esp8266/Arduino +#else +#include +#endif + +//needed for library +#include +#include //https://github.com/tzapu/WiFiManager + +AsyncWebServer server(80); +DNSServer dns; +AsyncWiFiManager wifiManager(&server,&dns); + +volatile boolean guard = true; +const uint32_t callCycleCount = ESP.getCpuFreqMHz() * 1024 / 8; + +/* + * If some other code was in the process of calling a method in + * WiFi, ESP or EEPROM, and we do too, there is a very good chance + * a reset will happen. + */ +void ICACHE_RAM_ATTR interruptFunction() { + /* + * This is equivalent to: + * timer0_write(ESP.getCycleCount() + ESP.getCpuFreqMHz() * 1024 / 8) + * But that could clash with the main thread code. + */ + uint32_t ccount; + __asm__ __volatile__("esync; rsr %0,ccount":"=a" (ccount)); + + timer0_write(ccount + callCycleCount); + + if (!guard) { + // An example call that would cause a reset if it happens + // to be called while AsyncWiFiManager is also calling + // methods that modify flash. + ESP.getChipId(); + } +} + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + //reset saved settings + //wifiManager.resetSettings(); + + //set custom ip for portal + //wifiManager.setAPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); + + //fetches ssid and pass from eeprom and tries to connect + //if it does not connect it starts an access point with the specified name + //here "AutoConnectAP" + //and goes into a blocking loop awaiting configuration + //wifiManager.autoConnect("AutoConnectAP"); + //or use this for auto generated name ESP + ChipID + //wifiManager.autoConnect(); + wifiManager.startConfigPortalModeless("ModelessAP", "Password"); + + + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/plain", "Hello World"); + }); + + // Set up some code that could cause a reset. + guard = true; + timer0_isr_init(); + timer0_attachInterrupt(interruptFunction); + timer0_write(ESP.getCycleCount() + ESP.getCpuFreqMHz() * 1024); // Wait 2 seconds before displaying +} + +void loop() { + // put your main code here, to run repeatedly: + // This can be called with guarding interrupt routines - so + // interrupt routines can interrupt it! + wifiManager.safeLoop(); + + guard = true; + // This call can not be interrupted without possibly causing a reset. + wifiManager.criticalLoop(); + guard = false; +} diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/OnDemandConfigPortal/OnDemandConfigPortal.ino b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/OnDemandConfigPortal/OnDemandConfigPortal.ino new file mode 100644 index 0000000..171ed6d --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/examples/OnDemandConfigPortal/OnDemandConfigPortal.ino @@ -0,0 +1,67 @@ +#if defined(ESP8266) +#include //https://github.com/esp8266/Arduino +#else +#include +#endif + +//needed for library +#include +#include //https://github.com/tzapu/WiFiManager + + +AsyncWebServer server(80); +DNSServer dns; + +// select wich pin will trigger the configuraton portal when set to LOW +// ESP-01 users please note: the only pins available (0 and 2), are shared +// with the bootloader, so always set them HIGH at power-up +#define TRIGGER_PIN 0 + + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + Serial.println("\n Starting"); + + pinMode(TRIGGER_PIN, INPUT); +} + + +void loop() { + // is configuration portal requested? + if ( digitalRead(TRIGGER_PIN) == LOW ) { + //WiFiManager + //Local intialization. Once its business is done, there is no need to keep it around + AsyncWiFiManager wifiManager(&server,&dns); + + //reset settings - for testing + //wifiManager.resetSettings(); + + //sets timeout until configuration portal gets turned off + //useful to make it all retry or go to sleep + //in seconds + //wifiManager.setTimeout(120); + + //it starts an access point with the specified name + //here "AutoConnectAP" + //and goes into a blocking loop awaiting configuration + + //WITHOUT THIS THE AP DOES NOT SEEM TO WORK PROPERLY WITH SDK 1.5 , update to at least 1.5.1 + //WiFi.mode(WIFI_STA); + + if (!wifiManager.startConfigPortal("OnDemandAP")) { + Serial.println("failed to connect and hit timeout"); + delay(3000); + //reset and try again, or maybe put it to deep sleep + ESP.reset(); + delay(5000); + } + + //if you get here you have connected to the WiFi + Serial.println("connected...yeey :)"); + } + + + // put your main code here, to run repeatedly: + +} diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/keywords.txt b/.piolibdeps/ESPAsyncWifiManager_ID1438/keywords.txt new file mode 100644 index 0000000..af0aa80 --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/keywords.txt @@ -0,0 +1,39 @@ +####################################### +# Syntax Coloring Map For WifiManager +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +AsyncWiFiManager KEYWORD1 +AsyncWiFiManagerParameter KEYWORD1 + + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +autoConnect KEYWORD2 +getSSID KEYWORD2 +getPassword KEYWORD2 +getConfigPortalSSID KEYWORD2 +resetSettings KEYWORD2 +setConfigPortalTimeout KEYWORD2 +setConnectTimeout KEYWORD2 +setDebugOutput KEYWORD2 +setMinimumSignalQuality KEYWORD2 +setAPStaticIPConfig KEYWORD2 +setSTAStaticIPConfig KEYWORD2 +setAPCallback KEYWORD2 +setSaveConfigCallback KEYWORD2 +addParameter KEYWORD2 +getID KEYWORD2 +getValue KEYWORD2 +getPlaceholder KEYWORD2 +getValueLength KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +# LITERAL1 diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/library.json b/.piolibdeps/ESPAsyncWifiManager_ID1438/library.json new file mode 100644 index 0000000..6c4bd22 --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/library.json @@ -0,0 +1,13 @@ +{ + "name": "ESPAsyncWifiManager", + "keywords": "wifi, wi-fi", + "description": "ESP8266 Async WiFi Connection manager with fallback web configuration portal", + "repository": + { + "type": "git", + "url": "https://github.com/alanswx/ESPAsyncWiFiManager.git" + }, + "frameworks": "arduino", + "platforms": ["espressif8266", "espressif32"], + "version": "0.16" +} diff --git a/.piolibdeps/ESPAsyncWifiManager_ID1438/library.properties b/.piolibdeps/ESPAsyncWifiManager_ID1438/library.properties new file mode 100644 index 0000000..611be94 --- /dev/null +++ b/.piolibdeps/ESPAsyncWifiManager_ID1438/library.properties @@ -0,0 +1,9 @@ +name=ESP Async WiFi Manager +version=0.15 +author=alanswx +maintainer=alanswx +sentence=ESP8266 and ESP32 Async WiFi Connection manager with fallback web configuration portal +paragraph=Library for configuring ESP8266 and ESP32 modules WiFi credentials at runtime. +category=Communication +url=https://github.com/alanswx/ESPAsyncWiFiManager.git +architectures=esp8266,esp32 diff --git a/.piolibdeps/LiquidCrystal_I2C_ID576/.library.json b/.piolibdeps/LiquidCrystal_I2C_ID576/.library.json new file mode 100644 index 0000000..6261d1d --- /dev/null +++ b/.piolibdeps/LiquidCrystal_I2C_ID576/.library.json @@ -0,0 +1,45 @@ +{ + "description": "A library for I2C LCD displays.", + "repository": { + "url": "https://github.com/marcoschwartz/LiquidCrystal_I2C", + "type": "git" + }, + "platforms": [ + "atmelavr" + ], + "export": { + "exclude": [ + "extras", + "docs", + "tests", + "test", + "*.doxyfile", + "*.pdf" + ], + "include": null + }, + "authors": [ + { + "maintainer": false, + "name": "Frank de Brabander", + "url": null, + "email": null + }, + { + "maintainer": true, + "name": "Marco Schwartz", + "url": null, + "email": "marcolivier.schwartz@gmail.com" + } + ], + "keywords": [ + "display" + ], + "id": 576, + "name": "LiquidCrystal_I2C", + "frameworks": [ + "arduino" + ], + "version": "1.1.4", + "homepage": null +} \ No newline at end of file diff --git a/.piolibdeps/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.cpp b/.piolibdeps/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.cpp new file mode 100644 index 0000000..f2d37cf --- /dev/null +++ b/.piolibdeps/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.cpp @@ -0,0 +1,332 @@ +// Based on the work by DFRobot + +#include "LiquidCrystal_I2C.h" +#include +#if defined(ARDUINO) && ARDUINO >= 100 + +#include "Arduino.h" + +#define printIIC(args) Wire.write(args) +inline size_t LiquidCrystal_I2C::write(uint8_t value) { + send(value, Rs); + return 1; +} + +#else +#include "WProgram.h" + +#define printIIC(args) Wire.send(args) +inline void LiquidCrystal_I2C::write(uint8_t value) { + send(value, Rs); +} + +#endif +#include "Wire.h" + + + +// When the display powers up, it is configured as follows: +// +// 1. Display clear +// 2. Function set: +// DL = 1; 8-bit interface data +// N = 0; 1-line display +// F = 0; 5x8 dot character font +// 3. Display on/off control: +// D = 0; Display off +// C = 0; Cursor off +// B = 0; Blinking off +// 4. Entry mode set: +// I/D = 1; Increment by 1 +// S = 0; No shift +// +// Note, however, that resetting the Arduino doesn't reset the LCD, so we +// can't assume that its in that state when a sketch starts (and the +// LiquidCrystal constructor is called). + +LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr,uint8_t lcd_cols,uint8_t lcd_rows) +{ + _Addr = lcd_Addr; + _cols = lcd_cols; + _rows = lcd_rows; + _backlightval = LCD_NOBACKLIGHT; +} + +void LiquidCrystal_I2C::oled_init(){ + _oled = true; + init_priv(); +} + +void LiquidCrystal_I2C::init(){ + init_priv(); +} + +void LiquidCrystal_I2C::init_priv() +{ + Wire.begin(); + _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; + begin(_cols, _rows); +} + +void LiquidCrystal_I2C::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { + if (lines > 1) { + _displayfunction |= LCD_2LINE; + } + _numlines = lines; + + // for some 1 line displays you can select a 10 pixel high font + if ((dotsize != 0) && (lines == 1)) { + _displayfunction |= LCD_5x10DOTS; + } + + // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! + // according to datasheet, we need at least 40ms after power rises above 2.7V + // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 + delay(50); + + // Now we pull both RS and R/W low to begin commands + expanderWrite(_backlightval); // reset expanderand turn backlight off (Bit 8 =1) + delay(1000); + + //put the LCD into 4 bit mode + // this is according to the hitachi HD44780 datasheet + // figure 24, pg 46 + + // we start in 8bit mode, try to set 4 bit mode + write4bits(0x03 << 4); + delayMicroseconds(4500); // wait min 4.1ms + + // second try + write4bits(0x03 << 4); + delayMicroseconds(4500); // wait min 4.1ms + + // third go! + write4bits(0x03 << 4); + delayMicroseconds(150); + + // finally, set to 4-bit interface + write4bits(0x02 << 4); + + + // set # lines, font size, etc. + command(LCD_FUNCTIONSET | _displayfunction); + + // turn the display on with no cursor or blinking default + _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; + display(); + + // clear it off + clear(); + + // Initialize to default text direction (for roman languages) + _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; + + // set the entry mode + command(LCD_ENTRYMODESET | _displaymode); + + home(); + +} + +/********** high level commands, for the user! */ +void LiquidCrystal_I2C::clear(){ + command(LCD_CLEARDISPLAY);// clear display, set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! + if (_oled) setCursor(0,0); +} + +void LiquidCrystal_I2C::home(){ + command(LCD_RETURNHOME); // set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + +void LiquidCrystal_I2C::setCursor(uint8_t col, uint8_t row){ + int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; + if ( row > _numlines ) { + row = _numlines-1; // we count rows starting w/0 + } + command(LCD_SETDDRAMADDR | (col + row_offsets[row])); +} + +// Turn the display on/off (quickly) +void LiquidCrystal_I2C::noDisplay() { + _displaycontrol &= ~LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal_I2C::display() { + _displaycontrol |= LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turns the underline cursor on/off +void LiquidCrystal_I2C::noCursor() { + _displaycontrol &= ~LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal_I2C::cursor() { + _displaycontrol |= LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turn on and off the blinking cursor +void LiquidCrystal_I2C::noBlink() { + _displaycontrol &= ~LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal_I2C::blink() { + _displaycontrol |= LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// These commands scroll the display without changing the RAM +void LiquidCrystal_I2C::scrollDisplayLeft(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); +} +void LiquidCrystal_I2C::scrollDisplayRight(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); +} + +// This is for text that flows Left to Right +void LiquidCrystal_I2C::leftToRight(void) { + _displaymode |= LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This is for text that flows Right to Left +void LiquidCrystal_I2C::rightToLeft(void) { + _displaymode &= ~LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'right justify' text from the cursor +void LiquidCrystal_I2C::autoscroll(void) { + _displaymode |= LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'left justify' text from the cursor +void LiquidCrystal_I2C::noAutoscroll(void) { + _displaymode &= ~LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// Allows us to fill the first 8 CGRAM locations +// with custom characters +void LiquidCrystal_I2C::createChar(uint8_t location, uint8_t charmap[]) { + location &= 0x7; // we only have 8 locations 0-7 + command(LCD_SETCGRAMADDR | (location << 3)); + for (int i=0; i<8; i++) { + write(charmap[i]); + } +} + +//createChar with PROGMEM input +void LiquidCrystal_I2C::createChar(uint8_t location, const char *charmap) { + location &= 0x7; // we only have 8 locations 0-7 + command(LCD_SETCGRAMADDR | (location << 3)); + for (int i=0; i<8; i++) { + write(pgm_read_byte_near(charmap++)); + } +} + +// Turn the (optional) backlight off/on +void LiquidCrystal_I2C::noBacklight(void) { + _backlightval=LCD_NOBACKLIGHT; + expanderWrite(0); +} + +void LiquidCrystal_I2C::backlight(void) { + _backlightval=LCD_BACKLIGHT; + expanderWrite(0); +} + + + +/*********** mid level commands, for sending data/cmds */ + +inline void LiquidCrystal_I2C::command(uint8_t value) { + send(value, 0); +} + + +/************ low level data pushing commands **********/ + +// write either command or data +void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) { + uint8_t highnib=value&0xf0; + uint8_t lownib=(value<<4)&0xf0; + write4bits((highnib)|mode); + write4bits((lownib)|mode); +} + +void LiquidCrystal_I2C::write4bits(uint8_t value) { + expanderWrite(value); + pulseEnable(value); +} + +void LiquidCrystal_I2C::expanderWrite(uint8_t _data){ + Wire.beginTransmission(_Addr); + printIIC((int)(_data) | _backlightval); + Wire.endTransmission(); +} + +void LiquidCrystal_I2C::pulseEnable(uint8_t _data){ + expanderWrite(_data | En); // En high + delayMicroseconds(1); // enable pulse must be >450ns + + expanderWrite(_data & ~En); // En low + delayMicroseconds(50); // commands need > 37us to settle +} + + +// Alias functions + +void LiquidCrystal_I2C::cursor_on(){ + cursor(); +} + +void LiquidCrystal_I2C::cursor_off(){ + noCursor(); +} + +void LiquidCrystal_I2C::blink_on(){ + blink(); +} + +void LiquidCrystal_I2C::blink_off(){ + noBlink(); +} + +void LiquidCrystal_I2C::load_custom_character(uint8_t char_num, uint8_t *rows){ + createChar(char_num, rows); +} + +void LiquidCrystal_I2C::setBacklight(uint8_t new_val){ + if(new_val){ + backlight(); // turn backlight on + }else{ + noBacklight(); // turn backlight off + } +} + +void LiquidCrystal_I2C::printstr(const char c[]){ + //This function is not identical to the function used for "real" I2C displays + //it's here so the user sketch doesn't have to be changed + print(c); +} + + +// unsupported API functions +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +void LiquidCrystal_I2C::off(){} +void LiquidCrystal_I2C::on(){} +void LiquidCrystal_I2C::setDelay (int cmdDelay,int charDelay) {} +uint8_t LiquidCrystal_I2C::status(){return 0;} +uint8_t LiquidCrystal_I2C::keypad (){return 0;} +uint8_t LiquidCrystal_I2C::init_bargraph(uint8_t graphtype){return 0;} +void LiquidCrystal_I2C::draw_horizontal_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end){} +void LiquidCrystal_I2C::draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_row_end){} +void LiquidCrystal_I2C::setContrast(uint8_t new_val){} +#pragma GCC diagnostic pop + diff --git a/.piolibdeps/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.h b/.piolibdeps/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.h new file mode 100644 index 0000000..6f98fc2 --- /dev/null +++ b/.piolibdeps/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.h @@ -0,0 +1,131 @@ +//YWROBOT +#ifndef LiquidCrystal_I2C_h +#define LiquidCrystal_I2C_h + +#include +#include "Print.h" +#include + +// commands +#define LCD_CLEARDISPLAY 0x01 +#define LCD_RETURNHOME 0x02 +#define LCD_ENTRYMODESET 0x04 +#define LCD_DISPLAYCONTROL 0x08 +#define LCD_CURSORSHIFT 0x10 +#define LCD_FUNCTIONSET 0x20 +#define LCD_SETCGRAMADDR 0x40 +#define LCD_SETDDRAMADDR 0x80 + +// flags for display entry mode +#define LCD_ENTRYRIGHT 0x00 +#define LCD_ENTRYLEFT 0x02 +#define LCD_ENTRYSHIFTINCREMENT 0x01 +#define LCD_ENTRYSHIFTDECREMENT 0x00 + +// flags for display on/off control +#define LCD_DISPLAYON 0x04 +#define LCD_DISPLAYOFF 0x00 +#define LCD_CURSORON 0x02 +#define LCD_CURSOROFF 0x00 +#define LCD_BLINKON 0x01 +#define LCD_BLINKOFF 0x00 + +// flags for display/cursor shift +#define LCD_DISPLAYMOVE 0x08 +#define LCD_CURSORMOVE 0x00 +#define LCD_MOVERIGHT 0x04 +#define LCD_MOVELEFT 0x00 + +// flags for function set +#define LCD_8BITMODE 0x10 +#define LCD_4BITMODE 0x00 +#define LCD_2LINE 0x08 +#define LCD_1LINE 0x00 +#define LCD_5x10DOTS 0x04 +#define LCD_5x8DOTS 0x00 + +// flags for backlight control +#define LCD_BACKLIGHT 0x08 +#define LCD_NOBACKLIGHT 0x00 + +#define En B00000100 // Enable bit +#define Rw B00000010 // Read/Write bit +#define Rs B00000001 // Register select bit + +class LiquidCrystal_I2C : public Print { +public: + LiquidCrystal_I2C(uint8_t lcd_Addr,uint8_t lcd_cols,uint8_t lcd_rows); + void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS ); + void clear(); + void home(); + void noDisplay(); + void display(); + void noBlink(); + void blink(); + void noCursor(); + void cursor(); + void scrollDisplayLeft(); + void scrollDisplayRight(); + void printLeft(); + void printRight(); + void leftToRight(); + void rightToLeft(); + void shiftIncrement(); + void shiftDecrement(); + void noBacklight(); + void backlight(); + void autoscroll(); + void noAutoscroll(); + void createChar(uint8_t, uint8_t[]); + void createChar(uint8_t location, const char *charmap); + // Example: const char bell[8] PROGMEM = {B00100,B01110,B01110,B01110,B11111,B00000,B00100,B00000}; + + void setCursor(uint8_t, uint8_t); +#if defined(ARDUINO) && ARDUINO >= 100 + virtual size_t write(uint8_t); +#else + virtual void write(uint8_t); +#endif + void command(uint8_t); + void init(); + void oled_init(); + +////compatibility API function aliases +void blink_on(); // alias for blink() +void blink_off(); // alias for noBlink() +void cursor_on(); // alias for cursor() +void cursor_off(); // alias for noCursor() +void setBacklight(uint8_t new_val); // alias for backlight() and nobacklight() +void load_custom_character(uint8_t char_num, uint8_t *rows); // alias for createChar() +void printstr(const char[]); + +////Unsupported API functions (not implemented in this library) +uint8_t status(); +void setContrast(uint8_t new_val); +uint8_t keypad(); +void setDelay(int,int); +void on(); +void off(); +uint8_t init_bargraph(uint8_t graphtype); +void draw_horizontal_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end); +void draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end); + + +private: + void init_priv(); + void send(uint8_t, uint8_t); + void write4bits(uint8_t); + void expanderWrite(uint8_t); + void pulseEnable(uint8_t); + uint8_t _Addr; + uint8_t _displayfunction; + uint8_t _displaycontrol; + uint8_t _displaymode; + uint8_t _numlines; + bool _oled = false; + uint8_t _cols; + uint8_t _rows; + uint8_t _backlightval; +}; + +#endif diff --git a/.piolibdeps/LiquidCrystal_I2C_ID576/README.md b/.piolibdeps/LiquidCrystal_I2C_ID576/README.md new file mode 100644 index 0000000..aee58d3 --- /dev/null +++ b/.piolibdeps/LiquidCrystal_I2C_ID576/README.md @@ -0,0 +1,2 @@ +# LiquidCrystal_I2C +LiquidCrystal Arduino library for the DFRobot I2C LCD displays diff --git a/.piolibdeps/LiquidCrystal_I2C_ID576/examples/CustomChars/CustomChars.pde b/.piolibdeps/LiquidCrystal_I2C_ID576/examples/CustomChars/CustomChars.pde new file mode 100644 index 0000000..e341000 --- /dev/null +++ b/.piolibdeps/LiquidCrystal_I2C_ID576/examples/CustomChars/CustomChars.pde @@ -0,0 +1,70 @@ +//YWROBOT +//Compatible with the Arduino IDE 1.0 +//Library version:1.1 +#include +#include + +#if defined(ARDUINO) && ARDUINO >= 100 +#define printByte(args) write(args); +#else +#define printByte(args) print(args,BYTE); +#endif + +uint8_t bell[8] = {0x4,0xe,0xe,0xe,0x1f,0x0,0x4}; +uint8_t note[8] = {0x2,0x3,0x2,0xe,0x1e,0xc,0x0}; +uint8_t clock[8] = {0x0,0xe,0x15,0x17,0x11,0xe,0x0}; +uint8_t heart[8] = {0x0,0xa,0x1f,0x1f,0xe,0x4,0x0}; +uint8_t duck[8] = {0x0,0xc,0x1d,0xf,0xf,0x6,0x0}; +uint8_t check[8] = {0x0,0x1,0x3,0x16,0x1c,0x8,0x0}; +uint8_t cross[8] = {0x0,0x1b,0xe,0x4,0xe,0x1b,0x0}; +uint8_t retarrow[8] = { 0x1,0x1,0x5,0x9,0x1f,0x8,0x4}; + +LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display + +void setup() +{ + lcd.init(); // initialize the lcd + lcd.backlight(); + + lcd.createChar(0, bell); + lcd.createChar(1, note); + lcd.createChar(2, clock); + lcd.createChar(3, heart); + lcd.createChar(4, duck); + lcd.createChar(5, check); + lcd.createChar(6, cross); + lcd.createChar(7, retarrow); + lcd.home(); + + lcd.print("Hello world..."); + lcd.setCursor(0, 1); + lcd.print(" i "); + lcd.printByte(3); + lcd.print(" arduinos!"); + delay(5000); + displayKeyCodes(); + +} + +// display all keycodes +void displayKeyCodes(void) { + uint8_t i = 0; + while (1) { + lcd.clear(); + lcd.print("Codes 0x"); lcd.print(i, HEX); + lcd.print("-0x"); lcd.print(i+15, HEX); + lcd.setCursor(0, 1); + for (int j=0; j<16; j++) { + lcd.printByte(i+j); + } + i+=16; + + delay(4000); + } +} + +void loop() +{ + +} + diff --git a/.piolibdeps/LiquidCrystal_I2C_ID576/examples/HelloWorld/HelloWorld.pde b/.piolibdeps/LiquidCrystal_I2C_ID576/examples/HelloWorld/HelloWorld.pde new file mode 100644 index 0000000..6dfdee8 --- /dev/null +++ b/.piolibdeps/LiquidCrystal_I2C_ID576/examples/HelloWorld/HelloWorld.pde @@ -0,0 +1,28 @@ +//YWROBOT +//Compatible with the Arduino IDE 1.0 +//Library version:1.1 +#include +#include + +LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display + +void setup() +{ + lcd.init(); // initialize the lcd + lcd.init(); + // Print a message to the LCD. + lcd.backlight(); + lcd.setCursor(3,0); + lcd.print("Hello, world!"); + lcd.setCursor(2,1); + lcd.print("Ywrobot Arduino!"); + lcd.setCursor(0,2); + lcd.print("Arduino LCM IIC 2004"); + lcd.setCursor(2,3); + lcd.print("Power By Ec-yuan!"); +} + + +void loop() +{ +} diff --git a/.piolibdeps/LiquidCrystal_I2C_ID576/examples/SerialDisplay/SerialDisplay.pde b/.piolibdeps/LiquidCrystal_I2C_ID576/examples/SerialDisplay/SerialDisplay.pde new file mode 100644 index 0000000..fdbbc64 --- /dev/null +++ b/.piolibdeps/LiquidCrystal_I2C_ID576/examples/SerialDisplay/SerialDisplay.pde @@ -0,0 +1,34 @@ +/* + * Displays text sent over the serial port (e.g. from the Serial Monitor) on + * an attached LCD. + * YWROBOT + *Compatible with the Arduino IDE 1.0 + *Library version:1.1 + */ +#include +#include + +LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display + +void setup() +{ + lcd.init(); // initialize the lcd + lcd.backlight(); + Serial.begin(9600); +} + +void loop() +{ + // when characters arrive over the serial port... + if (Serial.available()) { + // wait a bit for the entire message to arrive + delay(100); + // clear the screen + lcd.clear(); + // read all the available characters + while (Serial.available() > 0) { + // display each character to the LCD + lcd.write(Serial.read()); + } + } +} diff --git a/.piolibdeps/LiquidCrystal_I2C_ID576/keywords.txt b/.piolibdeps/LiquidCrystal_I2C_ID576/keywords.txt new file mode 100644 index 0000000..8c450a9 --- /dev/null +++ b/.piolibdeps/LiquidCrystal_I2C_ID576/keywords.txt @@ -0,0 +1,46 @@ +########################################### +# Syntax Coloring Map For LiquidCrystal_I2C +########################################### + +########################################### +# Datatypes (KEYWORD1) +########################################### + +LiquidCrystal_I2C KEYWORD1 + +########################################### +# Methods and Functions (KEYWORD2) +########################################### +init KEYWORD2 +begin KEYWORD2 +clear KEYWORD2 +home KEYWORD2 +noDisplay KEYWORD2 +display KEYWORD2 +noBlink KEYWORD2 +blink KEYWORD2 +noCursor KEYWORD2 +cursor KEYWORD2 +scrollDisplayLeft KEYWORD2 +scrollDisplayRight KEYWORD2 +leftToRight KEYWORD2 +rightToLeft KEYWORD2 +shiftIncrement KEYWORD2 +shiftDecrement KEYWORD2 +noBacklight KEYWORD2 +backlight KEYWORD2 +autoscroll KEYWORD2 +noAutoscroll KEYWORD2 +createChar KEYWORD2 +setCursor KEYWORD2 +print KEYWORD2 +blink_on KEYWORD2 +blink_off KEYWORD2 +cursor_on KEYWORD2 +cursor_off KEYWORD2 +setBacklight KEYWORD2 +load_custom_character KEYWORD2 +printstr KEYWORD2 +########################################### +# Constants (LITERAL1) +########################################### diff --git a/.piolibdeps/LiquidCrystal_I2C_ID576/library.json b/.piolibdeps/LiquidCrystal_I2C_ID576/library.json new file mode 100644 index 0000000..4710920 --- /dev/null +++ b/.piolibdeps/LiquidCrystal_I2C_ID576/library.json @@ -0,0 +1,16 @@ +{ + "name": "LiquidCrystal_I2C", + "keywords": "LCD, liquidcrystal, I2C", + "description": "A library for DFRobot I2C LCD displays", + "repository": + { + "type": "git", + "url": "https://github.com/marcoschwartz/LiquidCrystal_I2C.git" + }, + "frameworks": "arduino", + "platforms": + [ + "atmelavr", + "espressif8266" + ] +} diff --git a/.piolibdeps/LiquidCrystal_I2C_ID576/library.properties b/.piolibdeps/LiquidCrystal_I2C_ID576/library.properties new file mode 100644 index 0000000..9764810 --- /dev/null +++ b/.piolibdeps/LiquidCrystal_I2C_ID576/library.properties @@ -0,0 +1,9 @@ +name=LiquidCrystal_I2C +version=1.1.4 +author=Frank de Brabander +maintainer=Marco Schwartz +sentence=A library for I2C LCD displays. +paragraph= The library allows to control I2C displays with functions extremely similar to LiquidCrystal library. THIS LIBRARY MIGHT NOT BE COMPATIBLE WITH EXISTING SKETCHES. +category=Display +url=https://github.com/marcoschwartz/LiquidCrystal_I2C +architectures=avr diff --git a/.piolibdeps/TaskScheduler_ID721/.library.json b/.piolibdeps/TaskScheduler_ID721/.library.json new file mode 100644 index 0000000..b3c557c --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/.library.json @@ -0,0 +1,42 @@ +{ + "name": "TaskScheduler", + "repository": { + "url": "https://github.com/arkhipenko/TaskScheduler", + "type": "git" + }, + "platforms": [ + "atmelavr", + "atmelsam", + "espressif32", + "espressif8266", + "intel_arc32", + "microchippic32", + "nordicnrf51", + "nordicnrf52", + "ststm32", + "teensy", + "timsp430" + ], + "frameworks": [ + "arduino" + ], + "version": "2.6.1", + "authors": [ + { + "maintainer": true, + "name": "Anatoli Arkhipenko", + "url": "https://github.com/arkhipenko", + "email": "arkhipenko@hotmail.com" + } + ], + "keywords": [ + "taskscheduler", + "scheduling", + "task", + "cooperative", + "multitasking", + "event" + ], + "id": 721, + "description": "Cooperative multitasking for Arduino and ESP8266 microcontrollers" +} \ No newline at end of file diff --git a/.piolibdeps/TaskScheduler_ID721/LICENSE.txt b/.piolibdeps/TaskScheduler_ID721/LICENSE.txt new file mode 100644 index 0000000..71a431c --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright (c) 2015, Anatoli Arkhipenko. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of the copyright holder 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. diff --git a/.piolibdeps/TaskScheduler_ID721/README b/.piolibdeps/TaskScheduler_ID721/README new file mode 100644 index 0000000..fe4f039 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/README @@ -0,0 +1,205 @@ +Task Scheduler – cooperative multitasking for Arduino and ESP8266 microcontrollers +Version 2.6.1 2018-03-15 + +If you find TaskScheduler useful for your Arduino project, please drop me an email: arkhipenko@hotmail.com +---------------------------------------------------------------------------------------------------------- + +OVERVIEW: + A lightweight implementation of cooperative multitasking (task scheduling) supporting: + 1. Periodic task execution (with dynamic execution period in milliseconds or microseconds) + 2. Number of iterations (n times) + 3. Execution of tasks in predefined sequence + 4. Dynamic change of task execution parameters (frequency, number of iterations, callback function) + 5. Power saving via entering IDLE sleep mode between tasks are scheduled to run + 6. Task invocation via Status Request object + 7. Task IDs and Control Points for error handling and watchdog timer + 8. Local Task Storage pointer (allowing use of same callback code for multiple tasks) + 9. Layered task prioritization +10. Support for std::functions (ESP8266 only) +11. Overall task timeout + +Scheduling overhead: between 15 and 18 microseconds per scheduling pass (check the benchmark example). + +Tested on the following microcontrollers: + - Arduino Uno R3 + - Arduino Nano + - Arduino Micro + - ATtiny85 + - ESP8266 (Node MCU v2.0) + - ESP32 + - Teensy (tested on Teensy 3.5) + +For detailed functionality overview please refer to TaskScheduler documentation in the 'extras' folder. +======================================================================================================= + +Check out what TaskScheduler can do: +==================================== + Endeavor - build a space capable craft, with the ability to maneuver both in and out of atmosphere. + (by Radical Space Technologies + http://radicalspacetechnologies.com/2015/10/01/endeavor-phase-i/ + + "So after some digging I found TaskScheduler an awesome little library developed by Anatoli Arkhipenko, + his github is here and the documentation is here. Its fantastic, so far I’ve been able to replicate + some things I’ve seen in Ardupilot. ..." + link: http://radicalspacetechnologies.com/2015/10/25/endeavors-code-definitely-progress/ + + + 3 Devo - Quality 3D printing filament, now made accessible and affordable + http://3devo.eu/ (http://3devo.eu/license-information/) + + + Houston midi clock project - TaskScheduler with microseconds resolution + (by chaffneue: My first arduino project. It's a multi-master midi controller with a shared clock and + auto count in behaviour. Project files on github: https://github.com/chaffneue/houston + youtube: https://www.youtube.com/watch?v=QRof550TtXo) + + + Hackabot Nano - Compact Plug and Play Arduino compatible robotic kit + (by Funnyvale: http://hackarobot.com/ + also: https://www.kickstarter.com/projects/hackarobot/hackabot-nano-compact-plug-and-play-arduino-robot) + + + Arduino Nano based Hexbug Scarab Robotic Spider + (by arkhipenko: http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider/) + + + Wave your hand to control OWI Robotic Arm... no strings attached + (by arkhipenko: http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/) + + + APIS - Automated Plant Irrigation System + (by arkhipenko: http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/) + + + IoT APIS v2 - Autonomous IoT-enabled Automated Plant Irrigation System + (by arkhipenko: http://www.instructables.com/id/IoT-APIS-V2-Autonomous-IoT-enabled-Automated-Plant/) + + + Interactive Halloween Pumpkin + (by arkhipenko: http://www.instructables.com/id/Interactive-Halloween-Pumpkin/) + + +Changelog: +========= +v2.6.1: + 2018-02-13 - Bug: Support for task self-destruction in the OnDisable method. + Example 19: dynamic task creation and destruction. + 2018-03-14 - Bug: high level scheduler ignored if lower level chain is empty + Example 20: use of local task storage to work with task-specific class objects + +v2.6.0: + 2018-01-30 - _TASK_TIMEOUT compilation directive: Task overall timeout functionality + 2018-01-30 - Support for ESP32 + (Contributed by Marco Tombesi: https://github.com/baggior) + +v2.5.2: + 2018-01-09 - _TASK_INLINE compilation directive making all methods declared "inline" (issue #42) + + +v2.5.1: + 2018-01-06 - support for IDLE sleep on Teensy boards (tested on Teensy 3.6) + +v2.5.0: + 2017-04-27 - added optional support for std::functions via _TASK_STD_FUNCTION + (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder) + 2017-08-30 - add _TASK_DEBUG making all methods and variables public FOR DEBUGGING PURPOSES ONLY! + *** USE AT YOUR OWN RISK *** + 2017-08-30 - bug fix: Scheduler::addTask() checks if task is already part of an execution chain (github issue #37) + 2017-08-30 - support for multi-tab sketches (Contributed by Adam Ryczkowski - https://github.com/adamryczkowski) + +v2.4.0: + 2017-04-27 - added destructor to the Task class to ensure tasks are disables and taken off the execution chain + upon destruction. (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder) + +v2.3.0: + 2017-02-24 - new timeUntilNextIteration() method within Scheduler class - inquire when a particlar task is + scheduled to run next time + +v2.2.1: + 2016-11-30 - inlined constructors. Added "yield()" and "yieldOnce()" functions to easily break down and + chain back together long running callback methods + 2016-12-16 - added "getCount()" to StatusRequest objects, made every task StatusRequest enabled. + Internal StatusRequest objects are accessible via "getInternalStatusRequest()" method. + +v2.2.0: + 2016-11-17 - all methods made 'inline' to support inclusion of TaskSchedule.h file into other header files + +v2.1.0: + 2016-02-01 - support for microsecond resolution + 2016-02-02 - added Scheduler baseline start time reset method: startNow() + +v2.0.1: + 2016-01-02 - bug fix: issue#11 Xtensa compiler (esp8266): Declaration of constructor does not match implementation + +v2.0.0: + 2015-12-22 - _TASK_PRIORITY - support for layered task prioritization + +v1.9.2: + 2015-11-28 - _TASK_ROLLOVER_FIX is deprecated (not necessary) + 2015-12-16 - bug fixes: automatic millis rollover support for delay methods + 2015-12-17 - new method for _TASK_TIMECRITICAL option: getStartDelay() + +v1.9.0: + 2015-11-24 - packed three byte-long status variables into one byte-long bit array structure data type - saving 2 bytes per each task instance + +v1.8.5: + 2015-11-23 - bug fix: incorrect calculation of next task invocation in case callback changed the interval + 2015-11-23 - bug fix: Task::set() method calls setInterval() explicitly, therefore delaying the task in the same manner + +v1.8.4: + 2015-11-15 - bug fix: Task alignment with millis() for scheduling purposes should be done after OnEnable, not before. Especially since OnEnable method can change the interval + +v1.8.3: + 2015-11-05 - support for task activation on a status request with arbitrary interval and number of iterations (0 and 1 are still default values) + 2015-11-05 - implement waitForDelayed() method to allow task activation on the status request completion delayed for one current interval + 2015-11-09 - added callback methods prototypes to all examples for Arduino IDE 1.6.6 compatibility + 2015-11-14 - added several constants to be used as task parameters for readability (e.g, TASK_FOREVER, TASK_SECOND, etc.) + 2015-11-14 - significant optimization of the scheduler's execute loop, including millis() rollover fix option + +v1.8.2: + 2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks) + 2015-10-27 - bug: currentTask() method returns incorrect Task reference if called within OnEnable and OnDisable methods + 2015-10-27 - protection against infinite loop in OnEnable (if enable() methods are called within OnEnable) + 2015-10-29 - new currentLts() method in the scheduler class returns current task's LTS pointer in one call + +v1.8.1: + 2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging + +v1.8.0: + 2015-10-13 - support for status request objects allowing tasks waiting on requests + 2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch + +v1.7.0: + 2015-10-08 - introduced callback run counter - callback functions can branch on the iteration number. + 2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled, false if was disabled. + 2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled) + 2015-10-11 - introduced callback functions "on enable" and "on disable". On enable runs every time enable is called, on disable runs only if task was enabled + 2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass regardless how much time is left + +v1.6.0: + 2015-09-22 - revert back to having all tasks disable on last iteration. + 2015-09-22 - deprecated disableOnLastIteration method as a result + 2015-10-01 - made version numbers semver compliant (documentation only) + +v1.5.1: + 2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations(). + Thanks to Hannes Morgenstern for catching this one + +v1.5.0: + 2015-09-20 - access to currently executing task (for callback functions) + 2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain + 2015-09-20 - option to create a task already enabled + +v1.4.1: + 2015-09-15 - more careful placement of AVR-specific includes for sleep functions (compatibility with DUE) + sleep on idle run is no longer a default and should be explicitly compiled with _TASK_SLEEP_ON_IDLE_RUN defined + +v1.0.0: + 2015-02-24 - Initial release + 2015-02-28 - added delay() and disableOnLastIteration() functions + 2015-03-25 - changed scheduler execute() function for a more precise delay calculation: + 1. Do not delay if any of the tasks ran (making request for immediate execution redundant) + 2. Delay is invoked only if none of the tasks ran + 3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute function itself. + 2015-05-11 - added restart() and restartDelayed() functions to restart tasks which are on hold after running all iterations + 2015-05-19 - completely removed delay from the scheduler since there are no power saving there. using 1 ms sleep instead diff --git a/.piolibdeps/TaskScheduler_ID721/README.md b/.piolibdeps/TaskScheduler_ID721/README.md new file mode 100644 index 0000000..9448ca4 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/README.md @@ -0,0 +1,85 @@ +# Task Scheduler +### Cooperative multitasking for Arduino microcontrollers +#### Version 2.6.1: 2018-03-15 + +### OVERVIEW: +A lightweight implementation of cooperative multitasking (task scheduling) supporting: +1. Periodic task execution, with dynamic execution period in `milliseconds` (default) or `microseconds` (if explicitly enabled) – frequency of execution +2. Number of iterations (limited or infinite number of iterations) +3. Execution of tasks in predefined sequence +4. Dynamic change of task execution parameters (frequency, number of iterations, callback methods) +5. Power saving via entering **IDLE** sleep mode when tasks are not scheduled to run +6. Support for event-driven task invocation via Status Request object +7. Support for task IDs and Control Points for error handling and watchdog timer +8. Support for Local Task Storage pointer (allowing use of same callback code for multiple tasks) +9. Support for layered task prioritization +10. Support for `std::functions` (`ESP8266` only) +11. Overall task timeout + +Scheduling overhead: between `15` and `18` microseconds per scheduling pass (Arduino UNO rev 3 @ `16MHz` clock, single scheduler w/o prioritization) + +**TaskScheduler** was tested on the following platforms: +* Arduino Uno R3 +* Arduino Nano +* Arduino Micro +* ATtiny85 +* ESP8266 (Node MCU v2.0) +* ESP32 +* Teensy (tested on Teensy 3.5) +--- +### [Changelog:](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog) +Version|Version 1|Version 2 +---|---|--- +|| |[2.6.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v261) (*current version*) +||[1.9.2](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v192)|[2.6.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v260) +||[1.9.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v190)|[2.5.2](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v252) +||[1.8.5](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v185)|[2.5.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v251) +||[1.8.4](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v184)|[2.5.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v250) +||[1.8.3](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v183)|[2.4.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v240) +||[1.8.2](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v182)|[2.3.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v230) +||[1.8.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v181)|[2.2.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v221) +||[1.8.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v180)|[2.2.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v220) +||[1.7.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v170)|[2.1.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v210) +||[1.6.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v160)|[2.0.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v201) +||[1.5.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v151)|[2.0.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v200) +||[1.5.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v150)| +||[1.4.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v141)| +||[1.0.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v100)| + +#### For detailed functionality overview please refer to TaskScheduler documentation in the 'extras' folder or in the [Wiki page](https://github.com/arkhipenko/TaskScheduler/wiki). + +### Check out what TaskScheduler can do: + +* [3 Devo](http://3devo.eu/) - Quality 3D printing filament, now made accessible and affordable +(http://3devo.eu/license-information/) + + +* [Houston midi](https://github.com/chaffneue/houston) clock project - TaskScheduler with microseconds resolution + >by chaffneue: + >>My first arduino project. It's a multi-master midi controller with a shared clock and + auto count in behaviour. + + youtube: https://www.youtube.com/watch?v=QRof550TtXo + + +* [Hackabot Nano](http://hackarobot.com/) by Funnyvale - Compact Plug and Play Arduino compatible robotic kit + https://www.kickstarter.com/projects/hackarobot/hackabot-nano-compact-plug-and-play-arduino-robot + + +* Arduino Nano based Hexbug Scarab Robotic Spider + (by arkhipenko: http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider/) + +* Wave your hand to control OWI Robotic Arm... no strings attached + (by arkhipenko: http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/) + + +* APIS - Automated Plant Irrigation System + (by arkhipenko: http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/) + + +* IoT APIS v2 - Autonomous IoT-enabled Automated Plant Irrigation System + (by arkhipenko: http://www.instructables.com/id/IoT-APIS-V2-Autonomous-IoT-enabled-Automated-Plant/) + +* Interactive Halloween Pumpkin + (by arkhipenko: http://www.instructables.com/id/Interactive-Halloween-Pumpkin/) + diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example01/Scheduler_example01.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example01/Scheduler_example01.ino new file mode 100644 index 0000000..2175e57 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example01/Scheduler_example01.ino @@ -0,0 +1,84 @@ +/** + * TaskScheduler Test + * + * Initially only tasks 1 and 2 are enabled + * Task1 runs every 2 seconds 10 times and then stops + * Task2 runs every 3 seconds indefinitely + * Task1 enables Task3 at its first run + * Task3 run every 5 seconds + * Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds + * At the end Task2 is the only task running every 1/2 seconds + */ + + +#include + +// Callback methods prototypes +void t1Callback(); +void t2Callback(); +void t3Callback(); + +//Tasks +Task t4(); +Task t1(2000, 10, &t1Callback); +Task t2(3000, TASK_FOREVER, &t2Callback); +Task t3(5000, TASK_FOREVER, &t3Callback); + +Scheduler runner; + + +void t1Callback() { + Serial.print("t1: "); + Serial.println(millis()); + + if (t1.isFirstIteration()) { + runner.addTask(t3); + t3.enable(); + Serial.println("t1: enabled t3 and added to the chain"); + } + + if (t1.isLastIteration()) { + t3.disable(); + runner.deleteTask(t3); + t2.setInterval(500); + Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); + } +} + +void t2Callback() { + Serial.print("t2: "); + Serial.println(millis()); + +} + +void t3Callback() { + Serial.print("t3: "); + Serial.println(millis()); + +} + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler TEST"); + + runner.init(); + Serial.println("Initialized scheduler"); + + runner.addTask(t1); + Serial.println("added t1"); + + runner.addTask(t2); + Serial.println("added t2"); + + delay(5000); + + t1.enable(); + Serial.println("Enabled t1"); + t2.enable(); + Serial.println("Enabled t2"); +} + + +void loop () { + runner.execute(); +} \ No newline at end of file diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example02/Scheduler_example02.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example02/Scheduler_example02.ino new file mode 100644 index 0000000..377f9a8 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example02/Scheduler_example02.ino @@ -0,0 +1,79 @@ +#define _TASK_SLEEP_ON_IDLE_RUN +#include + +Scheduler runner; +// Callback methods prototypes +void t1Callback(); +void t2Callback(); +void t3Callback(); + +// Tasks +Task t4(); +Task t1(2000, 10, &t1Callback, &runner, true); //adding task to the chain on creation +Task t2(3000, TASK_FOREVER, &t2Callback, &runner, true); //adding task to the chain on creation +Task t3(5000, TASK_FOREVER, &t3Callback); + +// Test +// Initially only tasks 1 and 2 are enabled +// Task1 runs every 2 seconds 10 times and then stops +// Task2 runs every 3 seconds indefinitely +// Task1 enables Task3 at its first run +// Task3 run every 5 seconds +// loop() runs every 1 second (a default scheduler delay, if no shorter tasks' interval is detected) +// Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds +// Because Task2 interval is shorter than Scheduler default tick, loop() executes ecery 1/2 seconds now +// At the end Task2 is the only task running every 1/2 seconds +// +// NOTE that t1 and t2 are affected by the delay() function in the setup() method and are scheduled immediately twice to "catch up" with millis(). + + + + + +void t1Callback() { + Serial.print("t1: "); + Serial.println(millis()); + + if (t1.isFirstIteration()) { + runner.addTask(t3); + t3.enable(); + Serial.println("t1: enabled t3 and added to the chain"); + } + + if (t1.isLastIteration()) { + t3.disable(); + runner.deleteTask(t3); + t2.setInterval(500); + Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); + } +} + +void t2Callback() { + Serial.print("t2: "); + Serial.println(millis()); + +} + +void t3Callback() { + Serial.print("t3: "); + Serial.println(millis()); + +} + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler TEST"); + + delay(5000); + + runner.startNow(); // set point-in-time for scheduling start +} + + +void loop () { + + runner.execute(); + +// Serial.println("Loop ticks at: "); +// Serial.println(millis()); +} \ No newline at end of file diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example03/Scheduler_example03.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example03/Scheduler_example03.ino new file mode 100644 index 0000000..4c371e0 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example03/Scheduler_example03.ino @@ -0,0 +1,80 @@ +/** + * TaskScheduler Test of OnEnable and OnDisable methods and illustration of using wrapper tasks for timout purposes + * + * A wrapper task runs every 10 seconds and initiates the test case + * Another task is run once for 5 seconds, and serves as a LED blinking timeout - 5 seconds + * Finally, a dedicated task which controls LED is running periodically until stopped, and makes the LED blink with 0.5 to 1 second interval. + * + */ + +#define _TASK_SLEEP_ON_IDLE_RUN +#include + +#ifndef LED_BUILTIN +#define LED_BUILTIN 13 // define appropriate pin for your board +#endif + +Scheduler ts; + +// Callback methods prototypes +void WrapperCallback(); +bool BlinkOnEnable(); +void BlinkOnDisable(); +void LEDOn(); +void LEDOff(); + +// Tasks +Task tWrapper(10000L, TASK_FOREVER, &WrapperCallback, &ts, true); +Task tBlink(5000, TASK_ONCE, NULL, &ts, false, &BlinkOnEnable, &BlinkOnDisable); +Task tLED(0, TASK_FOREVER, NULL, &ts, false, NULL, &LEDOff); + +void WrapperCallback() { + tBlink.restartDelayed(); // LED blinking is initiated + //every 30 seconds for 5 seconds +} + + +// Upon being enabled, tBlink will define the parameters +// and enable LED blinking task, which actually controls +// the hardware (LED in this example) +bool BlinkOnEnable() { + tLED.setInterval( 500 + random(501) ); + tLED.setCallback( &LEDOn); + tLED.enable(); + + return true; // Task should be enabled +} + +// tBlink does not really need a callback function +// since it just waits for 5 seconds for the first +// and only iteration to occur. Once the iteration +// takes place, tBlink is disabled by the Scheduler, +// thus executing its OnDisable method below. + +void BlinkOnDisable() { + tLED.disable(); +} + +void LEDOn () { + digitalWrite(LED_BUILTIN , HIGH); + tLED.setCallback( &LEDOff); +} + +void LEDOff () { + digitalWrite(LED_BUILTIN , LOW); + tLED.setCallback( &LEDOn); +} + +// Note that LEDOff method serves as OnDisable method +// to make sure the LED is turned off when the tBlink +// task finishes (or disabled ahead of time) + +void setup() { +// put your setup code here, to run once: + pinMode(LED_BUILTIN , OUTPUT); +} + +void loop() { +// put your main code here, to run repeatedly: + ts.execute(); +} \ No newline at end of file diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example04_StatusRequest/Scheduler_example04_StatusRequest.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example04_StatusRequest/Scheduler_example04_StatusRequest.ino new file mode 100644 index 0000000..139de4d --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example04_StatusRequest/Scheduler_example04_StatusRequest.ino @@ -0,0 +1,88 @@ +/** This test demonstrates interaction between three simple tasks via StatusRequest object. + * Task T1 runs every 5 seconds and signals completion of a status request st. + * Tasks T2 and T3 are waiting on the same request (st) + * Task T3 does not renew its interest in status request st, so it is only invoked once (first iteration) + * Task T2 is invoked every time st completes, because it renews its interest in status of status request object st every iteration of T1 + */ + +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_STATUS_REQUEST +#include + +StatusRequest st; + +Scheduler ts; + +// Callback methods prototypes +void Callback1(); +void Disable1(); +void Callback2(); +void Callback3(); +void PrepareStatus(); + +// Tasks +Task t1(5000, TASK_ONCE, &Callback1, &ts, true, NULL, &Disable1); +Task t2(&Callback2, &ts); +Task t3(&Callback3, &ts); + +/** T1 callback + * T1 just signals completion of st every 5 seconds + */ +void Callback1() { + Serial.println("T1: Signaling completion of ST"); + st.signalComplete(); +} + +/** T1 On Disable callback + * This callback renews the status request and restarts T1 delayed to run again in 5 seconds + */ +void Disable1() { + PrepareStatus(); + t1.restartDelayed(); +} + +/** T2 callback + * Invoked when status request st completes + */ +void Callback2() { + Serial.println("T2: Invoked due to completion of ST"); +} + + +/** T3 callback + * Invoked when status request st completes. + * This is only run once since T3 does not renew its interest in the status request st after first iteration + */ + void Callback3() { + Serial.println("T3: Invoked due to completion of ST"); + +} + +/** Prepare Status request st for another iteration + * + */ +void PrepareStatus() { + st.setWaiting(); // set the statusrequest object for waiting + t2.waitFor(&st); // request tasks 1 & 2 to wait on the object st +} + + +/** Main Arduino code + * Not much to do here. Just init Serial and set the initial status request + */ +void setup() { + + Serial.begin(115200); + Serial.println("TaskScheduler: Status Request Test 1. Simple Test."); + + PrepareStatus(); + t3.waitFor(&st); + + t1.delay(); +} + +void loop() { + + ts.execute(); + +} \ No newline at end of file diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example05_StatusRequest/Scheduler_example05_StatusRequest.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example05_StatusRequest/Scheduler_example05_StatusRequest.ino new file mode 100644 index 0000000..6c1e950 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example05_StatusRequest/Scheduler_example05_StatusRequest.ino @@ -0,0 +1,172 @@ +/** This test emulates querying 3 sensors once every 10 seconds, each could respond with a different delay + * (ultrasonic sensors for instance) and printing a min value of the three when all three have reported their values. + * The overall timeout of 1 second is setup as well. + * An error message needs to be printed if a timeout occurred instead of a value. + */ + + +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_STATUS_REQUEST +#include + +StatusRequest measure; + +Scheduler ts; + +// Callback methods prototypes +void CycleCallback(); +void MeasureCallback(); +bool MeasureEnable(); +void MeasureDisable(); +void CalcCallback(); +void S1Callback(); bool S1Enable(); +void S2Callback(); bool S2Enable(); +void S3Callback(); bool S3Enable(); + +// Tasks +Task tCycle(10000, TASK_FOREVER, &CycleCallback, &ts, true); +Task tMeasure(1000, TASK_ONCE, &MeasureCallback, &ts, false, &MeasureEnable, &MeasureDisable); +Task tCalculate(&CalcCallback, &ts); +Task tSensor1(0, TASK_ONCE, &S1Callback, &ts, false, &S1Enable); +Task tSensor2(0, TASK_ONCE, &S2Callback, &ts, false, &S2Enable); +Task tSensor3(0, TASK_ONCE, &S3Callback, &ts, false, &S3Enable); + + +long distance, d1, d2, d3; + +void CycleCallback() { + Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); + + tMeasure.restartDelayed(); +} + + + +bool MeasureEnable() { + Serial.println("MeasureEnable: Activating sensors"); + + distance = 0; + measure.setWaiting(3); // Set the StatusRequest to wait for 3 signals. + tCalculate.waitFor(&measure); + + tSensor1.restartDelayed(); + tSensor2.restartDelayed(); + tSensor3.restartDelayed(); + + return true; +} + +void MeasureCallback() { + Serial.println("MeasureCallback: Invoked by calculate task or one second later"); + + if (measure.pending()) { + tCalculate.disable(); + measure.signalComplete(-1); // signal error + Serial.println("MeasureCallback: Timeout!"); + } + else { + Serial.print("MeasureCallback: Min distance=");Serial.println(distance); + } +} + +void MeasureDisable() { + Serial.println("MeasureDisable: Cleaning up"); + + tSensor1.disable(); + tSensor2.disable(); + tSensor3.disable(); +} + + +void CalcCallback() { + Serial.println("CalcCallback: calculating"); + distance = -1; + if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully + distance = d1 < d2 ? d1 : d2; + distance = d3 < distance ? d3 : distance; + tMeasure.forceNextIteration(); + } +} + + +/** Simulation code for sensor 1 + * ---------------------------- + */ +bool S1Enable() { + Serial.print("S1Enable: Triggering sensor1. Delay="); + + tSensor1.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout + d1 = 0; + + Serial.println( tSensor1.getInterval() ); + return true; +} + +void S1Callback() { + Serial.print("S1Callback: Emulating measurement. d1="); + d1 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement + measure.signal(); + + Serial.println(d1); +} + + +/** Simulation code for sensor 2 + * ---------------------------- + */ +bool S2Enable() { + Serial.print("S2Enable: Triggering sensor2. Delay="); + + tSensor2.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout + d2 = 0; + + Serial.println( tSensor2.getInterval() ); + return true; +} + +void S2Callback() { + Serial.print("S2Callback: Emulating measurement. d2="); + d2 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement + measure.signal(); + + Serial.println(d2); +} + + +/** Simulation code for sensor 3 + * ---------------------------- + */ +bool S3Enable() { + Serial.print("S3Enable: Triggering sensor3. Delay="); + + tSensor3.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout + d3 = 0; + + Serial.println( tSensor3.getInterval() ); + return true; +} + +void S3Callback() { + Serial.print("S3Callback: Emulating measurement. d3="); + d3 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement + measure.signal(); + + Serial.println(d3); +} + + +/** Main Arduino code + * Not much is left here - everything is taken care of by the framework + */ +void setup() { + + Serial.begin(115200); + Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); + randomSeed(analogRead(A0)+millis()); +} + +void loop() { + + ts.execute(); + +} diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example06_IDLE/Scheduler_example06_IDLE.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example06_IDLE/Scheduler_example06_IDLE.ino new file mode 100644 index 0000000..310bb50 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example06_IDLE/Scheduler_example06_IDLE.ino @@ -0,0 +1,93 @@ + +/** + * This is a test to prove that processor really goes into IDLE sleep. + * For this setup: + * + * + +Task c(10, -1, &Count, &ts); +Task t(10000, 1, NULL, &ts, true, &tOn, &tOff); + +The result are: + +1): With #define _TASK_SLEEP_ON_IDLE_RUN enabled +On Arduino Uno: + Start + c1=10771 - v2.5.0 (v1.9.0: same) + c2=1001 + +On Teensy 3.5 (120MHz ARM): + Start + c1=21065 + c2=1001 + +and + +2): With #define _TASK_SLEEP_ON_IDLE_RUN disabled (commented out) +Arduino Uno: + Start + c1=635735 - v2.5.0 (v1.9.0: 551947) + c2=1001 + +On Teensy 3.5 (120MHz ARM): + Start + c1=2690322 + c2=1001 + +C1 is scenario 2) is much higher than in scenario 1) because processor is put to sleep for 1), but not for 2) + + */ + + +/** + * Compile and run once with _TASK_SLEEP_ON_IDLE_RUN enabled, then with _TASK_SLEEP_ON_IDLE_RUN disabled. + * Compare the results. + */ + +//#define _TASK_SLEEP_ON_IDLE_RUN +#include + +Scheduler ts; + +// Callback methods prototypes +void Count(); +bool tOn(); void tOff(); + +// Tasks +Task c(10, TASK_FOREVER, &Count, &ts); +Task t(10000, TASK_ONCE, NULL, &ts, true, &tOn, &tOff); + + +volatile unsigned long c1, c2; +bool tOn() { + c1 = 0; + c2 = 0; + c.enable(); + + return true; +} + +void tOff() { + c.disable(); + Serial.print("c1=");Serial.println(c1); + Serial.print("c2=");Serial.println(c2); +} + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + Serial.println("Start"); + t.delay(); + // ts.allowSleep(false); +} + +void Count() { + c2++; +} + + +void loop() { + // put your main code here, to run repeatedly: + ts.execute(); + c1++; +} \ No newline at end of file diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example07_WDT/Scheduler_example07_WDT.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example07_WDT/Scheduler_example07_WDT.ino new file mode 100644 index 0000000..60983bd --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example07_WDT/Scheduler_example07_WDT.ino @@ -0,0 +1,132 @@ +/** + * TaskScheduler Test sketch - use of task IDs and watchdog timer to identify hung tasks + * Test case: + * Watchdog timer is set to 2 seconds (interrupt + reset) + * A hearbeat task (resetting the watchdog timer) is scheduled with 500 ms interval + * A number of tasks are running every 1 second and "rolling the dice" 0..19. If 5, task is made to enter infinite loop + * Device should reset in 2 seconds after a task enters infinite loop + * A task id and a control point number are saved to EEPROM prior to device reset, and are displayed after reboot. + * In real life, device might chose to NOT activate certain tasks which failed previously (failed sensors for instance) + */ + +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_WDT_IDS +#include + +#include +#include + +Scheduler ts; + +// Callback methods prototypes +void TaskCB(); +void HB(); bool HBOn(); void HBOff(); + +// Three tasks emulating accidental infinite loop +Task tTask1(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true); +Task tTask2(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true); +Task tTask3(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true); + +// Heartbeat task - resetting the watchdog timer periodically +// Initiates WDT on enable, and deactivates it on disable +Task tHB(500, TASK_FOREVER, &HB, &ts, false, &HBOn, &HBOff); + +/** + * Emulating task callback function + * Prints task id and randomly "hangs" in two places. + * Control points are stored on the task prior to section which might hang, + * making this information available to the WDT interrupt handler + */ +void TaskCB() { + Task& T = ts.currentTask(); + + Serial.print("Task #:"); + Serial.print(T.getId()); + Serial.print(" current iteration = "); + Serial.println(T.getRunCounter()); + +// Hang if random number between 0 and 19 is 5 (5% probability) + T.setControlPoint(10); + if (random(20) == 5) for(;;); + +// Hang if random number between 0 and 99 is more that 95 (5% probability) + T.setControlPoint(95); + if (random(100) > 94) for(;;); +} + +/** + * This On Enable method sets up the WDT + * for interrupt and reset after 2 seconds + */ +bool HBOn() { + + //disable interrupts + cli(); + //reset watchdog + wdt_reset(); + //set up WDT interrupt + WDTCSR = (1<id); + Serial.print("Sum: "); Serial.println(vars[i]->sum); + Serial.print("Product: "); Serial.println(vars[i]->product); + Serial.println(); + } +} + + +/** + * This method is executed when each calculator task is enabled + * The purpose is to initiate all local variables + */ +bool CalcOn() { + Task& T = ts.currentTask(); + task_var& var = *((task_var*) T.getLtsPointer()); + +// Initialize local variables + var.id = T.getId(); + var.sum = 0; + var.product = var.id; + + return true; +} + + +/** + * This method performs simple calculations on task's local variables + */ +void Calculate() { + Task& T = ts.currentTask(); +// Another way to get to LTS pointer: + task_var& var = *((task_var*) ts.currentLts()); + + + Serial.print("Calculating for task: "); + Serial.print(T.getId()); + Serial.print("; Task id per LTS is: "); + Serial.println( var.id ); + + var.sum += T.getId(); + var.product = var.product * 10; + +} + + +/** + * Standard Arduino setup and loop methods + */ +void setup() { + Serial.begin(115200); + + randomSeed(analogRead(0)+analogRead(5)); + + pinMode(13, OUTPUT); + digitalWrite(13, LOW); + + Serial.println("Local Task Storage pointer test"); + + tWrapper.enableDelayed(); +} + +void loop() { + ts.execute(); +} \ No newline at end of file diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example09_TimeCritical/Scheduler_example09_TimeCritical.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example09_TimeCritical/Scheduler_example09_TimeCritical.ino new file mode 100644 index 0000000..b330d6f --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example09_TimeCritical/Scheduler_example09_TimeCritical.ino @@ -0,0 +1,57 @@ +/** + * TaskScheduler Test + * Illustration of use of Time Critical Information + * + * Task1 runs every 1 second indefinitely + * On each run it reports how delayed the invokation of the callback method was, + * and what was the scheduling overun. + * Each run task 1 is dealyed randomly for up to 2 seconds, thus simulating scheduling overrun + */ + + #define _TASK_TIMECRITICAL + #define _TASK_SLEEP_ON_IDLE_RUN +#include + +// Callback methods prototypes +void t1Callback(); + + +//Tasks +Task t1(1000, -1, &t1Callback); + +Scheduler runner; + + +void t1Callback() { + Serial.print(millis()); + Serial.print(": overrun = "); + Serial.print(t1.getOverrun()); + Serial.print(", start delayed by "); + Serial.println(t1.getStartDelay()); + + int i = random(2000); + Serial.print("Delaying for "); Serial.println(i); + delay(i); +} + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler TimeCritical TEST"); + + runner.init(); + Serial.println("Initialized scheduler"); + + runner.addTask(t1); + Serial.println("added t1. Waiting for 5 seconds."); + + delay(5000); + + t1.enable(); + + Serial.println("Enabled t1"); +} + + +void loop () { + runner.execute(); +} diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example10_Benchmark/Scheduler_example10_Benchmark.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example10_Benchmark/Scheduler_example10_Benchmark.ino new file mode 100644 index 0000000..0015d8d --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example10_Benchmark/Scheduler_example10_Benchmark.ino @@ -0,0 +1,106 @@ + +/** + * This is a test to benchmark TaskScheduler execution. + * + * This test executes 1,000,000 cycles of a task with empty callback method + * Compiled with different options, you can assess the impact of each on the size of the Task object + * and the execution overhead of the main execution pass route. + * + * Sample execution times (in milliseconds per 1M iterations) are provided below. + * The test board is Arduino UNO 16MHz processor. + * + +TaskScheduler 2.1.0: +No modifiers +Duration=19869 + +with SLEEP +Duration=20058 + +with status request: +Duration=20058 + +with time critical: +Duration=27289 + + +TaskScheduler 1.9.0: +No modifiers +Duration=15656 + +with SLEEP +Duration=16285 + +with status request: +Duration=16600 + +with rollover fix: +Duration=18109 + + +TaskScheduler 1.8.5: +Duration=15719 + +with SLEEP +Duration=16348 + +with status request: +Duration=18360 + +with rollover fix: +Duration=18423 + + */ + + +//#define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +//#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +//#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +//#define _TASK_LTS_POINTER // Compile with support for local task storage pointer +//#define _TASK_SLEEP_ON_IDLE_RUN +//#define _TASK_MICRO_RES +#include + +Scheduler ts; + +// Callback methods prototypes +bool tOn(); void tOff(); +void callback(); + +// Tasks +Task t(TASK_IMMEDIATE, 1000000, &callback, &ts, false, &tOn, &tOff); + +unsigned long c1, c2; + +bool tOn() { + c1 = millis(); + c2 = 0; + + return true; +} + +void tOff() { + c2 = millis(); + Serial.println("done."); + Serial.print("Tstart =");Serial.println(c1); + Serial.print("Tfinish=");Serial.println(c2); + Serial.print("Duration=");Serial.println(c2-c1); +} + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + Serial.print("Start..."); + + t.enable(); +} + +void callback() { + +} + + +void loop() { + // put your main code here, to run repeatedly: + ts.execute(); +} diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example11_Priority/Scheduler_example11_Priority.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example11_Priority/Scheduler_example11_Priority.ino new file mode 100644 index 0000000..ef260bc --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example11_Priority/Scheduler_example11_Priority.ino @@ -0,0 +1,105 @@ +/** + * This is a test of TaskScheduler layered priority funtionality + * + * Current test employs two priority layers: + * Base scheduler runs tasks t1, t2 and t3 + * High priority scheduler runs tasks t4 and t5 + * + * Sequence of task scheduling (not execution!) is: + * 4, 5, 1, 4, 5, 2, 4, 5, 3 = one base scheduler pass + * + * Scheduling overhead (at 20 micros per one pass) is: (B + B * H) * T = (3 + 3 * 2) * 18 = 162 micros + * where + * B - number of tasks in the base scheduler's chain + * H - number of tasks in the high priority scheduler's chain + * T - scheduling overhead for 1 pass (~15-18 microseconds) + * + * Actual task execution order: + +Scheduler Priority Test +Task: 40: 0 Start delay = 0 +Task: 50: 10 Start delay = 10 +Task: 1: 21 Start delay = 21 +Task: 2: 31 Start delay = 31 +Task: 3: 41 Start delay = 41 + +Task: 40: 500 Start delay = 0 +Task: 40: 1000 Start delay = 0 +Task: 50: 1010 Start delay = 10 +Task: 1: 1021 Start delay = 20 +Task: 40: 1500 Start delay = 0 +Task: 40: 2000 Start delay = 0 +Task: 50: 2011 Start delay = 11 +Task: 1: 2022 Start delay = 21 +Task: 2: 2032 Start delay = 32 +Task: 40: 2500 Start delay = 0 +Task: 40: 3000 Start delay = 0 +Task: 50: 3010 Start delay = 10 +Task: 1: 3021 Start delay = 20 +Task: 3: 3032 Start delay = 32 + +Task: 40: 3500 Start delay = 0 +Task: 40: 4000 Start delay = 0 +Task: 50: 4011 Start delay = 11 +Task: 1: 4022 Start delay = 21 +Task: 2: 4032 Start delay = 32 +Task: 40: 4500 Start delay = 0 +Task: 40: 5000 Start delay = 0 +Task: 50: 5010 Start delay = 10 +Task: 1: 5021 Start delay = 20 +Task: 40: 5500 Start delay = 0 +Task: 40: 6000 Start delay = 0 +Task: 50: 6010 Start delay = 10 +Task: 1: 6022 Start delay = 21 +Task: 2: 6032 Start delay = 32 +Task: 3: 6043 Start delay = 42 + + */ + +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_PRIORITY +#define _TASK_WDT_IDS +#define _TASK_TIMECRITICAL +#include + +Scheduler r, hpr; + +// Callback methods prototypes +void tCallback(); + +// Tasks +Task t1(1000, TASK_FOREVER, &tCallback, &r); //adding task to the chain on creation +Task t2(2000, TASK_FOREVER, &tCallback, &r); +Task t3(3000, TASK_FOREVER, &tCallback, &r); + +Task t4(500, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation +Task t5(1000, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation + +void tCallback() { + Scheduler &s = Scheduler::currentScheduler(); + Task &t = s.currentTask(); + + Serial.print("Task: "); Serial.print(t.getId());Serial.print(":\t"); + Serial.print(millis()); Serial.print("\tStart delay = "); Serial.println(t.getStartDelay()); + delay(10); + + if (t.getId() == 3) Serial.println(); +} + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler Priority Test"); + + t4.setId(40); + t5.setId(50); + + r.setHighPriorityScheduler(&hpr); + r.enableAll(true); // this will recursively enable the higher priority tasks as well +} + + +void loop () { + + r.execute(); + +} diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example12_Priority/Scheduler_example12_Priority.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example12_Priority/Scheduler_example12_Priority.ino new file mode 100644 index 0000000..799c4fa --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example12_Priority/Scheduler_example12_Priority.ino @@ -0,0 +1,136 @@ +/** + * This is a test of TaskScheduler layered priority funtionality + * + * Current test employs three priority layers: + * Base scheduler runs tasks t1, t2 and t3 + * High priority scheduler runs tasks t4 and t5 + * Highest priority scheduler runs tasks t6 and t7 + * + * Sequence of task scheduling (not execution!) is: + * 6, 7, 4, 6, 7, 5, 1, 6, 7, 4, 6, 7, 5, 2, 6, 7, 4, 6, 7, 5, 3 = one base scheduler pass + * + * Scheduling overhead (at 20 micros per one pass) is: (B + B * H + B * H * C) * T = (3 + 3 * 2 + 3 * 2 * 2) * 18 = 378 micros + * where + * B - number of tasks in the base scheduler's chain + * H - number of tasks in the high priority scheduler's chain + * C - number of tasks in the critical priority scheduler's chain + * T - scheduling overhead for 1 pass (~15-18 microseconds) + * + * Actual task execution order: + +Scheduler Priority Test +Task: 600: 0 Start delay = 0 +Task: 700: 10 Start delay = 10 +Task: 40: 21 Start delay = 21 +Task: 50: 31 Start delay = 31 +Task: 1: 43 Start delay = 41 +Task: 2: 53 Start delay = 53 +Task: 3: 63 Start delay = 63 + +Task: 600: 500 Start delay = 0 +Task: 40: 510 Start delay = 10 +Task: 600: 1000 Start delay = 0 +Task: 700: 1010 Start delay = 10 +Task: 40: 1021 Start delay = 21 +Task: 50: 1032 Start delay = 32 +Task: 1: 1043 Start delay = 43 +Task: 600: 1500 Start delay = 0 +Task: 40: 1510 Start delay = 10 +Task: 600: 2000 Start delay = 0 +Task: 700: 2011 Start delay = 11 +Task: 40: 2022 Start delay = 22 +Task: 50: 2032 Start delay = 32 +Task: 1: 2043 Start delay = 43 +Task: 2: 2054 Start delay = 54 +Task: 600: 2500 Start delay = 0 +Task: 40: 2510 Start delay = 10 +Task: 600: 3000 Start delay = 0 +Task: 700: 3010 Start delay = 10 +Task: 40: 3021 Start delay = 21 +Task: 50: 3032 Start delay = 32 +Task: 1: 3043 Start delay = 43 +Task: 3: 3053 Start delay = 53 + +Task: 600: 3500 Start delay = 0 +Task: 40: 3510 Start delay = 10 +Task: 600: 4000 Start delay = 0 +Task: 700: 4011 Start delay = 11 +Task: 40: 4022 Start delay = 22 +Task: 50: 4032 Start delay = 32 +Task: 1: 4043 Start delay = 43 +Task: 2: 4054 Start delay = 54 +Task: 600: 4500 Start delay = 0 +Task: 40: 4510 Start delay = 10 +Task: 600: 5000 Start delay = 0 +Task: 700: 5010 Start delay = 10 +Task: 40: 5021 Start delay = 21 +Task: 50: 5031 Start delay = 31 +Task: 1: 5043 Start delay = 43 +Task: 600: 5500 Start delay = 0 +Task: 40: 5511 Start delay = 11 +Task: 600: 6000 Start delay = 0 +Task: 700: 6010 Start delay = 10 +Task: 40: 6022 Start delay = 22 +Task: 50: 6032 Start delay = 32 +Task: 1: 6043 Start delay = 43 +Task: 2: 6053 Start delay = 53 +Task: 3: 6065 Start delay = 65 + + */ + +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_PRIORITY +#define _TASK_WDT_IDS +#define _TASK_TIMECRITICAL +#include + +Scheduler r; +Scheduler hpr; +Scheduler cpr; + +// Callback methods prototypes +void tCallback(); + +// Tasks +Task t1(1000, TASK_FOREVER, &tCallback, &r); //adding task to the chain on creation +Task t2(2000, TASK_FOREVER, &tCallback, &r); +Task t3(3000, TASK_FOREVER, &tCallback, &r); + +Task t4(500, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation +Task t5(1000, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation + +Task t6(500, TASK_FOREVER, &tCallback, &cpr); //adding task to the chain on creation +Task t7(1000, TASK_FOREVER, &tCallback, &cpr); //adding task to the chain on creation + +void tCallback() { + Scheduler &s = Scheduler::currentScheduler(); + Task &t = s.currentTask(); + + Serial.print("Task: "); Serial.print(t.getId());Serial.print(":\t"); + Serial.print(millis()); Serial.print("\tStart delay = "); Serial.println(t.getStartDelay()); + delay(10); + + if (t.getId() == 3) Serial.println(); +} + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler Priority Test"); + + t4.setId(40); + t5.setId(50); + + t6.setId(600); + t7.setId(700); + + r.setHighPriorityScheduler(&hpr); + hpr.setHighPriorityScheduler(&cpr); + r.enableAll(true); // this will recursively enable the higher priority tasks as well +} + + +void loop () { + + r.execute(); + +} diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example13_Micros/Scheduler_example13_Micros.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example13_Micros/Scheduler_example13_Micros.ino new file mode 100644 index 0000000..ea5dd86 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example13_Micros/Scheduler_example13_Micros.ino @@ -0,0 +1,71 @@ +/** + * TaskScheduler Test of microsecond scheduling resolution + * + * Task 1 runs starting with 211 microseconds intervals, doubling the interval on every iteration + * until it wraps when interval reaches about 72 minutes mark + * + * Task 2 provides heartbeat at a steady 5 seconds intervals + * + */ + +#define _TASK_MICRO_RES +#include + +#define T1_INIT (211L) + +Scheduler runner; +// Callback methods prototypes +void t1Callback(); +void t1OnDisable(); +void t2Callback(); + + +unsigned long t1_interval = T1_INIT; + +// Tasks +Task t1(t1_interval, 1, &t1Callback, &runner, true, NULL, &t1OnDisable); //adding task to the chain on creation +Task t2(5 * TASK_SECOND, TASK_FOREVER, &t2Callback, &runner, true); //adding task to the chain on creation + + + +void t1Callback() { + unsigned long t = micros(); + Serial.print("t1: "); + Serial.println(t); +} + +void t1OnDisable() { + t1_interval += t1_interval; + if (t1_interval < T1_INIT) t1_interval = T1_INIT; + t1.setInterval(t1_interval); + t1.restartDelayed(); +} + +void t2Callback() { + unsigned long t = micros(); + Serial.print("t2: "); + Serial.print(t); + Serial.println(" heartbeat"); +} + + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler TEST Microsecond Resolution"); + + Serial.println("5 seconds delay"); + delay(5000); + + runner.startNow(); // This creates a new scheduling starting point for all ACTIVE tasks. + // PLEASE NOTE - THIS METHOD DOES NOT ACTIVATE TASKS, JUST RESETS THE START TIME + t1.delay(); // Tasks which need to start delayed, need to be delayed again after startNow(); + +// Alternatively, tasks should be just enabled at the bottom of setup() method +// runner.enableAll(); +// t1.delay(); +} + + +void loop () { + runner.execute(); +} diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example14_Yield/Scheduler_example14_Yield.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example14_Yield/Scheduler_example14_Yield.ino new file mode 100644 index 0000000..50aa109 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example14_Yield/Scheduler_example14_Yield.ino @@ -0,0 +1,339 @@ +/** + This test illustrates the use if yield methods and internal StatusRequest objects + THIS TEST HAS BEEN TESTED ON NODEMCU V.2 (ESP8266) + + The WiFi initialization and NTP update is executed in parallel to blinking the onboard LED + and an external LED connected to D2 (GPIO04) + Try running with and without correct WiFi parameters to observe the difference in behaviour +*/ + +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_STATUS_REQUEST +#include + +#include +#include + +Scheduler ts; + +// Callback methods prototypes +void connectInit(); +void ledCallback(); +bool ledOnEnable(); +void ledOnDisable(); +void ledOn(); +void ledOff(); +void ntpUpdateInit(); + +// Tasks + +Task tConnect (TASK_SECOND, TASK_FOREVER, &connectInit, &ts, true); +Task tLED (TASK_IMMEDIATE, TASK_FOREVER, &ledCallback, &ts, false, &ledOnEnable, &ledOnDisable); + +// Tasks running on events +Task tNtpUpdate (&ntpUpdateInit, &ts); + +// Replace with WiFi parameters of your Access Point/Router: +const char *ssid = "wifi_network"; +const char *pwd = "wifi_password"; + +long ledDelayOn, ledDelayOff; + +#define LEDPIN D0 // Onboard LED pin - linked to WiFi +#define LEDPIN2 D2 // External LED +#define CONNECT_TIMEOUT 30 // Seconds +#define CONNECT_OK 0 // Status of successful connection to WiFi +#define CONNECT_FAILED (-99) // Status of failed connection to WiFi + +// NTP Related Definitions +#define NTP_PACKET_SIZE 48 // NTP time stamp is in the first 48 bytes of the message + +IPAddress timeServerIP; // time.nist.gov NTP server address +const char* ntpServerName = "time.nist.gov"; +byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets +unsigned long epoch; + +WiFiUDP udp; // A UDP instance to let us send and receive packets over UDP + +#define LOCAL_NTP_PORT 2390 // Local UDP port for NTP update + + + +void setup() { + Serial.begin(74880); + Serial.println(F("TaskScheduler test #14 - Yield and internal StatusRequests")); + Serial.println(F("==========================================================")); + Serial.println(); + + pinMode (LEDPIN, OUTPUT); + pinMode (LEDPIN2, OUTPUT); + + tNtpUpdate.waitFor( tConnect.getInternalStatusRequest() ); // NTP Task will start only after connection is made +} + +void loop() { + ts.execute(); // Only Scheduler should be executed in the loop +} + +/** + Initiate connection to the WiFi network +*/ +void connectInit() { + Serial.print(millis()); + Serial.println(F(": connectInit.")); + Serial.println(F("WiFi parameters: ")); + Serial.print(F("SSID: ")); Serial.println(ssid); + Serial.print(F("PWD : ")); Serial.println(pwd); + + WiFi.mode(WIFI_STA); + WiFi.hostname("esp8266"); + WiFi.begin(ssid, pwd); + yield(); + + ledDelayOn = TASK_SECOND / 2; + ledDelayOff = TASK_SECOND / 4; + tLED.enable(); + + tConnect.yield(&connectCheck); // This will pass control back to Scheduler and then continue with connection checking +} + +/** + Periodically check if connected to WiFi + Re-request connection every 5 seconds + Stop trying after a timeout +*/ +void connectCheck() { + Serial.print(millis()); + Serial.println(F(": connectCheck.")); + + if (WiFi.status() == WL_CONNECTED) { // Connection established + Serial.print(millis()); + Serial.print(F(": Connected to AP. Local ip: ")); + Serial.println(WiFi.localIP()); + tConnect.disable(); + } + else { + + if (tConnect.getRunCounter() % 5 == 0) { // re-request connection every 5 seconds + + Serial.print(millis()); + Serial.println(F(": Re-requesting connection to AP...")); + + WiFi.disconnect(true); + yield(); // This is an esp8266 standard yield to allow linux wifi stack run + WiFi.hostname("esp8266"); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, pwd); + yield(); // This is an esp8266 standard yield to allow linux wifi stack run + } + + if (tConnect.getRunCounter() == CONNECT_TIMEOUT) { // Connection Timeout + tConnect.getInternalStatusRequest()->signal(CONNECT_FAILED); // Signal unsuccessful completion + tConnect.disable(); + + Serial.print(millis()); + Serial.println(F(": connectOnDisable.")); + Serial.print(millis()); + Serial.println(F(": Unable to connect to WiFi.")); + + ledDelayOn = TASK_SECOND / 16; // Blink LEDs quickly due to error + ledDelayOff = TASK_SECOND / 16; + tLED.enable(); + } + } +} + +/** + Initiate NTP update if connection was established +*/ +void ntpUpdateInit() { + Serial.print(millis()); + Serial.println(F(": ntpUpdateInit.")); + + if ( tConnect.getInternalStatusRequest()->getStatus() != CONNECT_OK ) { // Check status of the Connect Task + Serial.print(millis()); + Serial.println(F(": cannot update NTP - not connected.")); + return; + } + + udp.begin(LOCAL_NTP_PORT); + if ( WiFi.hostByName(ntpServerName, timeServerIP) ) { //get a random server from the pool + + Serial.print(millis()); + Serial.print(F(": timeServerIP = ")); + Serial.println(timeServerIP); + + sendNTPpacket(timeServerIP); // send an NTP packet to a time server + } + else { + Serial.print(millis()); + Serial.println(F(": NTP server address lookup failed.")); + tLED.disable(); + udp.stop(); + tNtpUpdate.disable(); + return; + } + + ledDelayOn = TASK_SECOND / 8; + ledDelayOff = TASK_SECOND / 8; + tLED.enable(); + + tNtpUpdate.set( TASK_SECOND, CONNECT_TIMEOUT, &ntpCheck ); + tNtpUpdate.enableDelayed(); +} + +/** + * Check if NTP packet was received + * Re-request every 5 seconds + * Stop trying after a timeout + */ +void ntpCheck() { + Serial.print(millis()); + Serial.println(F(": ntpCheck.")); + + if ( tNtpUpdate.getRunCounter() % 5 == 0) { + + Serial.print(millis()); + Serial.println(F(": Re-requesting NTP update...")); + + udp.stop(); + yield(); + udp.begin(LOCAL_NTP_PORT); + sendNTPpacket(timeServerIP); + return; + } + + if ( doNtpUpdateCheck()) { + Serial.print(millis()); + Serial.println(F(": NTP Update successful")); + + Serial.print(millis()); + Serial.print(F(": Unix time = ")); + Serial.println(epoch); + + tLED.disable(); + tNtpUpdate.disable(); + udp.stop(); + } + else { + if ( tNtpUpdate.isLastIteration() ) { + Serial.print(millis()); + Serial.println(F(": NTP Update failed")); + tLED.disable(); + udp.stop(); + } + } +} + +/** + * Send NTP packet to NTP server + */ +unsigned long sendNTPpacket(IPAddress & address) +{ + Serial.print(millis()); + Serial.println(F(": sendNTPpacket.")); + + // set all bytes in the buffer to 0 + memset(packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; + + // all NTP fields have been given values, now + // you can send a packet requesting a timestamp: + udp.beginPacket(address, 123); //NTP requests are to port 123 + udp.write(packetBuffer, NTP_PACKET_SIZE); + udp.endPacket(); + yield(); +} + +/** + * Check if a packet was recieved. + * Process NTP information if yes + */ +bool doNtpUpdateCheck() { + + Serial.print(millis()); + Serial.println(F(": doNtpUpdateCheck.")); + + yield(); + int cb = udp.parsePacket(); + if (cb) { + // We've received a packet, read the data from it + udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer + + //the timestamp starts at byte 40 of the received packet and is four bytes, + // or two words, long. First, esxtract the two words: + + unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); + unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + unsigned long secsSince1900 = highWord << 16 | lowWord; + + // now convert NTP time into everyday time: + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const unsigned long seventyYears = 2208988800UL; + // subtract seventy years: + epoch = secsSince1900 - seventyYears; + return (epoch != 0); + } + return false; +} + +/** + * Flip the LED state based on the current state + */ +bool ledState; +void ledCallback() { + if ( ledState ) ledOff(); + else ledOn(); +} + +/** + * Make sure the LED starts lit + */ +bool ledOnEnable() { + ledOn(); + return true; +} + +/** + * Make sure LED ends dimmed + */ +void ledOnDisable() { + ledOff(); +} + +/** + * Turn LEDs on. + * Set appropriate delay. + * PLEASE NOTE: NodeMCU onbaord LED is active-low + */ +void ledOn() { + ledState = true; + digitalWrite(LEDPIN, LOW); + digitalWrite(LEDPIN2, HIGH); + tLED.delay( ledDelayOn ); +} + +/** + * Turn LEDs off. + * Set appropriate delay. + * PLEASE NOTE: NodeMCU onbaord LED is active-low + */ +void ledOff() { + ledState = false; + digitalWrite(LEDPIN, HIGH); + digitalWrite(LEDPIN2, LOW); + tLED.delay( ledDelayOff ); +} + diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example15_STDFunction/Scheduler_example15_STDFunction.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example15_STDFunction/Scheduler_example15_STDFunction.ino new file mode 100644 index 0000000..cd4f2ac --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example15_STDFunction/Scheduler_example15_STDFunction.ino @@ -0,0 +1,53 @@ +/** + * TaskScheduler Test sketch - Showing how to use std::function + * to get acces to variables from within the task callback function + * + * Support for std::function is only available for ESP8266 architecture + */ +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_STD_FUNCTION // Compile with support for std::function +#include + +Scheduler ts; +int counter = 0; + +class Calculator { +public: + int cumSum = 0; // cumulative sum + Calculator(int b) { + // Pass the this pointer, so that we get access to this->cumSum + // Also pass a copy of b + calculateTask.set(TASK_SECOND, TASK_FOREVER, [this, b]() { + counter++; + Serial.printf("%u. %u: cumSum = %u + %u\t", counter, millis(), cumSum, b); + cumSum += b; + Serial.printf("Resulting cumulative sum: %u\n", cumSum); + }); + ts.addTask(calculateTask); + calculateTask.enable(); + } + + Task calculateTask; +}; + +Calculator calc1(2); +Calculator calc2(4); +Calculator calc3(8); + +// Disable tasks after 10 seconds +Task tWrapper(10*TASK_SECOND, TASK_ONCE, []() { + ts.disableAll(); +}, &ts); + +/** + * Standard Arduino setup and loop methods + */ +void setup() { + Serial.begin(74880); + Serial.println("std::function test"); + tWrapper.enableDelayed(); +} + +void loop() { + ts.execute(); +} \ No newline at end of file diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/Scheduler_example16_Multitab.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/Scheduler_example16_Multitab.ino new file mode 100644 index 0000000..a92becd --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/Scheduler_example16_Multitab.ino @@ -0,0 +1,18 @@ +//This file is intentionally left blank. +// +//Arduino IDE plays some dirty tricks on the main sketch .ino file: +//it rearranges #includes, blindly creates forward definitions, +//includes every file in the project that does not have .c or .cpp +//file extension. +// +//Usually it all turns well if you have only one source file and you are either +//inexperienced or really expert C++ Arduino programmer. +//For the folks with the middle ground skills level, when you want +//to split your code into several .cpp files, it is best to leave +//this main sketch empty. +// +//It doesn't matter where you define the void loop() and void setup(). +//Just make sure there is exactly one definition of each. +// +//And if you want to use standard Arduino functions +//like digitalWrite or the Serial object - just add #include. diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/file1.cpp b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/file1.cpp new file mode 100644 index 0000000..03e49cb --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/file1.cpp @@ -0,0 +1,31 @@ +#include +#include "header.hpp" + + +//Declare the functions we want to use before we are ready to define them +void t1Callback(); + + +// Tasks +Task t1(2000, 10, &t1Callback, &runner, true); //adding task to the chain on creation +Task t3(5000, TASK_FOREVER, &t3Callback); + + +void t1Callback() { + Serial.print("t1: "); + Serial.println(millis()); + + if (t1.isFirstIteration()) { + runner.addTask(t3); + t3.enable(); + Serial.println("t1: enabled t3 and added to the chain"); + } + + if (t1.isLastIteration()) { + t3.disable(); + runner.deleteTask(t3); + t2.setInterval(500); + Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); + } +} + diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/file2.cpp b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/file2.cpp new file mode 100644 index 0000000..ae177de --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/file2.cpp @@ -0,0 +1,56 @@ +// Test the same as example#2: +// Initially only tasks 1 and 2 are enabled +// Task1 runs every 2 seconds 10 times and then stops +// Task2 runs every 3 seconds indefinitely +// Task1 enables Task3 at its first run +// Task3 run every 5 seconds +// loop() runs every 1 second (a default scheduler delay, if no shorter tasks' interval is detected) +// Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds +// Because Task2 interval is shorter than Scheduler default tick, loop() executes ecery 1/2 seconds now +// At the end Task2 is the only task running every 1/2 seconds + + +//Header that declares all shared objects between .cpp files +#include "header.hpp" + +#include //for Serial and delay + +Scheduler runner; //Let the scheduler live here, in the main file, ok? + + +//Pretend, that the t2 task is a special task, +//that needs to live in file2 object file. +void t2Callback() { + Serial.print("t2: "); + Serial.println(millis()); +} +Task t2(3000, TASK_FOREVER, &t2Callback, &runner, true); + +//Lets define t3Callback here. We are going to use it in file1 +//for Task 1. +void t3Callback() { + Serial.print("t3: "); + Serial.println(millis()); +} + + + + + + +void setup () { + Serial.begin(115200); + Serial.println("Scheduler TEST"); + + delay(5000); + + runner.startNow(); // set point-in-time for scheduling start +} + + +void loop () { + runner.execute(); + +// Serial.println("Loop ticks at: "); +// Serial.println(millis()); +} diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/header.hpp b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/header.hpp new file mode 100644 index 0000000..a3cf633 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/header.hpp @@ -0,0 +1,15 @@ +//This is the place to declare every single function +//and global variable that is going to be reused between cpp files. + + +//We are going to use the TaskScheduler, but only the declarations part. +//Remember to put customization macros before the #include: +#define _TASK_SLEEP_ON_IDLE_RUN +#include + +//Let the runner object be a global, single instance shared between object files. +extern Scheduler runner; +extern Task t2; //the t2 is defined in file2, but we need to access it from file1. + +//This function needs to be shared (between file2 and file1). +void t3Callback(); diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/ts.cpp b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/ts.cpp new file mode 100644 index 0000000..c7cac8f --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example16_Multitab/ts.cpp @@ -0,0 +1,20 @@ +//This is the only .cpp file that gets the #include. +//Without it, the linker would not find necessary TaskScheduler's compiled code. +// +//Remember to put customization macros here as well. +// +//And don't import any common headers (here: header.hpp) +// +//Really. This file needs to be short. All stuff is in TaskScheduler.h. + +// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer +// #define _TASK_PRIORITY // Support for layered scheduling priority +// #define _TASK_MICRO_RES // Support for microsecond resolution +// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) +// #define _TASK_DEBUG // Make all methods and variables public for debug purposes + +#include diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example17_Timeout/Scheduler_example17_Timeout.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example17_Timeout/Scheduler_example17_Timeout.ino new file mode 100644 index 0000000..60392ba --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example17_Timeout/Scheduler_example17_Timeout.ino @@ -0,0 +1,90 @@ +/* + This eaxmple illustrates the use of overall Task timeout functionality: + + Task 1 - runs every 1 seconds and times out in 10 seconds + Task 2 - runs every 5 seconds and resets the timeout every run, so runs continuosly even though the timeout is set to 10 seconds +*/ + + +// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +//#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer +// #define _TASK_PRIORITY // Support for layered scheduling priority +// #define _TASK_MICRO_RES // Support for microsecond resolution +// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) +// #define _TASK_DEBUG // Make all methods and variables public for debug purposes +// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations +#define _TASK_TIMEOUT + +#include + +Scheduler ts; + +void task1Callback(); +void task1OnDisable(); +void task2Callback(); +void task2OnDisable(); + +Task t1(1 * TASK_SECOND, TASK_FOREVER, &task1Callback, &ts, false, NULL, &task1OnDisable); +Task t2(5 * TASK_SECOND, TASK_FOREVER, &task2Callback, &ts, false, NULL, &task2OnDisable); + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + + Serial.println("TaskScheduler Timeout example"); + Serial.println("============================="); + + t1.setTimeout(10 * TASK_SECOND); + t2.setTimeout(10 * TASK_SECOND); + + ts.enableAll(); +} + +void loop() { + // put your main code here, to run repeatedly: + ts.execute(); +} + + +void task1Callback() { + Serial.print("Task 1:\t"); + Serial.print(millis()); + Serial.print(": t/out="); + Serial.print(t1.getTimeout()); + Serial.print("\tms until t/out="); + Serial.println( t1.untilTimeout()); + +} +void task1OnDisable() { + if (t1.timedOut()) { + Serial.println("Task 1 has timed out. Restarting"); + t1.setInterval(1 * TASK_SECOND); + t1.setIterations(15); + t1.setTimeout(TASK_NOTIMEOUT); + t1.enable(); + } + else { + Serial.println("Task 1 has been disabled"); + } +} + +void task2Callback() { + Serial.print("Task 2:\t"); + Serial.print(millis()); + Serial.print(": t/out="); + Serial.print(t2.getTimeout()); + Serial.print("\tms until t/out="); + Serial.println( t2.untilTimeout()); + t2.resetTimeout(); +} +void task2OnDisable() { + if (t2.timedOut()) { + Serial.println("Task 2 has timed out"); + } + else { + Serial.println("Task 2 has been disabled"); + } +} diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example18_StatusRequest_LTS_WDT_Timeout/Scheduler_example18_StatusRequest_LTS_WDT_Timeout.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example18_StatusRequest_LTS_WDT_Timeout/Scheduler_example18_StatusRequest_LTS_WDT_Timeout.ino new file mode 100644 index 0000000..c17fed6 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example18_StatusRequest_LTS_WDT_Timeout/Scheduler_example18_StatusRequest_LTS_WDT_Timeout.ino @@ -0,0 +1,172 @@ +/** + This is example 5 rewritten with Timeout, LTS and WDT functioanlity: + - 1 second timeout is set for the main calculation task + - LTS is used to address individual array elements for each sensor sinlce the callback code is shared + - WDT is used to set the Task ID and use that as an index for array of distances (alternative to LTS) + + Original description: + ==================== + This test emulates querying 3 sensors once every 10 seconds, each could respond with a different delay + (ultrasonic sensors for instance) and printing a min value of the three when all three have reported their values. + The overall timeout of 1 second is setup as well. + An error message needs to be printed if a timeout occurred instead of a value. + + Example5: + Sketch uses 6066 bytes (18%) of program storage space. Maximum is 32256 bytes. + Global variables use 1039 bytes (50%) of dynamic memory, leaving 1009 bytes for local variables. Maximum is 2048 bytes. + Example 18: + Sketch uses 5142 bytes (15%) of program storage space. Maximum is 32256 bytes. + Global variables use 878 bytes (42%) of dynamic memory, leaving 1170 bytes for local variables. Maximum is 2048 bytes. +*/ + +// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +#define _TASK_LTS_POINTER // Compile with support for local task storage pointer +#define _TASK_PRIORITY // Support for layered scheduling priority +// #define _TASK_MICRO_RES // Support for microsecond resolution +// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY) +#define _TASK_DEBUG // Make all methods and variables public for debug purposes +#define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations +#define _TASK_TIMEOUT // Support for overall task timeout + +#include + +StatusRequest measure; + +Scheduler ts, hts; + +// Callback methods prototypes +void CycleCallback(); +void CalcCallback(); +bool CalcEnable(); +void CalcDisable(); +void SCallback(); bool SEnable(); + +// Tasks +Task tSensor1(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 1 +Task tSensor2(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 2 +Task tSensor3(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 3 + +Task tCycle(10000, TASK_FOREVER, &CycleCallback, &hts); +Task tCalculate(TASK_IMMEDIATE , TASK_ONCE, &CalcCallback, &hts, false, &CalcEnable, &CalcDisable); + +#define NO_OF_SENSORS 3 +long distance, d[NO_OF_SENSORS + 1], d_lts[NO_OF_SENSORS]; // d[] will be populated via task ID used as array indexes, d_lts will be addressed via LTS pointers + +void CycleCallback() { + Serial.println(); + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); + + distance = 0; + measure.setWaiting(NO_OF_SENSORS); // Set the StatusRequest to wait for 3 signals. + tCalculate.waitFor(&measure); +} + +bool CalcEnable() { + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CalcEnable: OnEnable"); + Serial.println("Activating sensors and setting timeout"); + + tSensor1.restartDelayed(); + tSensor2.restartDelayed(); + tSensor3.restartDelayed(); + + return true; +} + +void CalcDisable() { + if (tCalculate.timedOut()) { + measure.signalComplete(-1); // signal error + Serial.print(millis()); Serial.print(":\t"); + Serial.println("MeasureCallback: ***** Timeout *****"); + // tSensor1.disable(); + // tSensor2.disable(); + // tSensor3.disable(); + } +} + + +void CalcCallback() { + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CalcCallback: calculating"); + distance = -1; + if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully + distance = d[1] < d[2] ? d[1] : d[2]; + distance = d[3] < distance ? d[3] : distance; + Serial.print("CalcCallback: Min distance="); Serial.println(distance); + Serial.println(); + } +} + + +/** Simulation code for all sensors + ------------------------------- +*/ +bool SEnable() { + Task &t = ts.currentTask(); + int i = t.getId(); + + Serial.print(millis()); Serial.print(":\t"); + Serial.print("SEnable: TaskID="); + Serial.println(i); + Serial.print("Triggering sensor. Delay="); + + t.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout + // One way to update the 3 distances with one codebase - use task id as an index + d[i] = 0; + + // Another way to update the 3 distances with one codebase - use LTS pointers + int *pd = (int*) t.getLtsPointer(); + *pd = 0; + + Serial.println( t.getInterval() ); + return true; +} + +void SCallback() { + Task &t = ts.currentTask(); + int i = t.getId(); + + Serial.print(millis()); Serial.print(":\t"); + Serial.print("SCallback: TaskID="); + Serial.println(i); + Serial.print("Emulating measurement. d="); + + d[i] = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement + int *pd = (int*) t.getLtsPointer(); + *pd = d[i]; + + measure.signal(); + + Serial.print(d[i]); + Serial.print("\t"); + Serial.println(*pd); +} + +/** Main Arduino code + Not much is left here - everything is taken care of by the framework +*/ +void setup() { + + Serial.begin(115200); + Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); + randomSeed(analogRead(A0) + millis()); + + tSensor1.setLtsPointer(&d_lts[0]); + tSensor2.setLtsPointer(&d_lts[1]); + tSensor3.setLtsPointer(&d_lts[2]); + + ts.setHighPriorityScheduler(&hts); + + tCalculate.setTimeout(1 * TASK_SECOND); + tCycle.enable(); +} + +void loop() { + + ts.execute(); + +} diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example19_Dynamic_Tasks/Scheduler_example19_Dynamic_Tasks.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example19_Dynamic_Tasks/Scheduler_example19_Dynamic_Tasks.ino new file mode 100644 index 0000000..222a8d2 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example19_Dynamic_Tasks/Scheduler_example19_Dynamic_Tasks.ino @@ -0,0 +1,540 @@ +/** + TaskScheduler Test sketch - test of Task destructor + Test case: + Main task runs every 100 milliseconds 100 times and in 50% cases generates a task object + which runs 1 to 10 times with 100 ms to 5 s interval, and then destroyed. + + This sketch uses a FreeMemory library: https://github.com/McNeight/MemoryFree +*/ + +#define _TASK_WDT_IDS // To enable task unique IDs +#define _TASK_SLEEP_ON_IDLE_RUN // Compile with support for entering IDLE SLEEP state for 1 ms if not tasks are scheduled to run +#define _TASK_LTS_POINTER // Compile with support for Local Task Storage pointer +#include + +#include + +Scheduler ts; + +// Callback methods prototypes +void MainLoop(); + +// Statis task +Task tMain(100*TASK_MILLISECOND, 100, MainLoop, &ts, true); + + +void Iteration(); +bool OnEnable(); +void OnDisable(); + +int noOfTasks = 0; + +void MainLoop() { + Serial.print(millis()); Serial.print("\t"); + Serial.print("MainLoop run: "); + int i = tMain.getRunCounter(); + Serial.print(i); Serial.print(F(".\t")); + + if ( random(0, 101) > 50 ) { // generate a new task only in 50% of cases + // Generating another task + long p = random(100, 5001); // from 100 ms to 5 seconds + long j = random(1, 11); // from 1 to 10 iterations) + Task *t = new Task(p, j, Iteration, &ts, false, OnEnable, OnDisable); + + Serial.print(F("Generated a new task:\t")); Serial.print(t->getId()); Serial.print(F("\tInt, Iter = \t")); + Serial.print(p); Serial.print(", "); Serial.print(j); Serial.print(F("\tFree mem=")); + Serial.print(freeMemory()); Serial.print(F("\tNo of tasks=")); Serial.println(++noOfTasks); + t->enable(); + } + else { + Serial.println(F("Skipped generating a task")); + } +} + + +void Iteration() { + Task &t = ts.currentTask(); + + Serial.print(millis()); Serial.print("\t"); + Serial.print("Task N"); Serial.print(t.getId()); Serial.print(F("\tcurrent iteration: ")); + int i = t.getRunCounter(); + Serial.println(i); +} + +bool OnEnable() { + // to-do: think of something to put in here. + return true; +} + +void OnDisable() { + Task *t = &ts.currentTask(); + unsigned int tid = t->getId(); + + delete t; + Serial.print(millis()); Serial.print("\t"); + Serial.print("Task N"); Serial.print(tid); Serial.print(F("\tfinished and destroyed.\tFree mem=")); + Serial.print(freeMemory());Serial.print(F("\tNo of tasks=")); Serial.println(--noOfTasks); +} + +/** + Standard Arduino setup and loop methods +*/ +void setup() { + Serial.begin(115200); + + randomSeed(analogRead(0) + analogRead(5)); + noOfTasks = 0; + + Serial.println(F("Dynamic Task Creation/Destruction Example")); + Serial.println(); + Serial.print(F("Free mem=")); + Serial.print(freeMemory()); Serial.print(F("\tNo of tasks=")); Serial.println(noOfTasks); + Serial.println(); +} + +void loop() { + ts.execute(); +} + + +/* Output on Arduino Uno: + * + * Compile: + +Sketch uses 5312 bytes (16%) of program storage space. Maximum is 32256 bytes. +Global variables use 282 bytes (13%) of dynamic memory, leaving 1766 bytes for local variables. Maximum is 2048 bytes. + + * Execution: + +Dynamic Task Creation/Destruction Example + +Free mem=1758 No of tasks=0 + +1 MainLoop run: 1. Generated a new task: 2 Int, Iter = 421, 3 Free mem=1701 No of tasks=1 +8 Task N2 current iteration: 1 +100 MainLoop run: 2. Generated a new task: 3 Int, Iter = 4099, 9 Free mem=1656 No of tasks=2 +102 Task N3 current iteration: 1 +200 MainLoop run: 3. Skipped generating a task +300 MainLoop run: 4. Generated a new task: 4 Int, Iter = 1795, 1 Free mem=1611 No of tasks=3 +302 Task N4 current iteration: 1 +305 Task N4 finished and destroyed. Free mem=1613 No of tasks=2 +400 MainLoop run: 5. Skipped generating a task +429 Task N2 current iteration: 2 +500 MainLoop run: 6. Skipped generating a task +600 MainLoop run: 7. Skipped generating a task +700 MainLoop run: 8. Generated a new task: 5 Int, Iter = 4623, 7 Free mem=1611 No of tasks=3 +702 Task N5 current iteration: 1 +800 MainLoop run: 9. Generated a new task: 6 Int, Iter = 4987, 4 Free mem=1566 No of tasks=4 +802 Task N6 current iteration: 1 +850 Task N2 current iteration: 3 +850 Task N2 finished and destroyed. Free mem=1568 No of tasks=3 +900 MainLoop run: 10. Generated a new task: 7 Int, Iter = 600, 4 Free mem=1566 No of tasks=4 +902 Task N7 current iteration: 1 +1000 MainLoop run: 11. Skipped generating a task +1100 MainLoop run: 12. Generated a new task: 8 Int, Iter = 2530, 1 Free mem=1521 No of tasks=5 +1102 Task N8 current iteration: 1 +1105 Task N8 finished and destroyed. Free mem=1523 No of tasks=4 +1200 MainLoop run: 13. Skipped generating a task +1300 MainLoop run: 14. Generated a new task: 9 Int, Iter = 2215, 7 Free mem=1521 No of tasks=5 +1302 Task N9 current iteration: 1 +1400 MainLoop run: 15. Skipped generating a task +1500 MainLoop run: 16. Skipped generating a task +1502 Task N7 current iteration: 2 +1600 MainLoop run: 17. Skipped generating a task +1700 MainLoop run: 18. Skipped generating a task +1800 MainLoop run: 19. Skipped generating a task +1900 MainLoop run: 20. Skipped generating a task +2000 MainLoop run: 21. Generated a new task: 10 Int, Iter = 189, 10 Free mem=1476 No of tasks=6 +2002 Task N10 current iteration: 1 +2100 MainLoop run: 22. Generated a new task: 11 Int, Iter = 2898, 9 Free mem=1431 No of tasks=7 +2102 Task N7 current iteration: 3 +2105 Task N11 current iteration: 1 +2191 Task N10 current iteration: 2 +2200 MainLoop run: 23. Generated a new task: 12 Int, Iter = 1691, 6 Free mem=1386 No of tasks=8 +2202 Task N12 current iteration: 1 +2300 MainLoop run: 24. Generated a new task: 13 Int, Iter = 1448, 7 Free mem=1341 No of tasks=9 +2304 Task N13 current iteration: 1 +2380 Task N10 current iteration: 3 +2400 MainLoop run: 25. Generated a new task: 14 Int, Iter = 3919, 7 Free mem=1296 No of tasks=10 +2403 Task N14 current iteration: 1 +2500 MainLoop run: 26. Generated a new task: 15 Int, Iter = 3745, 5 Free mem=1251 No of tasks=11 +2503 Task N15 current iteration: 1 +2569 Task N10 current iteration: 4 +2600 MainLoop run: 27. Skipped generating a task +2700 MainLoop run: 28. Skipped generating a task +2702 Task N7 current iteration: 4 +2702 Task N7 finished and destroyed. Free mem=1253 No of tasks=10 +2758 Task N10 current iteration: 5 +2800 MainLoop run: 29. Generated a new task: 16 Int, Iter = 2144, 1 Free mem=1251 No of tasks=11 +2803 Task N16 current iteration: 1 +2806 Task N16 finished and destroyed. Free mem=1253 No of tasks=10 +2900 MainLoop run: 30. Generated a new task: 17 Int, Iter = 4618, 10 Free mem=1251 No of tasks=11 +2904 Task N17 current iteration: 1 +2947 Task N10 current iteration: 6 +3000 MainLoop run: 31. Skipped generating a task +3100 MainLoop run: 32. Generated a new task: 18 Int, Iter = 2885, 6 Free mem=1206 No of tasks=12 +3103 Task N18 current iteration: 1 +3136 Task N10 current iteration: 7 +3200 MainLoop run: 33. Skipped generating a task +3300 MainLoop run: 34. Skipped generating a task +3325 Task N10 current iteration: 8 +3400 MainLoop run: 35. Skipped generating a task +3500 MainLoop run: 36. Generated a new task: 19 Int, Iter = 2250, 4 Free mem=1161 No of tasks=13 +3503 Task N19 current iteration: 1 +3514 Task N10 current iteration: 9 +3518 Task N9 current iteration: 2 +3600 MainLoop run: 37. Skipped generating a task +3700 MainLoop run: 38. Generated a new task: 20 Int, Iter = 1689, 7 Free mem=1116 No of tasks=14 +3703 Task N10 current iteration: 10 +3706 Task N20 current iteration: 1 +3709 Task N10 finished and destroyed. Free mem=1118 No of tasks=13 +3750 Task N13 current iteration: 2 +3800 MainLoop run: 39. Generated a new task: 21 Int, Iter = 2607, 5 Free mem=1116 No of tasks=14 +3803 Task N21 current iteration: 1 +3893 Task N12 current iteration: 2 +3900 MainLoop run: 40. Generated a new task: 22 Int, Iter = 1390, 6 Free mem=1071 No of tasks=15 +3903 Task N22 current iteration: 1 +4000 MainLoop run: 41. Generated a new task: 23 Int, Iter = 3340, 8 Free mem=1026 No of tasks=16 +4003 Task N23 current iteration: 1 +4100 MainLoop run: 42. Skipped generating a task +4200 MainLoop run: 43. Skipped generating a task +4201 Task N3 current iteration: 2 +4300 MainLoop run: 44. Skipped generating a task +4400 MainLoop run: 45. Generated a new task: 24 Int, Iter = 4083, 2 Free mem=981 No of tasks=17 +4403 Task N24 current iteration: 1 +4500 MainLoop run: 46. Generated a new task: 25 Int, Iter = 4510, 1 Free mem=936 No of tasks=18 +4503 Task N25 current iteration: 1 +4506 Task N25 finished and destroyed. Free mem=938 No of tasks=17 +4600 MainLoop run: 47. Generated a new task: 26 Int, Iter = 4782, 10 Free mem=936 No of tasks=18 +4603 Task N26 current iteration: 1 +4700 MainLoop run: 48. Generated a new task: 27 Int, Iter = 641, 6 Free mem=891 No of tasks=19 +4702 Task N27 current iteration: 1 +4800 MainLoop run: 49. Generated a new task: 28 Int, Iter = 695, 5 Free mem=846 No of tasks=20 +4802 Task N28 current iteration: 1 +4900 MainLoop run: 50. Generated a new task: 29 Int, Iter = 3520, 3 Free mem=801 No of tasks=21 +4903 Task N29 current iteration: 1 +5000 MainLoop run: 51. Generated a new task: 30 Int, Iter = 3091, 9 Free mem=756 No of tasks=22 +5002 Task N11 current iteration: 2 +5006 Task N30 current iteration: 1 +5100 MainLoop run: 52. Skipped generating a task +5198 Task N13 current iteration: 3 +5200 MainLoop run: 53. Skipped generating a task +5293 Task N22 current iteration: 2 +5300 MainLoop run: 54. Generated a new task: 31 Int, Iter = 4359, 9 Free mem=711 No of tasks=23 +5303 Task N31 current iteration: 1 +5325 Task N5 current iteration: 2 +5343 Task N27 current iteration: 2 +5392 Task N20 current iteration: 2 +5400 MainLoop run: 55. Generated a new task: 32 Int, Iter = 837, 4 Free mem=666 No of tasks=24 +5403 Task N32 current iteration: 1 +5497 Task N28 current iteration: 2 +5501 MainLoop run: 56. Generated a new task: 33 Int, Iter = 274, 8 Free mem=621 No of tasks=25 +5505 Task N33 current iteration: 1 +5584 Task N12 current iteration: 3 +5600 MainLoop run: 57. Generated a new task: 34 Int, Iter = 923, 7 Free mem=576 No of tasks=26 +5603 Task N34 current iteration: 1 +5700 MainLoop run: 58. Generated a new task: 35 Int, Iter = 1007, 8 Free mem=531 No of tasks=27 +5703 Task N35 current iteration: 1 +5732 Task N9 current iteration: 3 +5753 Task N19 current iteration: 2 +5778 Task N33 current iteration: 2 +5789 Task N6 current iteration: 2 +5800 MainLoop run: 59. Skipped generating a task +5900 MainLoop run: 60. Generated a new task: 36 Int, Iter = 608, 4 Free mem=486 No of tasks=28 +5903 Task N36 current iteration: 1 +5984 Task N27 current iteration: 3 +5988 Task N18 current iteration: 2 +6000 MainLoop run: 61. Generated a new task: 37 Int, Iter = 4043, 3 Free mem=441 No of tasks=29 +6003 Task N37 current iteration: 1 +6052 Task N33 current iteration: 3 +6100 MainLoop run: 62. Skipped generating a task +6192 Task N28 current iteration: 3 +6200 MainLoop run: 63. Skipped generating a task +6239 Task N32 current iteration: 2 +6248 Task N15 current iteration: 2 +6300 MainLoop run: 64. Skipped generating a task +6322 Task N14 current iteration: 2 +6326 Task N33 current iteration: 4 +6400 MainLoop run: 65. Skipped generating a task +6410 Task N21 current iteration: 2 +6500 MainLoop run: 66. Skipped generating a task +6510 Task N36 current iteration: 2 +6525 Task N34 current iteration: 2 +6600 MainLoop run: 67. Skipped generating a task +6600 Task N33 current iteration: 5 +6625 Task N27 current iteration: 4 +6646 Task N13 current iteration: 4 +6683 Task N22 current iteration: 3 +6700 MainLoop run: 68. Generated a new task: 38 Int, Iter = 1907, 9 Free mem=396 No of tasks=30 +6703 Task N38 current iteration: 1 +6709 Task N35 current iteration: 2 +6800 MainLoop run: 69. Skipped generating a task +6874 Task N33 current iteration: 6 +6887 Task N28 current iteration: 4 +6900 MainLoop run: 70. Generated a new task: 39 Int, Iter = 2697, 1 Free mem=351 No of tasks=31 +6903 Task N39 current iteration: 1 +6906 Task N39 finished and destroyed. Free mem=353 No of tasks=30 +7000 MainLoop run: 71. Generated a new task: 40 Int, Iter = 2849, 4 Free mem=351 No of tasks=31 +7003 Task N40 current iteration: 1 +7076 Task N32 current iteration: 3 +7081 Task N20 current iteration: 3 +7100 MainLoop run: 72. Skipped generating a task +7118 Task N36 current iteration: 3 +7148 Task N33 current iteration: 7 +7200 MainLoop run: 73. Skipped generating a task +7266 Task N27 current iteration: 5 +7275 Task N12 current iteration: 4 +7300 MainLoop run: 74. Generated a new task: 41 Int, Iter = 4466, 4 Free mem=306 No of tasks=32 +7303 Task N41 current iteration: 1 +7343 Task N23 current iteration: 2 +7400 MainLoop run: 75. Skipped generating a task +7422 Task N33 current iteration: 8 +7422 Task N33 finished and destroyed. Free mem=308 No of tasks=31 +7448 Task N34 current iteration: 3 +7500 MainLoop run: 76. Generated a new task: 42 Int, Iter = 2133, 3 Free mem=306 No of tasks=32 +7503 Task N42 current iteration: 1 +7522 Task N17 current iteration: 2 +7582 Task N28 current iteration: 5 +7582 Task N28 finished and destroyed. Free mem=308 No of tasks=31 +7600 MainLoop run: 77. Skipped generating a task +7700 MainLoop run: 78. Skipped generating a task +7716 Task N35 current iteration: 3 +7726 Task N36 current iteration: 4 +7726 Task N36 finished and destroyed. Free mem=353 No of tasks=30 +7800 MainLoop run: 79. Generated a new task: 43 Int, Iter = 4113, 6 Free mem=351 No of tasks=31 +7803 Task N43 current iteration: 1 +7898 Task N11 current iteration: 3 +7900 MainLoop run: 80. Skipped generating a task +7907 Task N27 current iteration: 6 +7907 Task N27 finished and destroyed. Free mem=353 No of tasks=30 +7913 Task N32 current iteration: 4 +7913 Task N32 finished and destroyed. Free mem=398 No of tasks=29 +7947 Task N9 current iteration: 4 +8000 MainLoop run: 81. Skipped generating a task +8003 Task N19 current iteration: 3 +8073 Task N22 current iteration: 4 +8093 Task N30 current iteration: 2 +8094 Task N13 current iteration: 5 +8100 MainLoop run: 82. Skipped generating a task +8200 MainLoop run: 83. Generated a new task: 44 Int, Iter = 3389, 1 Free mem=396 No of tasks=30 +8203 Task N44 current iteration: 1 +8206 Task N44 finished and destroyed. Free mem=398 No of tasks=29 +8300 MainLoop run: 84. Generated a new task: 45 Int, Iter = 3548, 10 Free mem=396 No of tasks=30 +8303 Task N3 current iteration: 3 +8306 Task N45 current iteration: 1 +8371 Task N34 current iteration: 4 +8400 MainLoop run: 85. Skipped generating a task +8422 Task N29 current iteration: 2 +8485 Task N24 current iteration: 2 +8485 Task N24 finished and destroyed. Free mem=398 No of tasks=29 +8500 MainLoop run: 86. Generated a new task: 46 Int, Iter = 4962, 2 Free mem=396 No of tasks=30 +8503 Task N46 current iteration: 1 +8600 MainLoop run: 87. Skipped generating a task +8609 Task N38 current iteration: 2 +8700 MainLoop run: 88. Skipped generating a task +8723 Task N35 current iteration: 4 +8770 Task N20 current iteration: 4 +8800 MainLoop run: 89. Skipped generating a task +8873 Task N18 current iteration: 3 +8900 MainLoop run: 90. Skipped generating a task +8966 Task N12 current iteration: 5 +9000 MainLoop run: 91. Skipped generating a task +9017 Task N21 current iteration: 3 +9100 MainLoop run: 92. Skipped generating a task +9200 MainLoop run: 93. Skipped generating a task +9294 Task N34 current iteration: 5 +9300 MainLoop run: 94. Skipped generating a task +9385 Task N26 current iteration: 2 +9400 MainLoop run: 95. Generated a new task: 47 Int, Iter = 3556, 9 Free mem=351 No of tasks=31 +9403 Task N47 current iteration: 1 +9463 Task N22 current iteration: 5 +9500 MainLoop run: 96. Generated a new task: 48 Int, Iter = 1226, 3 Free mem=306 No of tasks=32 +9503 Task N48 current iteration: 1 +9542 Task N13 current iteration: 6 +9600 MainLoop run: 97. Generated a new task: 49 Int, Iter = 2850, 9 Free mem=261 No of tasks=33 +9603 Task N49 current iteration: 1 +9635 Task N42 current iteration: 2 +9661 Task N31 current iteration: 2 +9700 MainLoop run: 98. Skipped generating a task +9730 Task N35 current iteration: 5 +9800 MainLoop run: 99. Generated a new task: 50 Int, Iter = 2782, 10 Free mem=216 No of tasks=34 +9803 Task N50 current iteration: 1 +9851 Task N40 current iteration: 2 +9900 MainLoop run: 100. Skipped generating a task +9948 Task N5 current iteration: 3 +9993 Task N15 current iteration: 3 +10045 Task N37 current iteration: 2 +10162 Task N9 current iteration: 5 +10217 Task N34 current iteration: 6 +10241 Task N14 current iteration: 3 +10253 Task N19 current iteration: 4 +10253 Task N19 finished and destroyed. Free mem=218 No of tasks=33 +10459 Task N20 current iteration: 5 +10516 Task N38 current iteration: 3 +10657 Task N12 current iteration: 6 +10657 Task N12 finished and destroyed. Free mem=263 No of tasks=32 +10683 Task N23 current iteration: 3 +10728 Task N48 current iteration: 2 +10737 Task N35 current iteration: 6 +10776 Task N6 current iteration: 3 +10796 Task N11 current iteration: 4 +10853 Task N22 current iteration: 6 +10853 Task N22 finished and destroyed. Free mem=308 No of tasks=31 +10990 Task N13 current iteration: 7 +10990 Task N13 finished and destroyed. Free mem=353 No of tasks=30 +11140 Task N34 current iteration: 7 +11140 Task N34 finished and destroyed. Free mem=398 No of tasks=29 +11184 Task N30 current iteration: 3 +11624 Task N21 current iteration: 4 +11744 Task N35 current iteration: 7 +11758 Task N18 current iteration: 4 +11768 Task N41 current iteration: 2 +11768 Task N42 current iteration: 3 +11769 Task N42 finished and destroyed. Free mem=443 No of tasks=28 +11851 Task N45 current iteration: 2 +11915 Task N43 current iteration: 2 +11942 Task N29 current iteration: 3 +11942 Task N29 finished and destroyed. Free mem=488 No of tasks=27 +11954 Task N48 current iteration: 3 +11954 Task N48 finished and destroyed. Free mem=533 No of tasks=26 +12140 Task N17 current iteration: 3 +12148 Task N20 current iteration: 6 +12377 Task N9 current iteration: 6 +12399 Task N3 current iteration: 4 +12423 Task N38 current iteration: 4 +12452 Task N49 current iteration: 2 +12585 Task N50 current iteration: 2 +12700 Task N40 current iteration: 3 +12751 Task N35 current iteration: 8 +12751 Task N35 finished and destroyed. Free mem=578 No of tasks=25 +12958 Task N47 current iteration: 2 +13464 Task N46 current iteration: 2 +13464 Task N46 finished and destroyed. Free mem=623 No of tasks=24 +13694 Task N11 current iteration: 5 +13739 Task N15 current iteration: 4 +13837 Task N20 current iteration: 7 +13837 Task N20 finished and destroyed. Free mem=668 No of tasks=23 +14020 Task N31 current iteration: 3 +14023 Task N23 current iteration: 4 +14088 Task N37 current iteration: 3 +14088 Task N37 finished and destroyed. Free mem=713 No of tasks=22 +14160 Task N14 current iteration: 4 +14167 Task N26 current iteration: 3 +14231 Task N21 current iteration: 5 +14231 Task N21 finished and destroyed. Free mem=758 No of tasks=21 +14275 Task N30 current iteration: 4 +14330 Task N38 current iteration: 5 +14571 Task N5 current iteration: 4 +14592 Task N9 current iteration: 7 +14592 Task N9 finished and destroyed. Free mem=803 No of tasks=20 +14643 Task N18 current iteration: 5 +15302 Task N49 current iteration: 3 +15367 Task N50 current iteration: 3 +15399 Task N45 current iteration: 3 +15549 Task N40 current iteration: 4 +15549 Task N40 finished and destroyed. Free mem=848 No of tasks=19 +15763 Task N6 current iteration: 4 +15763 Task N6 finished and destroyed. Free mem=893 No of tasks=18 +16028 Task N43 current iteration: 3 +16234 Task N41 current iteration: 3 +16237 Task N38 current iteration: 6 +16498 Task N3 current iteration: 5 +16514 Task N47 current iteration: 3 +16592 Task N11 current iteration: 6 +16758 Task N17 current iteration: 4 +17363 Task N23 current iteration: 5 +17366 Task N30 current iteration: 5 +17483 Task N15 current iteration: 5 +17483 Task N15 finished and destroyed. Free mem=938 No of tasks=17 +17528 Task N18 current iteration: 6 +17528 Task N18 finished and destroyed. Free mem=983 No of tasks=16 +18079 Task N14 current iteration: 5 +18144 Task N38 current iteration: 7 +18149 Task N50 current iteration: 4 +18152 Task N49 current iteration: 4 +18379 Task N31 current iteration: 4 +18947 Task N45 current iteration: 4 +18949 Task N26 current iteration: 4 +19194 Task N5 current iteration: 5 +19490 Task N11 current iteration: 7 +20051 Task N38 current iteration: 8 +20070 Task N47 current iteration: 4 +20141 Task N43 current iteration: 4 +20457 Task N30 current iteration: 6 +20597 Task N3 current iteration: 6 +20700 Task N41 current iteration: 4 +20700 Task N41 finished and destroyed. Free mem=1028 No of tasks=15 +20703 Task N23 current iteration: 6 +20931 Task N50 current iteration: 5 +21002 Task N49 current iteration: 5 +21376 Task N17 current iteration: 5 +21958 Task N38 current iteration: 9 +21958 Task N38 finished and destroyed. Free mem=1073 No of tasks=14 +21998 Task N14 current iteration: 6 +22388 Task N11 current iteration: 8 +22495 Task N45 current iteration: 5 +22738 Task N31 current iteration: 5 +23548 Task N30 current iteration: 7 +23626 Task N47 current iteration: 5 +23713 Task N50 current iteration: 6 +23731 Task N26 current iteration: 5 +23817 Task N5 current iteration: 6 +23852 Task N49 current iteration: 6 +24043 Task N23 current iteration: 7 +24254 Task N43 current iteration: 5 +24696 Task N3 current iteration: 7 +25286 Task N11 current iteration: 9 +25286 Task N11 finished and destroyed. Free mem=1118 No of tasks=13 +25917 Task N14 current iteration: 7 +25917 Task N14 finished and destroyed. Free mem=1163 No of tasks=12 +25994 Task N17 current iteration: 6 +26043 Task N45 current iteration: 6 +26496 Task N50 current iteration: 7 +26639 Task N30 current iteration: 8 +26702 Task N49 current iteration: 7 +27097 Task N31 current iteration: 6 +27182 Task N47 current iteration: 6 +27383 Task N23 current iteration: 8 +27383 Task N23 finished and destroyed. Free mem=1208 No of tasks=11 +28367 Task N43 current iteration: 6 +28367 Task N43 finished and destroyed. Free mem=1253 No of tasks=10 +28440 Task N5 current iteration: 7 +28440 Task N5 finished and destroyed. Free mem=1298 No of tasks=9 +28513 Task N26 current iteration: 6 +28795 Task N3 current iteration: 8 +29277 Task N50 current iteration: 8 +29552 Task N49 current iteration: 8 +29591 Task N45 current iteration: 7 +29730 Task N30 current iteration: 9 +29730 Task N30 finished and destroyed. Free mem=1343 No of tasks=8 +30612 Task N17 current iteration: 7 +30738 Task N47 current iteration: 7 +31456 Task N31 current iteration: 7 +32059 Task N50 current iteration: 9 +32402 Task N49 current iteration: 9 +32402 Task N49 finished and destroyed. Free mem=1388 No of tasks=7 +32894 Task N3 current iteration: 9 +32894 Task N3 finished and destroyed. Free mem=1433 No of tasks=6 +33139 Task N45 current iteration: 8 +33295 Task N26 current iteration: 7 +34294 Task N47 current iteration: 8 +34841 Task N50 current iteration: 10 +34841 Task N50 finished and destroyed. Free mem=1478 No of tasks=5 +35230 Task N17 current iteration: 8 +35815 Task N31 current iteration: 8 +36687 Task N45 current iteration: 9 +37850 Task N47 current iteration: 9 +37850 Task N47 finished and destroyed. Free mem=1523 No of tasks=4 +38077 Task N26 current iteration: 8 +39848 Task N17 current iteration: 9 +40174 Task N31 current iteration: 9 +40174 Task N31 finished and destroyed. Free mem=1568 No of tasks=3 +40235 Task N45 current iteration: 10 +40235 Task N45 finished and destroyed. Free mem=1613 No of tasks=2 +42859 Task N26 current iteration: 9 +44466 Task N17 current iteration: 10 +44466 Task N17 finished and destroyed. Free mem=1658 No of tasks=1 +47641 Task N26 current iteration: 10 +47641 Task N26 finished and destroyed. Free mem=1703 No of tasks=0 + + */ diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object.ino b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object.ino new file mode 100644 index 0000000..fdfa1c9 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object.ino @@ -0,0 +1,185 @@ +/** + This is example 5 rewritten with Timeout, LTS, WDT functioanlity + multitab and extra classes + - 1 second timeout is set for the main calculation task + - LTS is used to address task-specific sensor class object + - WDT is used to set the Task ID and use that for identifying the tasks (debug) + + Original description: + ==================== + This test emulates querying 1 to 10 sensors once every 10 seconds, each could respond with a different delay + (ultrasonic sensors for instance) and printing a max value of them when all have reported their values. + The overall timeout of 1 second is setup as well. + An error message needs to be printed if a timeout occurred instead of a distance value. + + Task and SuperSensor objects are dynamically created and destroyed as needed every 10 seconds +*/ + +// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +#define _TASK_LTS_POINTER // Compile with support for local task storage pointer +#define _TASK_PRIORITY // Support for layered scheduling priority +// #define _TASK_MICRO_RES // Support for microsecond resolution +// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY) +#define _TASK_DEBUG // Make all methods and variables public for debug purposes +#define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations +#define _TASK_TIMEOUT // Support for overall task timeout + +#include +#include "SuperSensor.h" + +StatusRequest measure; + +Scheduler ts, hts; + +// Callback methods prototypes +void CycleCallback(); +void CalcCallback(); +bool CalcEnable(); +void CalcDisable(); +void SCallback(); +bool SEnable(); +void SDisable(); + +// Tasks + +Task tCycle(10000, TASK_FOREVER, &CycleCallback, &hts); +Task tCalculate(TASK_IMMEDIATE , TASK_ONCE, &CalcCallback, &hts, false, &CalcEnable, &CalcDisable); + +int numberSensors; +long distance; +int pins[] = { 1, 9, 3, 7, 5, 6, 4, 8, 2, 10 }; + +void CycleCallback() { + Serial.println();Serial.println();Serial.println(); + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); + Serial.print("Number of sensors="); + + numberSensors = random(1, 11); // 1 to 10 sensors, randomly + distance = 0; + Serial.println(numberSensors); + + measure.setWaiting(numberSensors); // Set the StatusRequest to wait for 3 signals. + tCalculate.waitFor(&measure); + tCalculate.setTimeout(1000 * TASK_MILLISECOND); +} + +bool CalcEnable() { + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CalcEnable: OnEnable"); + Serial.println("Activating sensors"); + + + for (int i = 0; i < numberSensors; i++) { + Task *t = new Task(TASK_MILLISECOND, TASK_FOREVER, &SCallback, &ts, false, &SEnable, &SDisable); + SuperSensor *s = new SuperSensor( pins[i] ); + t->setLtsPointer( (void*) s); + t->setId(i + 1); + + s->begin(); + + t->restartDelayed(); + } + + return true; +} + +void CalcDisable() { + if (tCalculate.timedOut()) { + measure.signalComplete(-1); // signal error + Serial.print(millis()); Serial.print(":\t"); + Serial.println("MeasureCallback: ***** Timeout *****"); + } + ts.disableAll(false); // only disable tasks in the ts scheduler +} + + +void CalcCallback() { + Serial.print(millis()); Serial.print(":\t"); + Serial.println("CalcCallback: calculating"); + if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully + Serial.print("CalcCallback: Max distance="); Serial.println(distance); + Serial.println(); + } +} + + +/** Simulation code for all sensors + ------------------------------- +*/ +bool SEnable() { + Task &t = ts.currentTask(); + int i = t.getId(); + + Serial.print(millis()); Serial.print(":\t"); + Serial.print("SEnable: TaskID="); + Serial.println(i); + Serial.print("Triggering sensor. Delay="); + + + // Another way to update the distances with one codebase - use LTS pointers + SuperSensor *s = (SuperSensor*) t.getLtsPointer(); + + long dly = s->trigger(); + + + Serial.println( dly ); + return true; +} + +void SCallback() { + Task &t = ts.currentTask(); + + SuperSensor *s = (SuperSensor*) t.getLtsPointer(); + if ( s->measurementReady() ) { + int i = t.getId(); + Serial.print(millis()); Serial.print(":\t"); + Serial.print("SCallback: TaskID="); + Serial.println(i); + Serial.print("Emulating measurement. d="); + + long d = s->value(); + if ( d > distance ) distance = d; + + Serial.println(d); + + measure.signal(); + t.disable(); + } +} + +void SDisable() { + Task &t = ts.currentTask(); + int i = t.getId(); + + Serial.print(millis()); Serial.print(":\t"); + Serial.print("SDisable: TaskID="); + Serial.println(i); + + SuperSensor *s = (SuperSensor*) ts.currentLts(); + s->stop(); + + delete s; + delete &t; +} + +/** Main Arduino code + Not much is left here - everything is taken care of by the framework +*/ +void setup() { + + Serial.begin(115200); + Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); + randomSeed(analogRead(A0) + millis()); + + ts.setHighPriorityScheduler(&hts); + + tCalculate.setTimeout(1 * TASK_SECOND); + tCycle.enable(); +} + +void loop() { + ts.execute(); +} diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.cpp b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.cpp new file mode 100644 index 0000000..231c2ea --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.cpp @@ -0,0 +1,36 @@ +#include "SuperSensor.h" + +SuperSensor::SuperSensor(int aPin) { + iPin = aPin; +} + +SuperSensor::~SuperSensor() { + iValue = -1; +} + +void SuperSensor::begin() { + iDelay = random(300, 1500); + iValue = -1; +} + +void SuperSensor::stop() { + //nothing to do +} + +long SuperSensor::trigger() { + iStart = millis(); + return iDelay; +} + +bool SuperSensor::measurementReady() { + if ( millis() - iStart > iDelay ) { + iValue = random(501); + return true; + } + return false; +} + +long SuperSensor::value() { + return iValue; +} + diff --git a/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.h b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.h new file mode 100644 index 0000000..eb491bf --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.h @@ -0,0 +1,25 @@ +#ifndef _SUPER_SENSOR_H +#define _SUPER_SENSOR_H + + +#include "Arduino.h" +#include + +class SuperSensor { + public: + SuperSensor(int aPin); + ~SuperSensor(); + void begin(); + void stop(); + long trigger(); + bool measurementReady(); + long value(); + + private: + long iDelay; + long iValue; + int iPin; + unsigned long iStart; +}; + +#endif // _SUPER_SENSOR_H diff --git a/.piolibdeps/TaskScheduler_ID721/extras/TaskScheduler.doc b/.piolibdeps/TaskScheduler_ID721/extras/TaskScheduler.doc new file mode 100644 index 0000000..c60b980 Binary files /dev/null and b/.piolibdeps/TaskScheduler_ID721/extras/TaskScheduler.doc differ diff --git a/.piolibdeps/TaskScheduler_ID721/extras/TaskScheduler.pdf b/.piolibdeps/TaskScheduler_ID721/extras/TaskScheduler.pdf new file mode 100644 index 0000000..7a2df8a Binary files /dev/null and b/.piolibdeps/TaskScheduler_ID721/extras/TaskScheduler.pdf differ diff --git a/.piolibdeps/TaskScheduler_ID721/extras/TaskScheduler_html.png b/.piolibdeps/TaskScheduler_ID721/extras/TaskScheduler_html.png new file mode 100644 index 0000000..febb8c7 Binary files /dev/null and b/.piolibdeps/TaskScheduler_ID721/extras/TaskScheduler_html.png differ diff --git a/.piolibdeps/TaskScheduler_ID721/keywords.txt b/.piolibdeps/TaskScheduler_ID721/keywords.txt new file mode 100644 index 0000000..fc3b4b9 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/keywords.txt @@ -0,0 +1,104 @@ +####################################### +# Syntax Coloring Map For TaskManager +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Task KEYWORD1 +Scheduler KEYWORD1 +StatusRequest KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +init KEYWORD2 +addTask KEYWORD2 +deleteTask KEYWORD2 +disableAll KEYWORD2 +enableAll KEYWORD2 +currentTask KEYWORD2 +currentLts KEYWORD2 +execute KEYWORD2 +timeUntilNextIteration KEYWORD2 +startNow KEYWORD2 +allowSleep KEYWORD2 +enable KEYWORD2 +enableIfNot KEYWORD2 +enableDelayed KEYWORD2 +delay KEYWORD2 +forceNextIteration KEYWORD2 +restart KEYWORD2 +restartDelayed KEYWORD2 +disable KEYWORD2 +isEnabled KEYWORD2 +set KEYWORD2 +setInterval KEYWORD2 +getInterval KEYWORD2 +setIterations KEYWORD2 +getIterations KEYWORD2 +getRunCounter KEYWORD2 +setCallback KEYWORD2 +setOnEnable KEYWORD2 +setOnDisable KEYWORD2 +disableOnLastIteration KEYWORD2 +yield KEYWORD2 +yieldOnce KEYWORD2 +getInternalStatusRequest KEYWORD2 +getCount KEYWORD2 +getOverrun KEYWORD2 +getStartDelay KEYWORD2 +isFirstIteration KEYWORD2 +isLastIteration KEYWORD2 +setWaiting KEYWORD2 +signal KEYWORD2 +signalComplete KEYWORD2 +pending KEYWORD2 +completed KEYWORD2 +getStatus KEYWORD2 +waitFor KEYWORD2 +waitForDelayed KEYWORD2 +getStatusRequest KEYWORD2 +getCount KEYWORD2 +setId KEYWORD2 +getId KEYWORD2 +setControlPoint KEYWORD2 +getControlPoint KEYWORD2 +setLtsPointer KEYWORD2 +getLtsPointer KEYWORD2 +isOverrun KEYWORD2 +setHighPriorityScheduler KEYWORD2 +currentScheduler KEYWORD2 +setTimeout KEYWORD2 +resetTimeout KEYWORD2 +getTimeout KEYWORD2 +untilTimeout KEYWORD2 +timedOut KEYWORD2 +####################################### +# Constants (LITERAL1) +TASK_MILLISECOND LITERAL1 +TASK_SECOND LITERAL1 +TASK_MINUTE LITERAL1 +TASK_HOUR LITERAL1 +TASK_FOREVER LITERAL1 +TASK_IMMEDIATE LITERAL1 +TASK_ONCE LITERAL1 +TASK_NOTIMEOUT LITERAL1 +_TASK_TIMECRITICAL LITERAL1 +_TASK_SLEEP_ON_IDLE_RUN LITERAL1 +_TASK_STATUS_REQUEST LITERAL1 +_TASK_WDT_IDS LITERAL1 +_TASK_LTS_POINTER LITERAL1 +_TASK_PRIORITY LITERAL1 +_TASK_MICRO_RES LITERAL1 +_TASK_STD_FUNCTION LITERAL1 +_TASK_DEBUG LITERAL1 +_TASK_INLINE LITERAL1 +_TASK_TIMEOUT LITERAL1 +TaskCallback LITERAL1 +TaskOnDisable LITERAL1 +TaskOnEnable LITERAL1 +####################################### + diff --git a/.piolibdeps/TaskScheduler_ID721/library.json b/.piolibdeps/TaskScheduler_ID721/library.json new file mode 100644 index 0000000..27214bc --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/library.json @@ -0,0 +1,22 @@ +{ + "name": "TaskScheduler", + "keywords": "multitasking, cooperative, event, task, taskscheduler, scheduling", + "description": "Cooperative multitasking for Arduino and ESP8266 microcontrollers", + "repository": + { + "type": "git", + "url": "https://github.com/arkhipenko/TaskScheduler" + }, + "authors": + [ + { + "name": "Anatoli Arkhipenko", + "email": "arkhipenko@hotmail.com", + "url": "https://github.com/arkhipenko", + "maintainer": true + } + ], + "version": "2.6.1", + "frameworks": "arduino", + "platforms": "*" +} diff --git a/.piolibdeps/TaskScheduler_ID721/library.properties b/.piolibdeps/TaskScheduler_ID721/library.properties new file mode 100644 index 0000000..655dbd7 --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/library.properties @@ -0,0 +1,9 @@ +name=TaskScheduler +version=2.6.1 +author=Anatoli Arkhipenko +maintainer=Anatoli Arkhipenko +sentence=A light-weight cooperative multitasking library for arduino and esp8266 microcontrollers. +paragraph=Supports: periodic task execution (with dynamic execution period in milliseconds or microseconds – frequency of execution), number of iterations (limited or infinite number of iterations), execution of tasks in predefined sequence, dynamic change of task execution parameters (frequency, number of iterations, callback methods), power saving via entering IDLE sleep mode when tasks are not scheduled to run, event-driven task invocation via Status Request object, task IDs and Control Points for error handling and watchdog timer, Local Task Storage pointer (allowing use of same callback code for multiple tasks), layered task prioritization, std::functions (esp8266, esp32 only), overall task timeout. +category=Timing +url=https://github.com/arkhipenko/TaskScheduler.git +architectures=* diff --git a/.piolibdeps/TaskScheduler_ID721/src/TaskScheduler.h b/.piolibdeps/TaskScheduler_ID721/src/TaskScheduler.h new file mode 100644 index 0000000..8e3f84e --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/src/TaskScheduler.h @@ -0,0 +1,930 @@ +// Cooperative multitasking library for Arduino +// Copyright (c) 2015-2017 Anatoli Arkhipenko +// +// Changelog: +// v1.0.0: +// 2015-02-24 - Initial release +// 2015-02-28 - added delay() and disableOnLastIteration() methods +// 2015-03-25 - changed scheduler execute() method for a more precise delay calculation: +// 1. Do not delay if any of the tasks ran (making request for immediate execution redundant) +// 2. Delay is invoked only if none of the tasks ran +// 3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute method itself. +// 2015-05-11 - added restart() and restartDelayed() methods to restart tasks which are on hold after running all iterations +// 2015-05-19 - completely removed delay from the scheduler since there are no power saving there. using 1 ms sleep instead +// +// v1.4.1: +// 2015-09-15 - more careful placement of AVR-specific includes for sleep method (compatibility with DUE) +// sleep on idle run is no longer a default and should be explicitly compiled with +// _TASK_SLEEP_ON_IDLE_RUN defined +// +// v1.5.0: +// 2015-09-20 - access to currently executing task (for callback methods) +// 2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain +// 2015-09-20 - option to create a task already enabled +// +// v1.5.1: +// 2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations(). +// Thanks to Hannes Morgenstern for catching this one +// +// v1.6.0: +// 2015-09-22 - revert back to having all tasks disable on last iteration. +// 2015-09-22 - deprecated disableOnLastIteration method as a result +// 2015-09-22 - created a separate branch 'disable-on-last-iteration' for this +// 2015-10-01 - made version numbers semver compliant (documentation only) +// +// v1.7.0: +// 2015-10-08 - introduced callback run counter - callback methods can branch on the iteration number. +// 2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled, +// false if was disabled. +// 2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled) +// 2015-10-11 - introduced callback methods "on enable" and "on disable". On enable runs every time enable is called, +// on disable runs only if task was enabled +// 2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass +// regardless how much time is left +// +// v1.8.0: +// 2015-10-13 - support for status request objects allowing tasks waiting on requests +// 2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch +// +// v1.8.1: +// 2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging +// +// v1.8.2: +// 2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks) +// 2015-10-27 - bug: currentTask() method returns incorrect Task reference if called within OnEnable and OnDisable methods +// 2015-10-27 - protection against infinite loop in OnEnable (if enable() methods are called within OnEnable) +// 2015-10-29 - new currentLts() method in the scheduler class returns current task's LTS pointer in one call +// +// v1.8.3: +// 2015-11-05 - support for task activation on a status request with arbitrary interval and number of iterations +// (0 and 1 are still default values) +// 2015-11-05 - implement waitForDelayed() method to allow task activation on the status request completion +// delayed for one current interval +// 2015-11-09 - added callback methods prototypes to all examples for Arduino IDE 1.6.6 compatibility +// 2015-11-14 - added several constants to be used as task parameters for readability (e.g, TASK_FOREVER, TASK_SECOND, etc.) +// 2015-11-14 - significant optimization of the scheduler's execute loop, including millis() rollover fix option +// +// v1.8.4: +// 2015-11-15 - bug fix: Task alignment with millis() for scheduling purposes should be done after OnEnable, not before. +// Especially since OnEnable method can change the interval +// 2015-11-16 - further optimizations of the task scheduler execute loop +// +// v1.8.5: +// 2015-11-23 - bug fix: incorrect calculation of next task invocation in case callback changed the interval +// 2015-11-23 - bug fix: Task::set() method calls setInterval() explicitly, therefore delaying the task in the same manner +// +// v1.9.0: +// 2015-11-24 - packed three byte-long status variables into bit array structure data type - saving 2 bytes per each task instance +// +// v1.9.2: +// 2015-11-28 - _TASK_ROLLOVER_FIX is deprecated (not necessary) +// 2015-12-16 - bug fixes: automatic millis rollover support for delay methods +// 2015-12-17 - new method for _TASK_TIMECRITICAL option: getStartDelay() +// +// v2.0.0: +// 2015-12-22 - _TASK_PRIORITY - support for layered task prioritization +// +// v2.0.1: +// 2016-01-02 - bug fix: issue#11 Xtensa compiler (esp8266): Declaration of constructor does not match implementation +// +// v2.0.2: +// 2016-01-05 - bug fix: time constants wrapped inside compile option +// 2016-01-05 - support for ESP8266 wifi power saving mode for _TASK_SLEEP_ON_IDLE_RUN compile option +// +// v2.1.0: +// 2016-02-01 - support for microsecond resolution +// 2016-02-02 - added Scheduler baseline start time reset method: startNow() +// +// v2.2.0: +// 2016-11-17 - all methods made 'inline' to support inclusion of TaskSchedule.h file into other header files +// +// v2.2.1: +// 2016-11-30 - inlined constructors. Added "yield()" and "yieldOnce()" functions to easily break down and chain +// back together long running callback methods +// 2016-12-16 - added "getCount()" to StatusRequest objects, made every task StatusRequest enabled. +// Internal StatusRequest objects are accessible via "getInternalStatusRequest()" method. +// +// v2.3.0: +// 2017-02-24 - new timeUntilNextIteration() method within Scheduler class - inquire when a particlar task is +// scheduled to run next time +// +// v2.4.0: +// 2017-04-27 - added destructor to the Task class to ensure tasks are disables and taken off the execution chain +// upon destruction. (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder) +// +// v2.5.0: +// 2017-04-27 - ESP8266 ONLY: added optional support for std::functions via _TASK_STD_FUNCTION compilation option +// (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder) +// 2017-08-30 - add _TASK_DEBUG making all methods and variables public FOR DEBUGGING PURPOSES ONLY! +// Use at your own risk! +// 2017-08-30 - bug fix: Scheduler::addTask() checks if task is already part of an execution chain (github issue #37) +// 2017-08-30 - support for multi-tab sketches (Contributed by Adam Ryczkowski - https://github.com/adamryczkowski) +// +// v2.5.1: +// 2018-01-06 - support for IDLE sleep on Teensy boards (tested on Teensy 3.5) +// +// v2.5.2: +// 2018-01-09 - _TASK_INLINE compilation directive making all methods declared "inline" (issue #42) +// +// v2.6.0: +// 2018-01-30 - _TASK_TIMEOUT compilation directive: Task overall timeout functionality +// 2018-01-30 - ESP32 support (experimental) +// (Contributed by Marco Tombesi: https://github.com/baggior) +// +// v2.6.1: +// 2018-02-13 - Bug: support for task self-destruction in the OnDisable method +// Example 19: dynamic tasks creation and destruction +// 2018-03-14 - Bug: high level scheduler ignored if lower level chain is empty +// Example 20: use of local task storage to work with task-specific class objects + + +#include +#include "TaskSchedulerDeclarations.h" + +#ifndef _TASKSCHEDULER_H_ +#define _TASKSCHEDULER_H_ + +// ---------------------------------------- +// The following "defines" control library functionality at compile time, +// and should be used in the main sketch depending on the functionality required +// +// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer +// #define _TASK_PRIORITY // Support for layered scheduling priority +// #define _TASK_MICRO_RES // Support for microsecond resolution +// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) +// #define _TASK_DEBUG // Make all methods and variables public for debug purposes +// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations +// #define _TASK_TIMEOUT // Support for overall task timeout + + #ifdef _TASK_MICRO_RES + + #undef _TASK_SLEEP_ON_IDLE_RUN // SLEEP_ON_IDLE has only millisecond resolution + #define _TASK_TIME_FUNCTION() micros() + + #else + #define _TASK_TIME_FUNCTION() millis() + + #endif // _TASK_MICRO_RES + + +#ifdef _TASK_SLEEP_ON_IDLE_RUN + +#ifdef ARDUINO_ARCH_AVR +#include +#include +#endif // ARDUINO_ARCH_AVR + +#ifdef ARDUINO_ARCH_ESP8266 +#define _TASK_ESP8266_DLY_THRESHOLD 200L +extern "C" { +#include "user_interface.h" +} +#endif //ARDUINO_ARCH_ESP8266 + +#ifdef ARDUINO_ARCH_ESP32 +#define _TASK_ESP8266_DLY_THRESHOLD 200L +#warning _TASK_SLEEP_ON_IDLE_RUN for ESP32 cannot use light sleep mode but a standard delay for 1 ms +#endif // ARDUINO_ARCH_ESP32 + +#endif // _TASK_SLEEP_ON_IDLE_RUN + + +#if !defined (ARDUINO_ARCH_ESP8266) && !defined (ARDUINO_ARCH_ESP32) +#ifdef _TASK_STD_FUNCTION + #error Support for std::function only for ESP8266 or ESP32 architecture +#undef _TASK_STD_FUNCTION +#endif // _TASK_STD_FUNCTION +#endif // ARDUINO_ARCH_ESP8266 + +#ifdef _TASK_WDT_IDS + static unsigned int __task_id_counter = 0; // global task ID counter for assiging task IDs automatically. +#endif // _TASK_WDT_IDS + +#ifdef _TASK_PRIORITY + Scheduler* iCurrentScheduler; +#endif // _TASK_PRIORITY + + +// ------------------ TaskScheduler implementation -------------------- + + +/** Constructor, uses default values for the parameters + * so could be called with no parameters. + */ +Task::Task( unsigned long aInterval, long aIterations, TaskCallback aCallback, Scheduler* aScheduler, bool aEnable, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable ) { + reset(); + set(aInterval, aIterations, aCallback, aOnEnable, aOnDisable); + if (aScheduler) aScheduler->addTask(*this); + +#ifdef _TASK_WDT_IDS + iTaskID = ++__task_id_counter; +#endif // _TASK_WDT_IDS + + if (aEnable) enable(); +} + +/** Destructor. + * Makes sure the task disabled and deleted out of the chain + * prior to being deleted. + */ +Task::~Task() { + disable(); + if (iScheduler) + iScheduler->deleteTask(*this); +} + + +#ifdef _TASK_STATUS_REQUEST + +/** Constructor with reduced parameter list for tasks created for + * StatusRequest only triggering (always immediate and only 1 iteration) + */ +Task::Task( TaskCallback aCallback, Scheduler* aScheduler, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable ) { + reset(); + set(TASK_IMMEDIATE, TASK_ONCE, aCallback, aOnEnable, aOnDisable); + if (aScheduler) aScheduler->addTask(*this); + +#ifdef _TASK_WDT_IDS + iTaskID = ++__task_id_counter; +#endif // _TASK_WDT_IDS +} + + +StatusRequest::StatusRequest() +{ + iCount = 0; + iStatus = 0; +} + +void StatusRequest::setWaiting(unsigned int aCount) { iCount = aCount; iStatus = 0; } +bool StatusRequest::pending() { return (iCount != 0); } +bool StatusRequest::completed() { return (iCount == 0); } +int StatusRequest::getStatus() { return iStatus; } +int StatusRequest::getCount() { return iCount; } +StatusRequest* Task::getStatusRequest() { return iStatusRequest; } +StatusRequest* Task::getInternalStatusRequest() { return &iMyStatusRequest; } + +/** Signals completion of the StatusRequest by one of the participating events + * @param: aStatus - if provided, sets the return code of the StatusRequest: negative = error, 0 (default) = OK, positive = OK with a specific status code + * Negative status will complete Status Request fully (since an error occured). + * @return: true, if StatusRequest is complete, false otherwise (still waiting for other events) + */ +bool StatusRequest::signal(int aStatus) { + if ( iCount) { // do not update the status request if it was already completed + if (iCount > 0) --iCount; + if ( (iStatus = aStatus) < 0 ) iCount = 0; // if an error is reported, the status is requested to be completed immediately + } + return (iCount == 0); +} + +void StatusRequest::signalComplete(int aStatus) { + if (iCount) { // do not update the status request if it was already completed + iCount = 0; + iStatus = aStatus; + } +} + +/** Sets a Task to wait until a particular event completes + * @param: aStatusRequest - a pointer for the StatusRequest to wait for. + * If aStatusRequest is NULL, request for waiting is ignored, and the waiting task is not enabled. + */ +void Task::waitFor(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) { + if ( ( iStatusRequest = aStatusRequest) ) { // assign internal StatusRequest var and check if it is not NULL + setIterations(aIterations); + setInterval(aInterval); + iStatus.waiting = _TASK_SR_NODELAY; // no delay + enable(); + } +} + +void Task::waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) { + if ( ( iStatusRequest = aStatusRequest) ) { // assign internal StatusRequest var and check if it is not NULL + setIterations(aIterations); + if ( aInterval ) setInterval(aInterval); // For the dealyed version only set the interval if it was not a zero + iStatus.waiting = _TASK_SR_DELAY; // with delay equal to the current interval + enable(); + } +} +#endif // _TASK_STATUS_REQUEST + +bool Task::isEnabled() { return iStatus.enabled; } + +unsigned long Task::getInterval() { return iInterval; } + +long Task::getIterations() { return iIterations; } + +unsigned long Task::getRunCounter() { return iRunCounter; } + +void Task::setCallback(TaskCallback aCallback) { iCallback = aCallback; } + +void Task::setOnEnable(TaskOnEnable aCallback) { iOnEnable = aCallback; } + +void Task::setOnDisable(TaskOnDisable aCallback) { iOnDisable = aCallback; } + +/** Resets (initializes) the task/ + * Task is not enabled and is taken out + * out of the execution chain as a result + */ +void Task::reset() { + iStatus.enabled = false; + iStatus.inonenable = false; + iPreviousMillis = 0; + iInterval = iDelay = 0; + iPrev = NULL; + iNext = NULL; + iScheduler = NULL; + iRunCounter = 0; + +#ifdef _TASK_TIMECRITICAL + iOverrun = 0; + iStartDelay = 0; +#endif // _TASK_TIMECRITICAL + +#ifdef _TASK_WDT_IDS + iControlPoint = 0; +#endif // _TASK_WDT_IDS + +#ifdef _TASK_LTS_POINTER + iLTS = NULL; +#endif // _TASK_LTS_POINTER + +#ifdef _TASK_STATUS_REQUEST + iStatusRequest = NULL; + iStatus.waiting = 0; + iMyStatusRequest.signalComplete(); +#endif // _TASK_STATUS_REQUEST + +#ifdef _TASK_TIMEOUT + iTimeout = 0; + iStarttime = 0; + iStatus.timeout = false; +#endif // _TASK_TIMEOUT +} + +/** Explicitly set Task execution parameters + * @param aInterval - execution interval in ms + * @param aIterations - number of iterations, use -1 for no limit + * @param aCallback - pointer to the callback method which executes the task actions + * @param aOnEnable - pointer to the callback method which is called on enable() + * @param aOnDisable - pointer to the callback method which is called on disable() + */ +void Task::set(unsigned long aInterval, long aIterations, TaskCallback aCallback, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable) { + setInterval(aInterval); + iSetIterations = iIterations = aIterations; + iCallback = aCallback; + iOnEnable = aOnEnable; + iOnDisable = aOnDisable; +} + +/** Sets number of iterations for the task + * if task is enabled, schedule for immediate execution + * @param aIterations - number of iterations, use -1 for no limit + */ +void Task::setIterations(long aIterations) { + iSetIterations = iIterations = aIterations; +} + +/** Prepare task for next step iteration following yielding of control to the scheduler + * @param aCallback - pointer to the callback method for the next step + */ +void Task::yield (TaskCallback aCallback) { + iCallback = aCallback; + forceNextIteration(); + + // The next 2 lines adjust runcounter and number of iterations + // as if it is the same run of the callback, just split between + // a series of callback methods + iRunCounter--; + if ( iIterations >= 0 ) iIterations++; +} + +/** Prepare task for next step iteration following yielding of control to the scheduler + * @param aCallback - pointer to the callback method for the next step + */ +void Task::yieldOnce (TaskCallback aCallback) { + yield(aCallback); + iIterations = 1; +} + +/** Enables the task + * schedules it for execution as soon as possible, + * and resets the RunCounter back to zero + */ +void Task::enable() { + if (iScheduler) { // activation without active scheduler does not make sense + iRunCounter = 0; + if ( iOnEnable && !iStatus.inonenable ) { + Task *current = iScheduler->iCurrent; + iScheduler->iCurrent = this; + iStatus.inonenable = true; // Protection against potential infinite loop + iStatus.enabled = iOnEnable(); + iStatus.inonenable = false; // Protection against potential infinite loop + iScheduler->iCurrent = current; + } + else { + iStatus.enabled = true; + } + iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval); + +#ifdef _TASK_TIMEOUT + resetTimeout(); +#endif // _TASK_TIMEOUT + +#ifdef _TASK_STATUS_REQUEST + if ( iStatus.enabled ) { + iMyStatusRequest.setWaiting(); + } +#endif // _TASK_STATUS_REQUEST + } +} + +/** Enables the task only if it was not enabled already + * Returns previous state (true if was already enabled, false if was not) + */ +bool Task::enableIfNot() { + bool previousEnabled = iStatus.enabled; + if ( !previousEnabled ) enable(); + return (previousEnabled); +} + +/** Enables the task + * and schedules it for execution after a delay = aInterval + */ +void Task::enableDelayed(unsigned long aDelay) { + enable(); + delay(aDelay); +} + +#ifdef _TASK_TIMEOUT +void Task::setTimeout(unsigned long aTimeout, bool aReset) { + iTimeout = aTimeout; + if (aReset) resetTimeout(); +} + +void Task::resetTimeout() { + iStarttime = _TASK_TIME_FUNCTION(); + iStatus.timeout = false; +} + +unsigned long Task::getTimeout() { + return iTimeout; +} + +long Task::untilTimeout() { + if ( iTimeout ) { + return ( (long) (iStarttime + iTimeout) - (long) _TASK_TIME_FUNCTION() ); + } + return -1; +} + +bool Task::timedOut() { + return iStatus.timeout; +} + +#endif // _TASK_TIMEOUT + + + +/** Delays Task for execution after a delay = aInterval (if task is enabled). + * leaves task enabled or disabled + * if aDelay is zero, delays for the original scheduling interval from now + */ +void Task::delay(unsigned long aDelay) { +// if (!aDelay) aDelay = iInterval; + iDelay = aDelay ? aDelay : iInterval; + iPreviousMillis = _TASK_TIME_FUNCTION(); // - iInterval + aDelay; +} + +/** Schedules next iteration of Task for execution immediately (if enabled) + * leaves task enabled or disabled + * Task's original schedule is shifted, and all subsequent iterations will continue from this point in time + */ +void Task::forceNextIteration() { + iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval); +} + +/** Sets the execution interval. + * Task execution is delayed for aInterval + * Use enable() to schedule execution ASAP + * @param aInterval - new execution interval + */ +void Task::setInterval (unsigned long aInterval) { + iInterval = aInterval; + delay(); // iDelay will be updated by the delay() function +} + +/** Disables task + * Task will no longer be executed by the scheduler + * Returns status of the task before disable was called (i.e., if the task was already disabled) + */ +bool Task::disable() { + bool previousEnabled = iStatus.enabled; + iStatus.enabled = false; + iStatus.inonenable = false; + if (previousEnabled && iOnDisable) { + Task *current = iScheduler->iCurrent; + iScheduler->iCurrent = this; + iOnDisable(); + iScheduler->iCurrent = current; + } +#ifdef _TASK_STATUS_REQUEST + iMyStatusRequest.signalComplete(); +#endif + return (previousEnabled); +} + +/** Restarts task + * Task will run number of iterations again + */ + +void Task::restart() { + enable(); + iIterations = iSetIterations; +} + +/** Restarts task delayed + * Task will run number of iterations again + */ +void Task::restartDelayed(unsigned long aDelay) { + enableDelayed(aDelay); + iIterations = iSetIterations; +} + +bool Task::isFirstIteration() { return (iRunCounter <= 1); } + +bool Task::isLastIteration() { return (iIterations == 0); } + +#ifdef _TASK_TIMECRITICAL + +long Task::getOverrun() { return iOverrun; } +long Task::getStartDelay() { return iStartDelay; } + +#endif // _TASK_TIMECRITICAL + + +#ifdef _TASK_WDT_IDS + +void Task::setId(unsigned int aID) { iTaskID = aID; } +unsigned int Task::getId() { return iTaskID; } +void Task::setControlPoint(unsigned int aPoint) { iControlPoint = aPoint; } +unsigned int Task::getControlPoint() { return iControlPoint; } + +#endif // _TASK_WDT_IDS + +#ifdef _TASK_LTS_POINTER + +void Task::setLtsPointer(void *aPtr) { iLTS = aPtr; } +void* Task::getLtsPointer() { return iLTS; } + +#endif // _TASK_LTS_POINTER + +// ------------------ Scheduler implementation -------------------- + +/** Default constructor. + * Creates a scheduler with an empty execution chain. + */ +Scheduler::Scheduler() { + init(); +} + +/* +Scheduler::~Scheduler() { +#ifdef _TASK_SLEEP_ON_IDLE_RUN +#endif // _TASK_SLEEP_ON_IDLE_RUN +} +*/ + +/** Initializes all internal varaibles + */ +void Scheduler::init() { + iFirst = NULL; + iLast = NULL; + iCurrent = NULL; + +#ifdef _TASK_PRIORITY + iHighPriority = NULL; +#endif // _TASK_PRIORITY + +#ifdef _TASK_SLEEP_ON_IDLE_RUN + allowSleep(true); +#endif // _TASK_SLEEP_ON_IDLE_RUN +} + +/** Appends task aTask to the tail of the execution chain. + * @param &aTask - reference to the Task to be appended. + * @note Task can only be part of the chain once. + */ + void Scheduler::addTask(Task& aTask) { + +// Avoid adding task twice to the same scheduler + if (aTask.iScheduler == this) + return; + + aTask.iScheduler = this; +// First task situation: + if (iFirst == NULL) { + iFirst = &aTask; + aTask.iPrev = NULL; + } + else { +// This task gets linked back to the previous last one + aTask.iPrev = iLast; + iLast->iNext = &aTask; + } +// "Previous" last task gets linked to this one - as this one becomes the last one + aTask.iNext = NULL; + iLast = &aTask; +} + +/** Deletes specific Task from the execution chain + * @param &aTask - reference to the task to be deleted from the chain + */ +void Scheduler::deleteTask(Task& aTask) { + if (aTask.iPrev == NULL) { + if (aTask.iNext == NULL) { + iFirst = NULL; + iLast = NULL; + return; + } + else { + aTask.iNext->iPrev = NULL; + iFirst = aTask.iNext; + aTask.iNext = NULL; + return; + } + } + + if (aTask.iNext == NULL) { + aTask.iPrev->iNext = NULL; + iLast = aTask.iPrev; + aTask.iPrev = NULL; + return; + } + + aTask.iPrev->iNext = aTask.iNext; + aTask.iNext->iPrev = aTask.iPrev; + aTask.iPrev = NULL; + aTask.iNext = NULL; +} + +/** Disables all tasks in the execution chain + * Convenient for error situations, when the only + * task remaining active is an error processing task + * @param aRecursive - if true, tasks of the higher priority chains are disabled as well recursively + */ +void Scheduler::disableAll(bool aRecursive) { + Task *current = iFirst; + while (current) { + current->disable(); + current = current->iNext; + } + +#ifdef _TASK_PRIORITY + if (aRecursive && iHighPriority) iHighPriority->disableAll(true); +#endif // _TASK_PRIORITY +} + + +/** Enables all the tasks in the execution chain + * @param aRecursive - if true, tasks of the higher priority chains are enabled as well recursively + */ + void Scheduler::enableAll(bool aRecursive) { + Task *current = iFirst; + while (current) { + current->enable(); + current = current->iNext; + } + +#ifdef _TASK_PRIORITY + if (aRecursive && iHighPriority) iHighPriority->enableAll(true); +#endif // _TASK_PRIORITY + +} + +/** Sets scheduler for the higher priority tasks (support for layered task priority) + * @param aScheduler - pointer to a scheduler for the higher priority tasks + */ +#ifdef _TASK_PRIORITY +void Scheduler::setHighPriorityScheduler(Scheduler* aScheduler) { + if (aScheduler != this) iHighPriority = aScheduler; // Setting yourself as a higher priority one will create infinite recursive call + +#ifdef _TASK_SLEEP_ON_IDLE_RUN + if (iHighPriority) { + iHighPriority->allowSleep(false); // Higher priority schedulers should not do power management + } +#endif // _TASK_SLEEP_ON_IDLE_RUN + +}; +#endif // _TASK_PRIORITY + + +#ifdef _TASK_SLEEP_ON_IDLE_RUN +void Scheduler::allowSleep(bool aState) { + iAllowSleep = aState; + +#ifdef ARDUINO_ARCH_ESP8266 + wifi_set_sleep_type( iAllowSleep ? LIGHT_SLEEP_T : NONE_SLEEP_T ); +#endif // ARDUINO_ARCH_ESP8266 + +#ifdef ARDUINO_ARCH_ESP32 + // TO-DO; find a suitable replacement for ESP32 if possible. +#endif // ARDUINO_ARCH_ESP32 +} +#endif // _TASK_SLEEP_ON_IDLE_RUN + + +void Scheduler::startNow( bool aRecursive ) { + unsigned long t = _TASK_TIME_FUNCTION(); + + iCurrent = iFirst; + while (iCurrent) { + if ( iCurrent->iStatus.enabled ) iCurrent->iPreviousMillis = t - iCurrent->iDelay; + iCurrent = iCurrent->iNext; + } + +#ifdef _TASK_PRIORITY + if (aRecursive && iHighPriority) iHighPriority->startNow( true ); +#endif // _TASK_PRIORITY +} + +/** Returns number millis or micros until next scheduled iteration of a given task + * + * @param aTask - reference to task which next iteration is in question + */ +long Scheduler::timeUntilNextIteration(Task& aTask) { + +#ifdef _TASK_STATUS_REQUEST + + StatusRequest *s = aTask.getStatusRequest(); + if ( s != NULL && s->pending() ) + return (-1); // cannot be determined +#endif + if ( !aTask.isEnabled() ) + return (-1); // cannot be determined + + long d = (long) aTask.iDelay - ( (long) ((_TASK_TIME_FUNCTION() - aTask.iPreviousMillis)) ); + + if ( d < 0 ) + return (0); // Task will run as soon as possible + return ( d ); +} + +Task& Scheduler::currentTask() { return *iCurrent; } + +#ifdef _TASK_LTS_POINTER +void* Scheduler::currentLts() { return iCurrent->iLTS; } +#endif // _TASK_LTS_POINTER +#ifdef _TASK_TIMECRITICAL +bool Scheduler::isOverrun() { return (iCurrent->iOverrun < 0); } +#endif // _TASK_TIMECRITICAL + + + +/** Makes one pass through the execution chain. + * Tasks are executed in the order they were added to the chain + * There is no concept of priority + * Different pseudo "priority" could be achieved + * by running task more frequently + */ +bool Scheduler::execute() { + bool idleRun = true; + register unsigned long m, i; // millis, interval; + +#ifdef _TASK_SLEEP_ON_IDLE_RUN +#if defined (ARDUINO_ARCH_ESP8266) || defined (ARDUINO_ARCH_ESP32) + unsigned long t1 = micros(); + unsigned long t2 = 0; +#endif // ARDUINO_ARCH_ESP8266 +#endif // _TASK_SLEEP_ON_IDLE_RUN + + Task *nextTask; // support for deleting the task in the onDisable method + iCurrent = iFirst; + +#ifdef _TASK_PRIORITY + // If lower priority scheduler does not have a single task in the chain + // the higher priority scheduler still has to have a chance to run + if (!iCurrent && iHighPriority) iHighPriority->execute(); + iCurrentScheduler = this; +#endif // _TASK_PRIORITY + + + while (iCurrent) { + +#ifdef _TASK_PRIORITY + // If scheduler for higher priority tasks is set, it's entire chain is executed on every pass of the base scheduler + if (iHighPriority) idleRun = iHighPriority->execute() && idleRun; + iCurrentScheduler = this; +#endif // _TASK_PRIORITY + nextTask = iCurrent->iNext; + do { + if ( iCurrent->iStatus.enabled ) { + +#ifdef _TASK_WDT_IDS + // For each task the control points are initialized to avoid confusion because of carry-over: + iCurrent->iControlPoint = 0; +#endif // _TASK_WDT_IDS + + // Disable task on last iteration: + if (iCurrent->iIterations == 0) { + iCurrent->disable(); + break; + } + m = _TASK_TIME_FUNCTION(); + i = iCurrent->iInterval; + +#ifdef _TASK_TIMEOUT + // Disable task on a timeout + if ( iCurrent->iTimeout && (m - iCurrent->iStarttime > iCurrent->iTimeout) ) { + iCurrent->iStatus.timeout = true; + iCurrent->disable(); + break; + } +#endif // _TASK_TIMEOUT + +#ifdef _TASK_STATUS_REQUEST + // If StatusRequest object was provided, and still pending, and task is waiting, this task should not run + // Otherwise, continue with execution as usual. Tasks waiting to StatusRequest need to be rescheduled according to + // how they were placed into waiting state (waitFor or waitForDelayed) + if ( iCurrent->iStatus.waiting ) { + if ( (iCurrent->iStatusRequest)->pending() ) break; + if (iCurrent->iStatus.waiting == _TASK_SR_NODELAY) { + iCurrent->iPreviousMillis = m - (iCurrent->iDelay = i); + } + else { + iCurrent->iPreviousMillis = m; + } + iCurrent->iStatus.waiting = 0; + } +#endif // _TASK_STATUS_REQUEST + + if ( m - iCurrent->iPreviousMillis < iCurrent->iDelay ) break; + + if ( iCurrent->iIterations > 0 ) iCurrent->iIterations--; // do not decrement (-1) being a signal of never-ending task + iCurrent->iRunCounter++; + iCurrent->iPreviousMillis += iCurrent->iDelay; + +#ifdef _TASK_TIMECRITICAL + // Updated_previous+current interval should put us into the future, so iOverrun should be positive or zero. + // If negative - the task is behind (next execution time is already in the past) + unsigned long p = iCurrent->iPreviousMillis; + iCurrent->iOverrun = (long) ( p + i - m ); + iCurrent->iStartDelay = (long) ( m - p ); +#endif // _TASK_TIMECRITICAL + + iCurrent->iDelay = i; + if ( iCurrent->iCallback ) { + iCurrent->iCallback(); + idleRun = false; + } + } + } while (0); //guaranteed single run - allows use of "break" to exit + iCurrent = nextTask; +#if defined (ARDUINO_ARCH_ESP8266) || defined (ARDUINO_ARCH_ESP32) + yield(); +#endif // ARDUINO_ARCH_ESP8266 + } + +#ifdef _TASK_SLEEP_ON_IDLE_RUN + if (idleRun && iAllowSleep) { + +#ifdef ARDUINO_ARCH_AVR // Could be used only for AVR-based boards. + set_sleep_mode(SLEEP_MODE_IDLE); + sleep_enable(); + /* Now enter sleep mode. */ + sleep_mode(); + + /* The program will continue from here after the timer timeout ~1 ms */ + sleep_disable(); /* First thing to do is disable sleep. */ +#endif // ARDUINO_ARCH_AVR + +#ifdef CORE_TEENSY + asm("wfi"); +#endif //CORE_TEENSY + +#ifdef ARDUINO_ARCH_ESP8266 +// to do: find suitable sleep function for esp8266 + t2 = micros() - t1; + if (t2 < _TASK_ESP8266_DLY_THRESHOLD) delay(1); // ESP8266 implementation of delay() uses timers and yield +#endif // ARDUINO_ARCH_ESP8266 + +#ifdef ARDUINO_ARCH_ESP32 +//TODO: find a correct light sleep implementation for ESP32 + // esp_sleep_enable_timer_wakeup(1000); //1 ms + // int ret= esp_light_sleep_start(); + t2 = micros() - t1; + if (t2 < _TASK_ESP8266_DLY_THRESHOLD) delay(1); +#endif // ARDUINO_ARCH_ESP32 + + } +#endif // _TASK_SLEEP_ON_IDLE_RUN + + return (idleRun); +} + + + +#endif /* _TASKSCHEDULER_H_ */ diff --git a/.piolibdeps/TaskScheduler_ID721/src/TaskSchedulerDeclarations.h b/.piolibdeps/TaskScheduler_ID721/src/TaskSchedulerDeclarations.h new file mode 100644 index 0000000..4a3b69c --- /dev/null +++ b/.piolibdeps/TaskScheduler_ID721/src/TaskSchedulerDeclarations.h @@ -0,0 +1,279 @@ +// Cooperative multitasking library for Arduino +// Copyright (c) 2015-2017 Anatoli Arkhipenko + +#include +#include + +#ifndef _TASKSCHEDULERDECLARATIONS_H_ +#define _TASKSCHEDULERDECLARATIONS_H_ + +// ---------------------------------------- +// The following "defines" control library functionality at compile time, +// and should be used in the main sketch depending on the functionality required +// +// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns +// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass +// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only +// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids +// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer +// #define _TASK_PRIORITY // Support for layered scheduling priority +// #define _TASK_MICRO_RES // Support for microsecond resolution +// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY) +// #define _TASK_DEBUG // Make all methods and variables public for debug purposes +// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations +// #define _TASK_TIMEOUT // Support for overall task timeout + +#ifdef _TASK_DEBUG + #define _TASK_SCOPE public +#else + #define _TASK_SCOPE private +#endif + +#define TASK_IMMEDIATE 0 +#define TASK_FOREVER (-1) +#define TASK_ONCE 1 + +#ifdef _TASK_TIMEOUT +#define TASK_NOTIMEOUT 0 +#endif + +#ifdef _TASK_PRIORITY + class Scheduler; + extern Scheduler* iCurrentScheduler; +#endif // _TASK_PRIORITY + +#if !defined(ARDUINO) + extern unsigned long micros(void); + extern unsigned long millis(void); +#endif + + +#ifdef _TASK_INLINE +#define INLINE inline +#else +#define INLINE +#endif + + +#ifndef _TASK_MICRO_RES + +#define TASK_MILLISECOND 1UL +#define TASK_SECOND 1000UL +#define TASK_MINUTE 60000UL +#define TASK_HOUR 3600000UL + +#else + +#define TASK_MILLISECOND 1000UL +#define TASK_SECOND 1000000UL +#define TASK_MINUTE 60000000UL +#define TASK_HOUR 3600000000UL + +#endif // _TASK_MICRO_RES + + +#ifdef _TASK_STATUS_REQUEST + +#define _TASK_SR_NODELAY 1 +#define _TASK_SR_DELAY 2 + +class StatusRequest { + public: + INLINE StatusRequest(); + INLINE void setWaiting(unsigned int aCount = 1); + INLINE bool signal(int aStatus = 0); + INLINE void signalComplete(int aStatus = 0); + INLINE bool pending(); + INLINE bool completed(); + INLINE int getStatus(); + INLINE int getCount(); + + _TASK_SCOPE: + unsigned int iCount; // number of statuses to wait for. waiting for more that 65000 events seems unreasonable: unsigned int should be sufficient + int iStatus; // status of the last completed request. negative = error; zero = OK; positive = OK with a specific status +}; +#endif // _TASK_STATUS_REQUEST + +#ifdef _TASK_STD_FUNCTION +#include +typedef std::function TaskCallback; +typedef std::function TaskOnDisable; +typedef std::function TaskOnEnable; +#else +typedef void (*TaskCallback)(); +typedef void (*TaskOnDisable)(); +typedef bool (*TaskOnEnable)(); +#endif + +typedef struct { + bool enabled : 1; // indicates that task is enabled or not. + bool inonenable : 1; // indicates that task execution is inside OnEnable method (preventing infinite loops) + +#ifdef _TASK_STATUS_REQUEST + uint8_t waiting : 2; // indication if task is waiting on the status request +#endif + +#ifdef _TASK_TIMEOUT + bool timeout : 1; // indication if task is waiting on the status request +#endif + +} __task_status; + +class Scheduler; + + +class Task { + friend class Scheduler; + public: + INLINE Task(unsigned long aInterval=0, long aIterations=0, TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, bool aEnable=false, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL); + +#ifdef _TASK_STATUS_REQUEST + INLINE Task(TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL); +#endif // _TASK_STATUS_REQUEST + + INLINE ~Task(); + +#ifdef _TASK_TIMEOUT + INLINE void setTimeout(unsigned long aTimeout, bool aReset=false); + INLINE void resetTimeout(); + INLINE unsigned long getTimeout(); + INLINE long untilTimeout(); + INLINE bool timedOut(); +#endif + + INLINE void enable(); + INLINE bool enableIfNot(); + INLINE void enableDelayed(unsigned long aDelay=0); + INLINE void restart(); + INLINE void restartDelayed(unsigned long aDelay=0); + + INLINE void delay(unsigned long aDelay=0); + INLINE void forceNextIteration(); + INLINE bool disable(); + INLINE bool isEnabled(); + INLINE void set(unsigned long aInterval, long aIterations, TaskCallback aCallback,TaskOnEnable aOnEnable=NULL, TaskOnDisable aOnDisable=NULL); + INLINE void setInterval(unsigned long aInterval); + INLINE unsigned long getInterval(); + INLINE void setIterations(long aIterations); + INLINE long getIterations(); + INLINE unsigned long getRunCounter() ; + INLINE void setCallback(TaskCallback aCallback) ; + INLINE void setOnEnable(TaskOnEnable aCallback) ; + INLINE void setOnDisable(TaskOnDisable aCallback) ; + INLINE void yield(TaskCallback aCallback); + INLINE void yieldOnce(TaskCallback aCallback); + INLINE bool isFirstIteration() ; + INLINE bool isLastIteration() ; + +#ifdef _TASK_TIMECRITICAL + INLINE long getOverrun() ; + INLINE long getStartDelay() ; +#endif // _TASK_TIMECRITICAL + +#ifdef _TASK_STATUS_REQUEST + INLINE void waitFor(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1); + INLINE void waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval = 0, long aIterations = 1); + INLINE StatusRequest* getStatusRequest() ; + INLINE StatusRequest* getInternalStatusRequest() ; +#endif // _TASK_STATUS_REQUEST + +#ifdef _TASK_WDT_IDS + INLINE void setId(unsigned int aID) ; + INLINE unsigned int getId() ; + INLINE void setControlPoint(unsigned int aPoint) ; + INLINE unsigned int getControlPoint() ; +#endif // _TASK_WDT_IDS + +#ifdef _TASK_LTS_POINTER + INLINE void setLtsPointer(void *aPtr) ; + void* getLtsPointer() ; +#endif // _TASK_LTS_POINTER + + _TASK_SCOPE: + INLINE void reset(); + + volatile __task_status iStatus; + volatile unsigned long iInterval; // execution interval in milliseconds (or microseconds). 0 - immediate + volatile unsigned long iDelay; // actual delay until next execution (usually equal iInterval) + volatile unsigned long iPreviousMillis; // previous invocation time (millis). Next invocation = iPreviousMillis + iInterval. Delayed tasks will "catch up" + +#ifdef _TASK_TIMECRITICAL + volatile long iOverrun; // negative if task is "catching up" to it's schedule (next invocation time is already in the past) + volatile long iStartDelay; // actual execution of the task's callback method was delayed by this number of millis +#endif // _TASK_TIMECRITICAL + + volatile long iIterations; // number of iterations left. 0 - last iteration. -1 - infinite iterations + long iSetIterations; // number of iterations originally requested (for restarts) + unsigned long iRunCounter; // current number of iteration (starting with 1). Resets on enable. + TaskCallback iCallback; // pointer to the void callback method + TaskOnEnable iOnEnable; // pointer to the bolol OnEnable callback method + TaskOnDisable iOnDisable; // pointer to the void OnDisable method + Task *iPrev, *iNext; // pointers to the previous and next tasks in the chain + Scheduler *iScheduler; // pointer to the current scheduler + +#ifdef _TASK_STATUS_REQUEST + StatusRequest *iStatusRequest; // pointer to the status request task is or was waiting on + StatusRequest iMyStatusRequest; // internal Status request to let other tasks know of completion +#endif // _TASK_STATUS_REQUEST + +#ifdef _TASK_WDT_IDS + unsigned int iTaskID; // task ID (for debugging and watchdog identification) + unsigned int iControlPoint; // current control point within the callback method. Reset to 0 by scheduler at the beginning of each pass +#endif // _TASK_WDT_IDS + +#ifdef _TASK_LTS_POINTER + void *iLTS; // pointer to task's local storage. Needs to be recast to appropriate type (usually a struct). +#endif // _TASK_LTS_POINTER + +#ifdef _TASK_TIMEOUT + unsigned long iTimeout; // Task overall timeout + unsigned long iStarttime; // millis at task start time +#endif // _TASK_TIMEOUT +}; + +class Scheduler { + friend class Task; + public: + INLINE Scheduler(); +// ~Scheduler(); + INLINE void init(); + INLINE void addTask(Task& aTask); + INLINE void deleteTask(Task& aTask); + INLINE void disableAll(bool aRecursive = true); + INLINE void enableAll(bool aRecursive = true); + INLINE bool execute(); // Returns true if none of the tasks' callback methods was invoked (true = idle run) + INLINE void startNow(bool aRecursive = true); // reset ALL active tasks to immediate execution NOW. + INLINE Task& currentTask() ; + INLINE long timeUntilNextIteration(Task& aTask); // return number of ms until next iteration of a given Task + +#ifdef _TASK_SLEEP_ON_IDLE_RUN + INLINE void allowSleep(bool aState = true); +#endif // _TASK_SLEEP_ON_IDLE_RUN + +#ifdef _TASK_LTS_POINTER + INLINE void* currentLts(); +#endif // _TASK_LTS_POINTER + +#ifdef _TASK_TIMECRITICAL + INLINE bool isOverrun(); +#endif // _TASK_TIMECRITICAL + +#ifdef _TASK_PRIORITY + INLINE void setHighPriorityScheduler(Scheduler* aScheduler); + INLINE static Scheduler& currentScheduler() { return *(iCurrentScheduler); }; +#endif // _TASK_PRIORITY + + _TASK_SCOPE: + Task *iFirst, *iLast, *iCurrent; // pointers to first, last and current tasks in the chain + +#ifdef _TASK_SLEEP_ON_IDLE_RUN + bool iAllowSleep; // indication if putting avr to IDLE_SLEEP mode is allowed by the program at this time. +#endif // _TASK_SLEEP_ON_IDLE_RUN + +#ifdef _TASK_PRIORITY + Scheduler *iHighPriority; // Pointer to a higher priority scheduler +#endif // _TASK_PRIORITY +}; + + +#endif /* _TASKSCHEDULERDECLARATIONS_H_ */ diff --git a/.piolibdeps/eeprom_ID2284/.hg_archival.txt b/.piolibdeps/eeprom_ID2284/.hg_archival.txt new file mode 100644 index 0000000..a9f0dfb --- /dev/null +++ b/.piolibdeps/eeprom_ID2284/.hg_archival.txt @@ -0,0 +1,5 @@ +repo: 80245aff63ced45acef2e06f847e6db76144e912 +node: 925096a4c7f03ca671d84f2ac1c0e82a69fee5d3 +branch: default +latesttag: null +latesttagdistance: 4 diff --git a/.piolibdeps/eeprom_ID2284/.library.json b/.piolibdeps/eeprom_ID2284/.library.json new file mode 100644 index 0000000..a1de9bc --- /dev/null +++ b/.piolibdeps/eeprom_ID2284/.library.json @@ -0,0 +1,43 @@ +{ + "name": "eeprom", + "repository": { + "url": "https://os.mbed.com/users/bborredon/code/eeprom/", + "type": "hg" + }, + "platforms": [ + "atmelsam", + "freescalekinetis", + "maxim32", + "nordicnrf51", + "nordicnrf52", + "nxplpc", + "siliconlabsefm32", + "ststm32", + "teensy", + "wiznet7500" + ], + "frameworks": [ + "mbed" + ], + "version": "925096a4c7", + "examples": [ + "https://os.mbed.com/users/pai2015arminnogo/code/Digitalni_Sat_MG_AN/", + "https://os.mbed.com/users/sameera0824/code/EEROM_1768/", + "https://os.mbed.com/users/sameera0824/code/EEPROM_kamal/", + "https://os.mbed.com/users/sameera0824/code/EEPROM_MY/", + "https://os.mbed.com/users/jolyon/code/ColourSensor/" + ], + "authors": [ + { + "url": "https://os.mbed.com/users/bborredon/", + "maintainer": false, + "email": null, + "name": "bernard borredon" + } + ], + "keywords": [ + "eeprom" + ], + "id": 2284, + "description": "I2C EEPROM library from 24C01 to 24C1025" +} \ No newline at end of file diff --git a/.piolibdeps/eeprom_ID2284/eeprom.cpp b/.piolibdeps/eeprom_ID2284/eeprom.cpp new file mode 100644 index 0000000..118619e --- /dev/null +++ b/.piolibdeps/eeprom_ID2284/eeprom.cpp @@ -0,0 +1,1064 @@ +/*********************************************************** +Author: Bernard Borredon +Version: 1.3 + - Correct write(uint32_t address, int8_t data[], uint32_t length) for eeprom >= T24C32. + Tested with 24C02, 24C08, 24C16, 24C64, 24C256, 24C512, 24C1025 on LPC1768 (mbed online and µVision V5.16a). + - Correct main test. + +Date : 12 decembre 2013 +Version: 1.2 + - Update api documentation + +Date: 11 december 2013 +Version: 1.1 + - Change address parameter size form uint16_t to uint32_t (error for eeprom > 24C256). + - Change size parameter size from uint16_t to uint32_t (error for eeprom > 24C256). + - Correct a bug in function write(uint32_t address, int8_t data[], uint32_t length) : + last step must be done only if it remain datas to send. + - Add function getName. + - Add function clear. + - Initialize _name array. + +Date: 27 december 2011 +Version: 1.0 + +************************************************************/ +#include "eeprom.h" + +#define BIT_SET(x,n) (x=x | (0x01< 7) { + _errnum = EEPROM_BadAddress; + } + _address = _address << 1; + _page_write = 8; + _page_number = 1; + break; + case T24C04 : + if(address > 7) { + _errnum = EEPROM_BadAddress; + } + _address = (_address & 0xFE) << 1; + _page_write = 16; + _page_number = 2; + break; + case T24C08 : + if(address > 7) { + _errnum = EEPROM_BadAddress; + } + _address = (_address & 0xFC) << 1; + _page_write = 16; + _page_number = 4; + break; + case T24C16 : + _address = 0; + _page_write = 16; + _page_number = 8; + break; + case T24C32 : + case T24C64 : + if(address > 7) { + _errnum = EEPROM_BadAddress; + } + _address = _address << 1; + _page_write = 32; + _page_number = 1; + break; + case T24C128 : + case T24C256 : + if(address > 3) { + _errnum = EEPROM_BadAddress; + } + _address = _address << 1; + _page_write = 64; + _page_number = 1; + break; + case T24C512 : + if(address > 3) { + _errnum = EEPROM_BadAddress; + } + _address = _address << 1; + _page_write = 128; + _page_number = 1; + break; + case T24C1024 : + if(address > 7) { + _errnum = EEPROM_BadAddress; + } + _address = (_address & 0xFE) << 1; + _page_write = 128; + _page_number = 2; + break; + case T24C1025 : + if(address > 3) { + _errnum = EEPROM_BadAddress; + } + _address = _address << 1; + _page_write = 128; + _page_number = 2; + break; + } + + // Size in bytes + _size = _type; + if(_type == T24C1025) + _size = T24C1024; + + // Set I2C frequency + _i2c.frequency(400000); + +} + +/** + * void write(uint32_t address, int8_t data) + * + * Write byte + * @param address start address (uint32_t) + * @param data byte to write (int8_t) + * @return none +*/ +void EEPROM::write(uint32_t address, int8_t data) +{ + uint8_t page; + uint8_t addr; + uint8_t cmd[3]; + int len; + int ack; + + // Check error + if(_errnum) + return; + + // Check address + if(!checkAddress(address)) { + _errnum = EEPROM_OutOfRange; + return; + } + + // Compute page number + page = 0; + if(_type < T24C32) + page = (uint8_t) (address / 256); + + // Device address + addr = EEPROM_Address | _address | (page << 1); + + if(_type < T24C32) { + len = 2; + + // Word address + cmd[0] = (uint8_t) (address - page * 256); + + // Data + cmd[1] = (uint8_t) data; + } + else { + len = 3; + + // First word address (MSB) + cmd[0] = (uint8_t) (address >> 8); + + // Second word address (LSB) + cmd[1] = (uint8_t) address; + + // Data + cmd[2] = (uint8_t) data; + } + + ack = _i2c.write((int)addr,(char *)cmd,len); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + + // Wait end of write + ready(); + +} + +/** + * void write(uint32_t address, int8_t data[], uint32_t length) + * + * Write array of bytes (use the page mode) + * @param address start address (uint32_t) + * @param data bytes array to write (int8_t[]) + * @param size number of bytes to write (uint32_t) + * @return none +*/ +void EEPROM::write(uint32_t address, int8_t data[], uint32_t length) +{ + uint8_t page; + uint8_t addr = 0; + uint8_t blocs,remain; + uint8_t fpart,lpart; + uint8_t i,j,ind; + uint8_t cmd[129]; + int ack; + + // Check error + if(_errnum) + return; + + // Check address + if(!checkAddress(address)) { + _errnum = EEPROM_OutOfRange; + return; + } + + // Check length + if(!checkAddress(address + length - 1)) { + _errnum = EEPROM_OutOfRange; + return; + } + + // Compute blocs numbers + blocs = length / _page_write; + + // Compute remaining bytes + remain = length - blocs * _page_write; + + for(i = 0;i < blocs;i++) { + // Compute page number + page = 0; + if(_type < T24C32) + page = (uint8_t) (address / 256); + + // Device address + addr = EEPROM_Address | _address | (page << 1); + + if(_type < T24C32) { + // Word address + cmd[0] = (uint8_t) (address - page * 256); + + if((uint8_t) ((address + _page_write) / 256) == page) { // Data fit in the same page + // Add data + for(j = 0;j < _page_write;j++) + cmd[j + 1] = (uint8_t) data[i * _page_write + j]; + + // Write data + ack = _i2c.write((int)addr,(char *)cmd,_page_write + 1); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + + // Wait end of write + ready(); + + // Increment address + address += _page_write; + } + else { // Data on 2 pages. We must split the write + // Number of bytes in current page + fpart = (page + 1) * 256 - address; + + // Add data for current page + for(j = 0;j < fpart;j++) + cmd[j + 1] = (uint8_t) data[i * _page_write + j]; + + // Write data for current page + ack = _i2c.write((int)addr,(char *)cmd,fpart + 1); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + + // Wait end of write + ready(); + + // Increment address + address += fpart; + + if(page < _page_number - 1) { + // Increment page + page++; + + // Device address + addr = EEPROM_Address | _address | (page << 1); + + // Word address + cmd[0] = (uint8_t) (address - page * 256); + + // Data index + ind = i * _page_write + fpart; + + // Number of bytes in next page + lpart = _page_write - fpart; + + // Add data for next page + for(j = 0;j < lpart;j++) + cmd[j + 1] = (uint8_t) data[ind + j]; + + // Write data for next page + ack = _i2c.write((int)addr,(char *)cmd,lpart + 1); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + + // Wait end of write + ready(); + + // Increment address + address += lpart; + } + } + } + else { + // First word address (MSB) + cmd[0] = (uint8_t) (address >> 8); + + // Second word address (LSB) + cmd[1] = (uint8_t) address; + + // Add data + for(j = 0;j < _page_write;j++) + cmd[j + 2] = (uint8_t) data[i * _page_write + j]; + + // Write data + ack = _i2c.write((int)addr,(char *)cmd,_page_write + 2); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + + // Wait end of write + ready(); + + // Increment address + address += _page_write; + } + } + + if(remain) { + // Compute page number + page = 0; + if(_type < T24C32) + page = (uint8_t) (address / 256); + + // Device address + addr = EEPROM_Address | _address | (page << 1); + + if(_type < T24C32) { + // Word address + cmd[0] = (uint8_t) (address - page * 256); + + if((uint8_t) ((address + remain) / 256) == page) { // Data fit in the same page + // Add data for the current page + for(j = 0;j < remain;j++) + cmd[j + 1] = (uint8_t) data[blocs * _page_write + j]; + + // Write data for the current page + ack = _i2c.write((int)addr,(char *)cmd,remain + 1); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + + // Wait end of write + ready(); + } + else { // Data on 2 pages. We must split the write + // Number of bytes in current page + fpart = (page + 1) * 256 - address; + + // Add data for current page + for(j = 0;j < fpart;j++) + cmd[j + 1] = (uint8_t) data[blocs * _page_write + j]; + + // Write data for current page + ack = _i2c.write((int)addr,(char *)cmd,fpart + 1); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + + // Wait end of write + ready(); + + // Increment address + address += fpart; + + if(page < _page_number - 1) { + // Increment page + page++; + + // Device address + addr = EEPROM_Address | _address | (page << 1); + + // Word address + cmd[0] = (uint8_t) (address - page * 256); + + // Data index + ind = blocs * _page_write + fpart; + + // Number of bytes in next page + lpart = remain - fpart; + + // Add data for next page + for(j = 0;j < lpart;j++) + cmd[j + 1] = (uint8_t) data[ind + j]; + + // Write data for next page + ack = _i2c.write((int)addr,(char *)cmd,lpart + 1); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + + // Wait end of write + ready(); + } + } + } + else { + // Fist word address (MSB) + cmd[0] = (uint8_t) (address >> 8); + + // Second word address (LSB) + cmd[1] = (uint8_t) address; + + // Add data for the current page + for(j = 0;j < remain;j++) + cmd[j + 2] = (uint8_t) data[blocs * _page_write + j]; + + // Write data for the current page + ack = _i2c.write((int)addr,(char *)cmd,remain + 2); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + + // Wait end of write + ready(); + } + } + +} + +/** + * void write(uint32_t address, int16_t data) + * + * Write short + * @param address start address (uint32_t) + * @param data short to write (int16_t) + * @return none +*/ +void EEPROM::write(uint32_t address, int16_t data) +{ + int8_t cmd[2]; + + // Check error + if(_errnum) + return; + + // Check address + if(!checkAddress(address + 1)) { + _errnum = EEPROM_OutOfRange; + return; + } + + memcpy(cmd,&data,2); + + write(address,cmd,2); + +} + +/** + * void write(uint32_t address, int32_t data) + * + * Write long + * @param address start address (uint32_t) + * @param data long to write (int32_t) + * @return none +*/ +void EEPROM::write(uint32_t address, int32_t data) +{ + int8_t cmd[4]; + + // Check error + if(_errnum) + return; + + // Check address + if(!checkAddress(address + 3)) { + _errnum = EEPROM_OutOfRange; + return; + } + + memcpy(cmd,&data,4); + + write(address,cmd,4); + +} + +/** + * void write(uint32_t address, float data) + * + * Write float + * @param address start address (uint32_t) + * @param data float to write (float) + * @return none +*/ +void EEPROM::write(uint32_t address, float data) +{ + int8_t cmd[4]; + + // Check error + if(_errnum) + return; + + // Check address + if(!checkAddress(address + 3)) { + _errnum = EEPROM_OutOfRange; + return; + } + + memcpy(cmd,&data,4); + + write(address,cmd,4); + +} + +/** + * void write(uint32_t address, void *data, uint32_t size) + * + * Write anything (use the page write mode) + * @param address start address (uint32_t) + * @param data data to write (void *) + * @param size number of bytes to write (uint32_t) + * @return none +*/ +void EEPROM::write(uint32_t address, void *data, uint32_t size) +{ + int8_t *cmd = NULL; + + // Check error + if(_errnum) + return; + + // Check address + if(!checkAddress(address + size - 1)) { + _errnum = EEPROM_OutOfRange; + return; + } + + cmd = (int8_t *)malloc(size); + if(cmd == NULL) { + _errnum = EEPROM_MallocError; + return; + } + + memcpy(cmd,(uint8_t *)data,size); + + write(address,cmd,size); + + free(cmd); + +} + +/** + * void read(uint32_t address, int8_t& data) + * + * Random read byte + * @param address start address (uint32_t) + * @param data byte to read (int8_t&) + * @return none +*/ +void EEPROM::read(uint32_t address, int8_t& data) +{ + uint8_t page; + uint8_t addr; + uint8_t cmd[2]; + uint8_t len; + int ack; + + // Check error + if(_errnum) + return; + + // Check address + if(!checkAddress(address)) { + _errnum = EEPROM_OutOfRange; + return; + } + + // Compute page number + page = 0; + if(_type < T24C32) + page = (uint8_t) (address / 256); + + // Device address + addr = EEPROM_Address | _address | (page << 1); + + if(_type < T24C32) { + len = 1; + + // Word address + cmd[0] = (uint8_t) (address - page * 256); + } + else { + len = 2; + + // First word address (MSB) + cmd[0] = (uint8_t) (address >> 8); + + // Second word address (LSB) + cmd[1] = (uint8_t)address; + } + + // Write command + ack = _i2c.write((int)addr,(char *)cmd,len,true); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + + // Read data + ack = _i2c.read((int)addr,(char *)&data,sizeof(data)); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + +} + +/** + * void read(uint32_t address, int8_t *data, uint32_t size) + * + * Sequential read byte + * @param address start address (uint32_t) + * @param data bytes array to read (int8_t[]&) + * @param size number of bytes to read (uint32_t) + * @return none +*/ +void EEPROM::read(uint32_t address, int8_t *data, uint32_t size) +{ + uint8_t page; + uint8_t addr; + uint8_t cmd[2]; + uint8_t len; + int ack; + + // Check error + if(_errnum) + return; + + // Check address + if(!checkAddress(address)) { + _errnum = EEPROM_OutOfRange; + return; + } + + // Check size + if(!checkAddress(address + size - 1)) { + _errnum = EEPROM_OutOfRange; + return; + } + + // Compute page number + page = 0; + if(_type < T24C32) + page = (uint8_t) (address / 256); + + // Device address + addr = EEPROM_Address | _address | (page << 1); + + if(_type < T24C32) { + len = 1; + + // Word address + cmd[0] = (uint8_t) (address - page * 256); + } + else { + len = 2; + + // First word address (MSB) + cmd[0] = (uint8_t) (address >> 8); + + // Second word address (LSB) + cmd[1] = (uint8_t) address; + } + + // Write command + ack = _i2c.write((int)addr,(char *)cmd,len,true); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + + // Sequential read + ack = _i2c.read((int)addr,(char *)data,size); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + +} + +/** + * void read(int8_t& data) + * + * Current address read byte + * @param data byte to read (int8_t&) + * @return none + */ +void EEPROM::read(int8_t& data) +{ + uint8_t addr; + int ack; + + // Check error + if(_errnum) + return; + + // Device address + addr = EEPROM_Address | _address; + + // Read data + ack = _i2c.read((int)addr,(char *)&data,sizeof(data)); + if(ack != 0) { + _errnum = EEPROM_I2cError; + return; + } + +} + +/** + * void read(uint32_t address, int16_t& data) + * + * Random read short + * @param address start address (uint32_t) + * @param data short to read (int16_t&) + * @return none +*/ +void EEPROM::read(uint32_t address, int16_t& data) +{ + int8_t cmd[2]; + + // Check error + if(_errnum) + return; + + // Check address + if(!checkAddress(address + 1)) { + _errnum = EEPROM_OutOfRange; + return; + } + + read(address,cmd,2); + + memcpy(&data,cmd,2); + +} + +/** + * void read(uint32_t address, int32_t& data) + * + * Random read long + * @param address start address (uint32_t) + * @param data long to read (int32_t&) + * @return none +*/ +void EEPROM::read(uint32_t address, int32_t& data) +{ + int8_t cmd[4]; + + // Check error + if(_errnum) + return; + + // Check address + if(!checkAddress(address + 3)) { + _errnum = EEPROM_OutOfRange; + return; + } + + read(address,cmd,4); + + memcpy(&data,cmd,4); + +} + +/** + * void read(uint32_t address, float& data) + * + * Random read float + * @param address start address (uint32_t) + * @param data float to read (float&) + * @return none + */ +void EEPROM::read(uint32_t address, float& data) +{ + int8_t cmd[4]; + + // Check error + if(_errnum) + return; + + // Check address + if(!checkAddress(address + 3)) { + _errnum = EEPROM_OutOfRange; + return; + } + + read(address,cmd,4); + + memcpy(&data,cmd,4); + +} + +/** + * void read(uint32_t address, void *data, uint32_t size) + * + * Random read anything + * @param address start address (uint32_t) + * @param data data to read (void *) + * @param size number of bytes to read (uint32_t) + * @return none +*/ +void EEPROM::read(uint32_t address, void *data, uint32_t size) +{ + int8_t *cmd = NULL; + + // Check error + if(_errnum) + return; + + // Check address + if(!checkAddress(address + size - 1)) { + _errnum = EEPROM_OutOfRange; + return; + } + + cmd = (int8_t *)malloc(size); + + if(cmd == NULL) { + _errnum = EEPROM_MallocError; + return; + } + + read(address,(int8_t *)cmd,size); + + memcpy(data,cmd,size); + + free(cmd); + +} + +/** + * void clear(void) + * + * Clear eeprom (write with 0) + * @param none + * @return none + */ +void EEPROM::clear(void) +{ + int32_t data; + uint32_t i; + + data = 0; + + for(i = 0; i < _size / 4;i++) { + write((uint32_t)(i * 4),data); + } +} + +/** + * void ready(void) + * + * Wait eeprom ready + * @param none + * @return none +*/ +void EEPROM::ready(void) +{ + int ack; + uint8_t addr; + uint8_t cmd[2]; + + // Check error + if(_errnum) + return; + + // Device address + addr = EEPROM_Address | _address; + + cmd[0] = 0; + + // Wait end of write + do { + ack = _i2c.write((int)addr,(char *)cmd,0); + //wait(0.5); + } while(ack != 0); + +} + +/** + * uint32_t getSize(void) + * + * Get eeprom size in bytes + * @param none + * @return size in bytes (uint32_t) +*/ +uint32_t EEPROM::getSize(void) +{ + return(_size); +} + +/** + * const char* getName(void) + * + * Get eeprom name + * @param none + * @return name (const char*) + */ +const char* EEPROM::getName(void) +{ + uint8_t i = 0; + + switch(_type) { + case T24C01 : + i = 0; + break; + case T24C02 : + i = 1; + break; + case T24C04 : + i = 2; + break; + case T24C08 : + i = 3; + break; + case T24C16 : + i = 4; + break; + case T24C32 : + i = 5; + break; + case T24C64 : + i = 6; + break; + case T24C128 : + i = 7; + break; + case T24C256 : + i = 8; + break; + case T24C512 : + i = 9; + break; + case T24C1024 : + i = 10; + break; + case T24C1025 : + i = 11; + break; + } + + return(_name[i]); +} + +/** + * uint8_t getError(void) + * + * Get the current error number (EEPROM_NoError if no error) + * @param none + * @return none +*/ +uint8_t EEPROM::getError(void) +{ + return(_errnum); +} + +/** + * bool checkAddress(uint32_t address) + * + * Check if address is in the eeprom range address + * @param address address to check (uint32_t) + * @return true if in eeprom range, overwise false (bool) +*/ +bool EEPROM::checkAddress(uint32_t address) +{ + bool ret = true; + + switch(_type) { + case T24C01 : + if(address >= T24C01) + ret = false; + break; + case T24C02 : + if(address >= T24C02) + ret = false; + break; + case T24C04 : + if(address >= T24C04) + ret = false; + break; + case T24C08 : + if(address >= T24C08) + ret = false; + break; + case T24C16 : + if(address >= T24C16) + ret = false; + break; + case T24C32 : + if(address >= T24C32) + ret = false; + break; + case T24C64 : + if(address >= T24C64) + ret = false; + break; + case T24C128 : + if(address >= T24C128) + ret = false; + break; + case T24C256 : + if(address >= T24C256) + ret = false; + break; + case T24C512 : + if(address >= T24C512) + ret = false; + break; + case T24C1024 : + if(address >= T24C1024) + ret = false; + break; + case T24C1025 : + if(address >= T24C1025 - 1) + ret = false; + break; + } + + return(ret); +} diff --git a/.piolibdeps/eeprom_ID2284/eeprom.h b/.piolibdeps/eeprom_ID2284/eeprom.h new file mode 100644 index 0000000..94ec800 --- /dev/null +++ b/.piolibdeps/eeprom_ID2284/eeprom.h @@ -0,0 +1,560 @@ +#ifndef __EEPROM__H_ +#define __EEPROM__H_ + +/*********************************************************** +Author: Bernard Borredon +Date : 21 decembre 2015 +Version: 1.3 + - Correct write(uint32_t address, int8_t data[], uint32_t length) for eeprom >= T24C32. + Tested with 24C02, 24C08, 24C16, 24C64, 24C256, 24C512, 24C1025 on LPC1768 (mbed online and µVision V5.16a). + - Correct main test. + +Date : 12 decembre 2013 +Version: 1.2 + - Update api documentation + +Date: 11 december 2013 +Version: 1.1 + - Change address parameter size form uint16_t to uint32_t (error for eeprom > 24C256). + - Change size parameter size from uint16_t to uint32_t (error for eeprom > 24C256). + - Add EEPROM name as a private static const char array. + - Add function getName. + - Add a test program. + +Date: 27 december 2011 +Version: 1.0 +************************************************************/ + +// Includes +#include + +#include "mbed.h" + +// Example +/* +#include + +#include "mbed.h" +#include "eeprom.h" + +#define EEPROM_ADDR 0x0 // I2c EEPROM address is 0x00 + +#define SDA p9 // I2C SDA pin +#define SCL p10 // I2C SCL pin + +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) + +DigitalOut led2(LED2); + +typedef struct _MyData { + int16_t sdata; + int32_t idata; + float fdata; + } MyData; + +static void myerror(std::string msg) +{ + printf("Error %s\n",msg.c_str()); + exit(1); +} + +void eeprom_test(void) +{ + EEPROM ep(SDA,SCL,EEPROM_ADDR,EEPROM::T24C64); // 24C64 eeprom with sda = p9 and scl = p10 + uint8_t data[256],data_r[256]; + int8_t ival; + uint16_t s; + int16_t sdata,sdata_r; + int32_t ldata[1024]; + int32_t eeprom_size,max_size; + uint32_t addr; + int32_t idata,idata_r; + uint32_t i,j,k,l,t,id; + float fdata,fdata_r; + MyData md,md_r; + + eeprom_size = ep.getSize(); + max_size = MIN(eeprom_size,256); + + printf("Test EEPROM I2C model %s of %d bytes\n\n",ep.getName(),eeprom_size); + + // Test sequential read byte (max_size first bytes) + for(i = 0;i < max_size;i++) { + ep.read(i,ival); + data_r[i] = ival; + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + } + + printf("Test sequential read %d first bytes :\n",max_size); + for(i = 0;i < max_size/16;i++) { + for(j = 0;j < 16;j++) { + addr = i * 16 + j; + printf("%3d ",(uint8_t)data_r[addr]); + } + printf("\n"); + } + + // Test sequential read byte (max_size last bytes) + for(i = 0;i < max_size;i++) { + addr = eeprom_size - max_size + i; + ep.read(addr,ival); + data_r[i] = ival; + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + } + + printf("\nTest sequential read %d last bytes :\n",max_size); + for(i = 0;i < max_size/16;i++) { + for(j = 0;j < 16;j++) { + addr = i * 16 + j; + printf("%3d ",(uint8_t)data_r[addr]); + } + printf("\n"); + } + + // Test write byte (max_size first bytes) + for(i = 0;i < max_size;i++) + data[i] = i; + + for(i = 0;i < max_size;i++) { + ep.write(i,(int8_t)data[i]); + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + } + + // Test read byte (max_size first bytes) + for(i = 0;i < max_size;i++) { + ep.read(i,(int8_t&)ival); + data_r[i] = (uint8_t)ival; + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + } + + printf("\nTest write and read %d first bytes :\n",max_size); + for(i = 0;i < max_size/16;i++) { + for(j = 0;j < 16;j++) { + addr = i * 16 + j; + printf("%3d ",(uint8_t)data_r[addr]); + } + printf("\n"); + } + + // Test current address read byte (max_size first bytes) + ep.read((uint32_t)0,(int8_t&)ival); // current address is 0 + data_r[0] = (uint8_t)ival; + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + + for(i = 1;i < max_size;i++) { + ep.read((int8_t&)ival); + data_r[i] = (uint8_t)ival; + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + } + + printf("\nTest current address read %d first bytes :\n",max_size); + for(i = 0;i < max_size/16;i++) { + for(j = 0;j < 16;j++) { + addr = i * 16 + j; + printf("%3d ",(uint8_t)data_r[addr]); + } + printf("\n"); + } + + // Test sequential read byte (first max_size bytes) + ep.read((uint32_t)0,(int8_t *)data_r,(uint32_t) max_size); + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + + printf("\nTest sequential read %d first bytes :\n",max_size); + for(i = 0;i < max_size/16;i++) { + for(j = 0;j < 16;j++) { + addr = i * 16 + j; + printf("%3d ",(uint8_t)data_r[addr]); + } + printf("\n"); + } + + // Test write short, long, float + sdata = -15202; + addr = eeprom_size - 16; + ep.write(addr,(int16_t)sdata); // short write at address eeprom_size - 16 + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + + idata = 45123; + addr = eeprom_size - 12; + ep.write(addr,(int32_t)idata); // long write at address eeprom_size - 12 + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + + fdata = -12.26; + addr = eeprom_size - 8; + ep.write(addr,(float)fdata); // float write at address eeprom_size - 8 + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + + // Test read short, long, float + printf("\nTest write and read short (%d), long (%d), float (%f) :\n", + sdata,idata,fdata); + + ep.read((uint32_t)(eeprom_size - 16),(int16_t&)sdata_r); + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + printf("sdata %d\n",sdata_r); + + ep.read((uint32_t)(eeprom_size - 12),(int32_t&)idata_r); + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + printf("idata %d\n",idata_r); + + ep.read((uint32_t)(eeprom_size - 8),fdata_r); + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + printf("fdata %f\n",fdata_r); + + // Test read and write a structure + md.sdata = -15203; + md.idata = 45124; + md.fdata = -12.27; + + ep.write((uint32_t)(eeprom_size - 32),(void *)&md,sizeof(md)); // write a structure eeprom_size - 32 + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + + printf("\nTest write and read a structure (%d %d %f) :\n",md.sdata,md.idata,md.fdata); + + ep.read((uint32_t)(eeprom_size - 32),(void *)&md_r,sizeof(md_r)); + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + + printf("md.sdata %d\n",md_r.sdata); + printf("md.idata %d\n",md_r.idata); + printf("md.fdata %f\n",md_r.fdata); + + // Test read and write of an array of the first max_size bytes + for(i = 0;i < max_size;i++) + data[i] = max_size - i - 1; + + ep.write((uint32_t)(0),data,(uint32_t)max_size); + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + + ep.read((uint32_t)(0),data_r,(uint32_t)max_size); + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + + printf("\nTest write and read an array of the first %d bytes :\n",max_size); + for(i = 0;i < max_size/16;i++) { + for(j = 0;j < 16;j++) { + addr = i * 16 + j; + printf("%3d ",(uint8_t)data_r[addr]); + } + printf("\n"); + } + printf("\n"); + + // Test write and read an array of int32 + s = eeprom_size / 4; // size of eeprom in int32 + int ldata_size = sizeof(ldata) / 4; // size of data array in int32 + l = s / ldata_size; // loop index + + // size of read / write in bytes + t = eeprom_size; + if(t > ldata_size * 4) + t = ldata_size * 4; + + printf("Test write and read an array of %d int32 (write entire memory) :\n",t/4); + + // Write entire eeprom + if(l) { + for(k = 0;k < l;k++) { + for(i = 0;i < ldata_size;i++) + ldata[i] = ldata_size * k + i; + + addr = k * ldata_size * 4; + ep.write(addr,(void *)ldata,t); + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + } + + printf("Write OK\n"); + + // Read entire eeprom + id = 0; + for(k = 0;k < l;k++) { + addr = k * ldata_size * 4; + ep.read(addr,(void *)ldata,t); + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + + // format outputs with 8 words rows + for(i = 0;i < ldata_size / 8;i++) { + id++; + printf("%4d ",id); + for(j = 0;j < 8;j++) { + addr = i * 8 + j; + printf("%5d ",ldata[addr]); + } + printf("\n"); + } + } + } + else { + for(i = 0;i < s;i++) + ldata[i] = i; + + addr = 0; + ep.write(addr,(void *)ldata,t); + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + + printf("Write OK\n"); + + // Read entire eeprom + id = 0; + + addr = 0; + ep.read(addr,(void *)ldata,t); + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + + // format outputs with 8 words rows + for(i = 0;i < s / 8;i++) { + id++; + printf("%4d ",id); + for(j = 0;j < 8;j++) { + addr = i * 8 + j; + printf("%5d ",ldata[addr]); + } + printf("\n"); + } + } + + // clear eeprom + printf("\nClear eeprom\n"); + + ep.clear(); + if(ep.getError() != 0) + myerror(ep.getErrorMessage()); + + printf("End\n"); + +} + +int main() +{ + + eeprom_test(); + + return(0); +} +*/ + +// Defines +#define EEPROM_Address 0xa0 + +#define EEPROM_NoError 0x00 +#define EEPROM_BadAddress 0x01 +#define EEPROM_I2cError 0x02 +#define EEPROM_ParamError 0x03 +#define EEPROM_OutOfRange 0x04 +#define EEPROM_MallocError 0x05 + +#define EEPROM_MaxError 6 + +static std::string _ErrorMessageEEPROM[EEPROM_MaxError] = { + "", + "Bad chip address", + "I2C error (nack)", + "Invalid parameter", + "Data address out of range", + "Memory allocation error" + }; + +/** EEPROM Class +*/ +class EEPROM { +public: + enum TypeEeprom {T24C01=128,T24C02=256,T24C04=512,T24C08=1024,T24C16=2048, + T24C32=4096,T24C64=8192,T24C128=16384,T24C256=32768, + T24C512=65536,T24C1024=131072,T24C1025=131073} Type; + + /** + * Constructor, initialize the eeprom on i2c interface. + * @param sda sda i2c pin (PinName) + * @param scl scl i2c pin (PinName) + * @param address eeprom address, according to eeprom type (uint8_t) + * @param type eeprom type (TypeEeprom) + * @return none + */ + EEPROM(PinName sda, PinName scl, uint8_t address, TypeEeprom type); + + /** + * Random read byte + * @param address start address (uint32_t) + * @param data byte to read (int8_t&) + * @return none + */ + void read(uint32_t address, int8_t& data); + + /** + * Random read short + * @param address start address (uint32_t) + * @param data short to read (int16_t&) + * @return none + */ + void read(uint32_t address, int16_t& data); + + /** + * Random read long + * @param address start address (uint32_t) + * @param data long to read (int32_t&) + * @return none + */ + void read(uint32_t address, int32_t& data); + + /** + * Random read float + * @param address start address (uint32_t) + * @param data float to read (float&) + * @return none + */ + void read(uint32_t address, float& data); + + /** + * Random read anything + * @param address start address (uint32_t) + * @param data data to read (void *) + * @param size number of bytes to read (uint32_t) + * @return none + */ + void read(uint32_t address, void *data, uint32_t size); + + /** + * Current address read byte + * @param data byte to read (int8_t&) + * @return none + */ + void read(int8_t& data); + + /** + * Sequential read byte + * @param address start address (uint32_t) + * @param data bytes array to read (int8_t[]&) + * @param size number of bytes to read (uint32_t) + * @return none + */ + void read(uint32_t address, int8_t *data, uint32_t size); + + /** + * Write byte + * @param address start address (uint32_t) + * @param data byte to write (int8_t) + * @return none + */ + void write(uint32_t address, int8_t data); + + /** + * Write short + * @param address start address (uint32_t) + * @param data short to write (int16_t) + * @return none + */ + void write(uint32_t address, int16_t data); + + /** + * Write long + * @param address start address (uint32_t) + * @param data long to write (int32_t) + * @return none + */ + void write(uint32_t address, int32_t data); + + /** + * Write float + * @param address start address (uint32_t) + * @param data float to write (float) + * @return none + */ + void write(uint32_t address, float data); + + /** + * Write anything (use the page write mode) + * @param address start address (uint32_t) + * @param data data to write (void *) + * @param size number of bytes to write (uint32_t) + * @return none + */ + void write(uint32_t address, void *data, uint32_t size); + + /** + * Write array of bytes (use the page mode) + * @param address start address (uint32_t) + * @param data bytes array to write (int8_t[]) + * @param size number of bytes to write (uint32_t) + * @return none + */ + void write(uint32_t address, int8_t data[], uint32_t size); + + /** + * Wait eeprom ready + * @param none + * @return none + */ + void ready(void); + + /** + * Get eeprom size in bytes + * @param none + * @return size in bytes (uint32_t) + */ + uint32_t getSize(void); + + /** + * Get eeprom name + * @param none + * @return name (const char*) + */ + const char* getName(void); + + /** + * Clear eeprom (write with 0) + * @param none + * @return none + */ + void clear(void); + + /** + * Get the current error number (EEPROM_NoError if no error) + * @param none + * @return none + */ + uint8_t getError(void); + + /** + * Get current error message + * @param none + * @return current error message(std::string) + */ + std::string getErrorMessage(void) + { + return(_ErrorMessageEEPROM[_errnum]); + } + +//---------- local variables ---------- +private: + I2C _i2c; // Local i2c communication interface instance + int _address; // Local i2c address + uint8_t _errnum; // Error number + TypeEeprom _type; // EEPROM type + uint8_t _page_write; // Page write size + uint8_t _page_number; // Number of page + uint32_t _size; // Size in bytes + bool checkAddress(uint32_t address); // Check address range + static const char * const _name[]; // eeprom name +//------------------------------------- +}; +#endif diff --git a/.piolibdeps/eeprom_ID2284/library.json b/.piolibdeps/eeprom_ID2284/library.json new file mode 100644 index 0000000..623209c --- /dev/null +++ b/.piolibdeps/eeprom_ID2284/library.json @@ -0,0 +1,22 @@ +{ + "authors": { + "name": "bernard borredon", + "url": "https://os.mbed.com/users/bborredon/" + }, + "description": "I2C EEPROM library from 24C01 to 24C1025", + "examples": [ + "https://os.mbed.com/users/pai2015arminnogo/code/Digitalni_Sat_MG_AN/", + "https://os.mbed.com/users/sameera0824/code/EEROM_1768/", + "https://os.mbed.com/users/sameera0824/code/EEPROM_kamal/", + "https://os.mbed.com/users/sameera0824/code/EEPROM_MY/", + "https://os.mbed.com/users/jolyon/code/ColourSensor/" + ], + "frameworks": "mbed", + "keywords": "eeprom", + "name": "eeprom", + "platforms": "*", + "repository": { + "type": "hg", + "url": "https://os.mbed.com/users/bborredon/code/eeprom/" + } +} \ No newline at end of file