Merge branch 'WiFi-Manager' into refactor-BT/I2C
288
.cproject
@@ -1,96 +1,198 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
|
||||
<storageModule moduleId="org.eclipse.cdt.core.settings">
|
||||
<cconfiguration id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786">
|
||||
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786" moduleId="org.eclipse.cdt.core.settings" name="Default">
|
||||
<externalSettings/>
|
||||
<extensions>
|
||||
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
</extensions>
|
||||
</storageModule>
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
<configuration artifactName="${ProjName}" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786" name="Default" optionalBuildProperties="org.eclipse.cdt.docker.launcher.containerbuild.property.volumes=,org.eclipse.cdt.docker.launcher.containerbuild.property.selectedvolumes=" parent="org.eclipse.cdt.build.core.emptycfg">
|
||||
<folderInfo id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786.1800826258" name="/" resourcePath="">
|
||||
<toolChain id="cdt.managedbuild.toolchain.gnu.cross.base.811827721" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.base">
|
||||
<option id="cdt.managedbuild.option.gnu.cross.prefix.1666584715" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix"/>
|
||||
<option id="cdt.managedbuild.option.gnu.cross.path.144124148" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path"/>
|
||||
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.1562292378" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
|
||||
<builder id="cdt.managedbuild.builder.gnu.cross.1011968237" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.builder.gnu.cross"/>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.1502936757" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
|
||||
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1614739014" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.254690821" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler">
|
||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1365876654" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.407309631" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.linker.765822218" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
|
||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.655344480" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
|
||||
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
|
||||
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
|
||||
</inputType>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.1494383819" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.assembler.280698320" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler">
|
||||
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1422333326" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
|
||||
</tool>
|
||||
</toolChain>
|
||||
</folderInfo>
|
||||
</configuration>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||
</cconfiguration>
|
||||
</storageModule>
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
<project id="squeezelite-esp32.null.1272501664" name="squeezelite-esp32"/>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
||||
<storageModule moduleId="scannerConfiguration">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1943329896;cdt.managedbuild.toolchain.gnu.cross.base.1943329896.30011915;cdt.managedbuild.tool.gnu.cross.cpp.compiler.1749746745;cdt.managedbuild.tool.gnu.cpp.compiler.input.1914005798">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
</scannerConfigBuildInfo>
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1476804786;cdt.managedbuild.toolchain.gnu.cross.base.1476804786.1800826258;cdt.managedbuild.tool.gnu.cross.cpp.compiler.254690821;cdt.managedbuild.tool.gnu.cpp.compiler.input.1365876654">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
</scannerConfigBuildInfo>
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1943329896;cdt.managedbuild.toolchain.gnu.cross.base.1943329896.30011915;cdt.managedbuild.tool.gnu.cross.c.compiler.2083405506;cdt.managedbuild.tool.gnu.c.compiler.input.404320567">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
</scannerConfigBuildInfo>
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1476804786;cdt.managedbuild.toolchain.gnu.cross.base.1476804786.1800826258;cdt.managedbuild.tool.gnu.cross.c.compiler.1502936757;cdt.managedbuild.tool.gnu.c.compiler.input.1614739014">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
</scannerConfigBuildInfo>
|
||||
</storageModule>
|
||||
<storageModule moduleId="refreshScope"/>
|
||||
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
|
||||
<buildTargets>
|
||||
<target name="all" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||
<buildCommand>make</buildCommand>
|
||||
<buildArguments/>
|
||||
<buildTarget>all</buildTarget>
|
||||
<stopOnError>true</stopOnError>
|
||||
<useDefaultCommand>true</useDefaultCommand>
|
||||
<runAllBuilders>true</runAllBuilders>
|
||||
</target>
|
||||
<target name="size-components" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||
<buildCommand>make</buildCommand>
|
||||
<buildArguments/>
|
||||
<buildTarget>size-components</buildTarget>
|
||||
<stopOnError>true</stopOnError>
|
||||
<useDefaultCommand>true</useDefaultCommand>
|
||||
<runAllBuilders>true</runAllBuilders>
|
||||
</target>
|
||||
<target name="flash" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||
<buildCommand>make</buildCommand>
|
||||
<buildArguments/>
|
||||
<buildTarget>flash</buildTarget>
|
||||
<stopOnError>true</stopOnError>
|
||||
<useDefaultCommand>true</useDefaultCommand>
|
||||
<runAllBuilders>true</runAllBuilders>
|
||||
</target>
|
||||
</buildTargets>
|
||||
</storageModule>
|
||||
|
||||
<storageModule moduleId="org.eclipse.cdt.core.settings">
|
||||
|
||||
<cconfiguration id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786">
|
||||
|
||||
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786" moduleId="org.eclipse.cdt.core.settings" name="Default">
|
||||
|
||||
<externalSettings/>
|
||||
|
||||
<extensions>
|
||||
|
||||
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
|
||||
|
||||
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
|
||||
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
|
||||
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
|
||||
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
|
||||
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
|
||||
</extensions>
|
||||
|
||||
</storageModule>
|
||||
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
|
||||
<configuration artifactName="${ProjName}" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786" name="Default" optionalBuildProperties="org.eclipse.cdt.docker.launcher.containerbuild.property.selectedvolumes=,org.eclipse.cdt.docker.launcher.containerbuild.property.volumes=" parent="org.eclipse.cdt.build.core.emptycfg">
|
||||
|
||||
<folderInfo id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786.1800826258" name="/" resourcePath="">
|
||||
|
||||
<toolChain id="cdt.managedbuild.toolchain.gnu.cross.base.811827721" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.base">
|
||||
|
||||
<option id="cdt.managedbuild.option.gnu.cross.prefix.1666584715" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix"/>
|
||||
|
||||
<option id="cdt.managedbuild.option.gnu.cross.path.144124148" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path"/>
|
||||
|
||||
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.1562292378" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
|
||||
|
||||
<builder arguments="${IDF_PATH}/tools/windows/eclipse_make.py" command="python" id="cdt.managedbuild.builder.gnu.cross.1011968237" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="cdt.managedbuild.builder.gnu.cross"/>
|
||||
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.1502936757" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
|
||||
|
||||
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1614739014" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
|
||||
|
||||
</tool>
|
||||
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.254690821" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler">
|
||||
|
||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1365876654" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
|
||||
|
||||
</tool>
|
||||
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.407309631" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
|
||||
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.linker.765822218" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
|
||||
|
||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.655344480" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
|
||||
|
||||
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
|
||||
|
||||
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
|
||||
|
||||
</inputType>
|
||||
|
||||
</tool>
|
||||
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.1494383819" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
|
||||
|
||||
<tool id="cdt.managedbuild.tool.gnu.cross.assembler.280698320" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler">
|
||||
|
||||
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1422333326" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
|
||||
|
||||
</tool>
|
||||
|
||||
</toolChain>
|
||||
|
||||
</folderInfo>
|
||||
|
||||
</configuration>
|
||||
|
||||
</storageModule>
|
||||
|
||||
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||
|
||||
</cconfiguration>
|
||||
|
||||
</storageModule>
|
||||
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
|
||||
<project id="squeezelite-esp32.null.1272501664" name="squeezelite-esp32"/>
|
||||
|
||||
</storageModule>
|
||||
|
||||
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
||||
|
||||
<storageModule moduleId="scannerConfiguration">
|
||||
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1943329896;cdt.managedbuild.toolchain.gnu.cross.base.1943329896.30011915;cdt.managedbuild.tool.gnu.cross.cpp.compiler.1749746745;cdt.managedbuild.tool.gnu.cpp.compiler.input.1914005798">
|
||||
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
|
||||
</scannerConfigBuildInfo>
|
||||
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1476804786;cdt.managedbuild.toolchain.gnu.cross.base.1476804786.1800826258;cdt.managedbuild.tool.gnu.cross.cpp.compiler.254690821;cdt.managedbuild.tool.gnu.cpp.compiler.input.1365876654">
|
||||
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
|
||||
</scannerConfigBuildInfo>
|
||||
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1943329896;cdt.managedbuild.toolchain.gnu.cross.base.1943329896.30011915;cdt.managedbuild.tool.gnu.cross.c.compiler.2083405506;cdt.managedbuild.tool.gnu.c.compiler.input.404320567">
|
||||
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
|
||||
</scannerConfigBuildInfo>
|
||||
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1476804786;cdt.managedbuild.toolchain.gnu.cross.base.1476804786.1800826258;cdt.managedbuild.tool.gnu.cross.c.compiler.1502936757;cdt.managedbuild.tool.gnu.c.compiler.input.1614739014">
|
||||
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
|
||||
</scannerConfigBuildInfo>
|
||||
|
||||
</storageModule>
|
||||
|
||||
<storageModule moduleId="refreshScope" versionNumber="2">
|
||||
|
||||
<configuration configurationName="Default">
|
||||
|
||||
<resource resourceType="PROJECT" workspacePath="/squeezelite-esp32"/>
|
||||
|
||||
</configuration>
|
||||
|
||||
</storageModule>
|
||||
|
||||
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
|
||||
|
||||
<buildTargets>
|
||||
|
||||
<target name="all" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||
|
||||
<buildCommand>make</buildCommand>
|
||||
|
||||
<buildArguments/>
|
||||
|
||||
<buildTarget>all</buildTarget>
|
||||
|
||||
<stopOnError>true</stopOnError>
|
||||
|
||||
<useDefaultCommand>true</useDefaultCommand>
|
||||
|
||||
<runAllBuilders>true</runAllBuilders>
|
||||
|
||||
</target>
|
||||
|
||||
<target name="size-components" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||
|
||||
<buildCommand>make</buildCommand>
|
||||
|
||||
<buildArguments/>
|
||||
|
||||
<buildTarget>size-components</buildTarget>
|
||||
|
||||
<stopOnError>true</stopOnError>
|
||||
|
||||
<useDefaultCommand>true</useDefaultCommand>
|
||||
|
||||
<runAllBuilders>true</runAllBuilders>
|
||||
|
||||
</target>
|
||||
|
||||
<target name="flash" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||
|
||||
<buildCommand>make</buildCommand>
|
||||
|
||||
<buildArguments/>
|
||||
|
||||
<buildTarget>flash</buildTarget>
|
||||
|
||||
<stopOnError>true</stopOnError>
|
||||
|
||||
<useDefaultCommand>true</useDefaultCommand>
|
||||
|
||||
<runAllBuilders>true</runAllBuilders>
|
||||
|
||||
</target>
|
||||
|
||||
</buildTargets>
|
||||
|
||||
</storageModule>
|
||||
|
||||
</cproject>
|
||||
|
||||
1
.gitignore
vendored
@@ -66,4 +66,3 @@ libs/
|
||||
/cdump.cmd
|
||||
/_*
|
||||
sdkconfig
|
||||
*_history/
|
||||
|
||||
9
.gitmodules
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
[submodule "components/libwebsockets"]
|
||||
path = components/libwebsockets
|
||||
url = https://github.com/warmcat/libwebsockets.git
|
||||
[submodule "components/mbedtls"]
|
||||
path = components/mbedtls
|
||||
url = https://github.com/lws-team/mbedtls.git
|
||||
[submodule "components/lws-esp32"]
|
||||
path = components/lws-esp32
|
||||
url = https://github.com/huming2207/lws-esp32.git
|
||||
@@ -1,15 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project>
|
||||
<configuration id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786" name="Default">
|
||||
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
|
||||
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuildCommandParser" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser" keep-relative-paths="false" name="CDT GCC Build Output Parser" parameter="xtensa-esp32-elf-(gcc|g\+\+|c\+\+|cc|cpp|clang)" prefer-non-shared="true"/>
|
||||
<provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="-869409282784451881" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="xtensa-esp32-elf-gcc ${FLAGS} -std=c++11 -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
|
||||
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||
</provider>
|
||||
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||
</extension>
|
||||
</configuration>
|
||||
|
||||
<configuration id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786" name="Default">
|
||||
|
||||
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
|
||||
|
||||
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||
|
||||
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||
|
||||
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuildCommandParser" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser" keep-relative-paths="false" name="CDT GCC Build Output Parser" parameter="xtensa-esp32-elf-(gcc|g\+\+|c\+\+|cc|cpp|clang)" prefer-non-shared="true"/>
|
||||
|
||||
<provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="1104469646593236716" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="xtensa-esp32-elf-gcc ${FLAGS} -std=c++11 -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
|
||||
|
||||
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||
|
||||
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||
|
||||
</provider>
|
||||
|
||||
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||
|
||||
</extension>
|
||||
|
||||
</configuration>
|
||||
|
||||
</project>
|
||||
|
||||
3
CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(squeezelite-esp32)
|
||||
2
Makefile
@@ -4,6 +4,4 @@
|
||||
#
|
||||
|
||||
PROJECT_NAME := squeezelite
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
TODO
|
||||
- when IP changes, best is to reboot at this point
|
||||
|
||||
MOST IMPORTANT: create the right default config file
|
||||
- make defconfig
|
||||
Then adapt the config file to your wifi/BT/I2C device (can alos be done on the command line)
|
||||
@@ -23,11 +20,6 @@ nvs_set autoexec2 str -v "squeezelite -o I2S -b 500:2000 -d all=info -m ESP32"
|
||||
|
||||
nvs_set autoexec u8 -v 1
|
||||
|
||||
4/ set bluetooth & airplaysink name (if not set in menuconfig)
|
||||
|
||||
nvs_set bt_sink_name str -v "<name>"
|
||||
nvs_set airplay_sink_name str -v "<name>"
|
||||
|
||||
The "join" and "squeezelite" commands can also be typed at the prompt to start manually. Use "help" to see the list.
|
||||
|
||||
The squeezelite options are very similar to the regular Linux ones. Differences are :
|
||||
@@ -43,7 +35,6 @@ To add options that require quotes ("), escape them with \". For example, so use
|
||||
nvs_set autoexec2 str -v "squeezelite -o \"BT -n 'MySpeaker'\" -b 500:2000 -R -u m -Z 192000 -r \"44100-44100\""
|
||||
|
||||
# Additional misc notes to do you build
|
||||
- as of this writing, ESP-IDF has a bug int he way the PLL values are calculated for i2s, so you *must* use the i2s.c file in the patch directory
|
||||
- for all libraries, add -mlongcalls.
|
||||
- audio libraries are complicated to rebuild, open an issue if you really want to
|
||||
- libmad, libflac (no esp's version), libvorbis (tremor - not esp's version), alac work
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
set(COMPONENT_SRCS "cmd_i2ctools.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
set(COMPONENT_REQUIRES console spi_flash)
|
||||
|
||||
register_component()
|
||||
|
||||
@@ -47,7 +47,7 @@ static const type_str_pair_t type_str_pair[] = {
|
||||
|
||||
static const size_t TYPE_STR_PAIR_SIZE = sizeof(type_str_pair) / sizeof(type_str_pair[0]);
|
||||
static const char *ARG_TYPE_STR = "type can be: i8, u8, i16, u16 i32, u32 i64, u64, str, blob";
|
||||
char current_namespace[16] = "storage";
|
||||
char current_namespace[16] = "espwifimgr";
|
||||
static const char * TAG = "platform_esp32";
|
||||
|
||||
static struct {
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
#ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS
|
||||
#define WITH_TASKS_INFO 1
|
||||
#endif
|
||||
#define LWS_MAGIC_REBOOT_TYPE_ADS 0x50001ffc
|
||||
#define LWS_MAGIC_REBOOT_TYPE_REQ_FACTORY 0xb00bcafe
|
||||
#define LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY 0xfaceb00b
|
||||
#define LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON 0xf0cedfac
|
||||
#define LWS_MAGIC_REBOOT_TYPE_REQ_FACTORY_ERASE_OTA 0xfac0eeee
|
||||
|
||||
|
||||
static const char * TAG = "platform_esp32";
|
||||
|
||||
@@ -37,6 +43,7 @@ static void register_version();
|
||||
static void register_restart();
|
||||
static void register_deep_sleep();
|
||||
static void register_light_sleep();
|
||||
static void register_factory_boot();
|
||||
#if WITH_TASKS_INFO
|
||||
static void register_tasks();
|
||||
#endif
|
||||
@@ -49,6 +56,7 @@ void register_system()
|
||||
register_restart();
|
||||
register_deep_sleep();
|
||||
register_light_sleep();
|
||||
register_factory_boot();
|
||||
#if WITH_TASKS_INFO
|
||||
register_tasks();
|
||||
#endif
|
||||
@@ -91,7 +99,20 @@ static int restart(int argc, char **argv)
|
||||
ESP_LOGI(TAG, "Restarting");
|
||||
esp_restart();
|
||||
}
|
||||
void guided_factory()
|
||||
{
|
||||
ESP_LOGI(TAG, "Rebooting to factory.");
|
||||
uint32_t *p_force_factory_magic = (uint32_t *)LWS_MAGIC_REBOOT_TYPE_ADS;
|
||||
*p_force_factory_magic = LWS_MAGIC_REBOOT_TYPE_REQ_FACTORY;
|
||||
|
||||
esp_restart();
|
||||
|
||||
}
|
||||
static int restart_factory(int argc, char **argv)
|
||||
{
|
||||
guided_factory();
|
||||
return 1;
|
||||
}
|
||||
static void register_restart()
|
||||
{
|
||||
const esp_console_cmd_t cmd = {
|
||||
@@ -103,6 +124,16 @@ static void register_restart()
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
}
|
||||
|
||||
static void register_factory_boot()
|
||||
{
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "factory",
|
||||
.help = "Resets and boot to factory (if available)",
|
||||
.hint = NULL,
|
||||
.func = &restart_factory,
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
}
|
||||
/** 'free' command prints available heap memory */
|
||||
|
||||
static int free_mem(int argc, char **argv)
|
||||
|
||||
@@ -14,6 +14,7 @@ extern "C" {
|
||||
|
||||
// Register system functions
|
||||
void register_system();
|
||||
void guided_factory();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
set(COMPONENT_ADD_INCLUDEDIRS . )
|
||||
idf_component_register(SRCS "bt_app_core.c" "bt_app_sink.c" "bt_app_source.c"
|
||||
INCLUDE_DIRS . ../tools/
|
||||
REQUIRES esp_common
|
||||
PRIV_REQUIRES freertos bt io nvs_flash esp32 spi_flash newlib log console pthread
|
||||
)
|
||||
|
||||
set(COMPONENT_SRCS "bt_app_core.c" )
|
||||
set(REQUIRES esp_common)
|
||||
set(REQUIRES_COMPONENTS freertos nvs_flash esp32 spi_flash newlib log console )
|
||||
|
||||
register_component()
|
||||
|
||||
6
components/io/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
idf_component_register(SRCS "led.c"
|
||||
INCLUDE_DIRS . ../tools/
|
||||
|
||||
)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
#ifndef LED_H
|
||||
|
||||
#define LED_H
|
||||
#include "driver/gpio.h"
|
||||
|
||||
enum { LED_GREEN = 0, LED_RED };
|
||||
@@ -35,4 +35,4 @@ bool led_unconfig(int idx);
|
||||
bool led_blink_core(int idx, int ontime, int offtime, bool push);
|
||||
bool led_unpush(int idx);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
6
components/wifi-manager/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
idf_component_register(SRCS "dns_server.c" "http_server.c" "json.c" "wifi_manager.c"
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES esp_common
|
||||
PRIV_REQUIRES newlib freertos spi_flash nvs_flash mdns pthread wpa_supplicant cmd_system
|
||||
EMBED_FILES style.css jquery.gz code.js index.html
|
||||
)
|
||||
67
components/wifi-manager/Kconfig.projbuild
Normal file
@@ -0,0 +1,67 @@
|
||||
menu "Wifi Manager Configuration"
|
||||
|
||||
config WIFI_MANAGER_TASK_PRIORITY
|
||||
int "RTOS Task Priority for the wifi_manager"
|
||||
default 5
|
||||
help
|
||||
Tasks spawn by the manager will have a priority of WIFI_MANAGER_TASK_PRIORITY-1. For this particular reason, minimum recommended task priority is 2.
|
||||
|
||||
config WIFI_MANAGER_MAX_RETRY
|
||||
int "Max Retry on failed connection"
|
||||
default 2
|
||||
help
|
||||
Defines when a connection is lost/attempt to connect is made, how many retries should be made before giving up.
|
||||
|
||||
config DEFAULT_AP_SSID
|
||||
string "Access Point SSID"
|
||||
default "esp32"
|
||||
help
|
||||
SSID (network name) the the esp32 will broadcast.
|
||||
|
||||
config DEFAULT_AP_PASSWORD
|
||||
string "Access Point Password"
|
||||
default "esp32pwd"
|
||||
help
|
||||
Password used for the Access Point. Leave empty and set AUTH MODE to WIFI_AUTH_OPEN for no password.
|
||||
|
||||
config DEFAULT_AP_CHANNEL
|
||||
int "Access Point WiFi Channel"
|
||||
default 1
|
||||
help
|
||||
Be careful you might not see the access point if you use a channel not allowed in your country.
|
||||
|
||||
config DEFAULT_AP_IP
|
||||
string "Access Point IP Address"
|
||||
default "10.10.0.1"
|
||||
help
|
||||
This is used for the redirection to the captive portal. It is recommended to leave unchanged.
|
||||
|
||||
config DEFAULT_AP_GATEWAY
|
||||
string "Access Point IP Gateway"
|
||||
default "10.10.0.1"
|
||||
help
|
||||
This is used for the redirection to the captive portal. It is recommended to leave unchanged.
|
||||
|
||||
config DEFAULT_AP_NETMASK
|
||||
string "Access Point Netmask"
|
||||
default "255.255.255.0"
|
||||
help
|
||||
This is used for the redirection to the captive portal. It is recommended to leave unchanged.
|
||||
|
||||
config DEFAULT_AP_MAX_CONNECTIONS
|
||||
int "Access Point Max Connections"
|
||||
default 4
|
||||
help
|
||||
Max is 4.
|
||||
|
||||
config DEFAULT_AP_BEACON_INTERVAL
|
||||
int "Access Point Beacon Interval (ms)"
|
||||
default 100
|
||||
help
|
||||
100ms is the recommended default.
|
||||
config DEFAULT_COMMAND_LINE
|
||||
string "Default command line to execute"
|
||||
default "squeezelite -o I2S -b 500:2000 -d all=info"
|
||||
help
|
||||
This is the command to run when starting the device
|
||||
endmenu
|
||||
19
components/wifi-manager/LICENSE.md
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2017-2019 Tony Pottier
|
||||
|
||||
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.
|
||||
41
components/wifi-manager/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# What is esp32-wifi-manager?
|
||||
*esp32-wifi-manager* is an esp32 program that enables easy management of wifi networks through a web application.
|
||||
|
||||
*esp32-wifi-manager* is **lightweight** (8KB of task stack in total) and barely uses any CPU power through a completely event driven architecture. It's an all in one wifi scanner, http server & dns daemon living in the least amount of RAM possible.
|
||||
|
||||
For real time constrained applications, *esp32-wifi-manager* can live entirely on PRO CPU, leaving the entire APP CPU untouched for your own needs.
|
||||
|
||||
*esp32-wifi-manager* will automatically attempt to re-connect to a previously saved network on boot, and it will start its own wifi access point through which you can manage wifi networks if a saved network cannot be found and/or if the connection is lost.
|
||||
|
||||
*esp32-wifi-manager* is an esp-idf project that compiles successfully with the esp-idf 3.2 release. You can simply copy the project and start adding your own code to it.
|
||||
|
||||
# Demo
|
||||
[](http://www.youtube.com/watch?v=hxlZi15bym4)
|
||||
|
||||
# Look and Feel
|
||||
 
|
||||
|
||||
# Adding esp32-wifi-manager to your code
|
||||
Ther are effectively three different ways you can embed esp32-wifi-manager with your code:
|
||||
* Just forget about it and poll in your code for wifi connectivity status
|
||||
* Use event callbacks
|
||||
* Modify esp32-wifi-manager code directly to fit your needs
|
||||
|
||||
**Event callbacks** are the cleanest way to use the wifi manager and that's the recommended way to do it. A typical use-case would be to get notified when wifi manager finally gets a connection an access point. In order to do this you can simply define a callback function:
|
||||
|
||||
```c
|
||||
void cb_connection_ok(void *pvParameter){
|
||||
ESP_LOGI(TAG, "I have a connection!");
|
||||
}
|
||||
```
|
||||
|
||||
Then just register it by calling:
|
||||
|
||||
```c
|
||||
wifi_manager_set_callback(EVENT_STA_GOT_IP, &cb_connection_ok);
|
||||
```
|
||||
|
||||
That's it! Now everytime the event is triggered it will call this function.
|
||||
|
||||
# License
|
||||
*esp32-wifi-manager* is MIT licensed. As such, it can be included in any project, commercial or not, as long as you retain original copyright. Please make sure to read the license file.
|
||||
12
components/wifi-manager/ap.json
Normal file
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{"ssid":"Pantum-AP-A6D49F","chan":11,"rssi":-55,"auth":4},
|
||||
{"ssid":"a0308","chan":1,"rssi":-56,"auth":3},
|
||||
{"ssid":"dlink-D9D8","chan":11,"rssi":-82,"auth":4},
|
||||
{"ssid":"Linksys06730","chan":7,"rssi":-85,"auth":3},
|
||||
{"ssid":"SINGTEL-5171","chan":9,"rssi":-88,"auth":4},
|
||||
{"ssid":"1126-1","chan":11,"rssi":-89,"auth":4},
|
||||
{"ssid":"The Shah 5GHz-2","chan":1,"rssi":-90,"auth":3},
|
||||
{"ssid":"SINGTEL-1D28 (2G)","chan":11,"rssi":-91,"auth":3},
|
||||
{"ssid":"dlink-F864","chan":1,"rssi":-92,"auth":4},
|
||||
{"ssid":"dlink-74F0","chan":1,"rssi":-93,"auth":4}
|
||||
]
|
||||
439
components/wifi-manager/code.js
Normal file
@@ -0,0 +1,439 @@
|
||||
var commandHeader = 'squeezelite -b 500:2000 -d all=info ';
|
||||
|
||||
// First, checks if it isn't implemented yet.
|
||||
if (!String.prototype.format) {
|
||||
String.prototype.format = function() {
|
||||
var args = arguments;
|
||||
return this.replace(/{(\d+)}/g, function(match, number) {
|
||||
return typeof args[number] != 'undefined'
|
||||
? args[number]
|
||||
: match
|
||||
;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
var apList = null;
|
||||
var selectedSSID = "";
|
||||
var refreshAPInterval = null;
|
||||
var checkStatusInterval = null;
|
||||
|
||||
var StatusIntervalActive = false;
|
||||
var ConfigIntervalActive = false;
|
||||
var RefreshAPIIntervalActive = false;
|
||||
|
||||
|
||||
function stopCheckStatusInterval(){
|
||||
if(checkStatusInterval != null){
|
||||
clearTimeout(checkStatusInterval);
|
||||
checkStatusInterval = null;
|
||||
}
|
||||
StatusIntervalActive = false;
|
||||
}
|
||||
|
||||
function stopRefreshAPInterval(){
|
||||
|
||||
if(refreshAPInterval != null){
|
||||
clearTimeout(refreshAPInterval);
|
||||
refreshAPInterval = null;
|
||||
}
|
||||
RefreshAPIIntervalActive = false;
|
||||
}
|
||||
|
||||
|
||||
function startCheckStatusInterval(){
|
||||
StatusIntervalActive = true;
|
||||
checkStatusInterval = setTimeout(checkStatus, 950);
|
||||
}
|
||||
|
||||
function startRefreshAPInterval(){
|
||||
RefreshAPIIntervalActive = true;
|
||||
refreshAPInterval = setTimeout(refreshAP, 2800);
|
||||
}
|
||||
|
||||
|
||||
function RepeatCheckStatusInterval(){
|
||||
if(StatusIntervalActive)
|
||||
startCheckStatusInterval();
|
||||
}
|
||||
|
||||
function RepeatCheckConfigInterval(){
|
||||
if(ConfigIntervalActive)
|
||||
startCheckConfigInterval();
|
||||
}
|
||||
|
||||
function RepeatRefreshAPInterval(){
|
||||
if(RefreshAPIIntervalActive)
|
||||
startRefreshAPInterval()
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
$("#wifi-status").on("click", ".ape", function() {
|
||||
$( "#wifi" ).slideUp( "fast", function() {});
|
||||
$( "#connect-details" ).slideDown( "fast", function() {});
|
||||
});
|
||||
|
||||
$("#manual_add").on("click", ".ape", function() {
|
||||
selectedSSID = $(this).text();
|
||||
$( "#ssid-pwd" ).text(selectedSSID);
|
||||
$( "#wifi" ).slideUp( "fast", function() {});
|
||||
$( "#connect_manual" ).slideDown( "fast", function() {});
|
||||
$( "#connect" ).slideUp( "fast", function() {});
|
||||
|
||||
//update wait screen
|
||||
$( "#loading" ).show();
|
||||
$( "#connect-success" ).hide();
|
||||
$( "#connect-fail" ).hide();
|
||||
});
|
||||
|
||||
$("#wifi-list").on("click", ".ape", function() {
|
||||
selectedSSID = $(this).text();
|
||||
$( "#ssid-pwd" ).text(selectedSSID);
|
||||
$( "#wifi" ).slideUp( "fast", function() {});
|
||||
$( "#connect_manual" ).slideUp( "fast", function() {});
|
||||
$( "#connect" ).slideDown( "fast", function() {});
|
||||
|
||||
//update wait screen
|
||||
$( "#loading" ).show();
|
||||
$( "#connect-success" ).hide();
|
||||
$( "#connect-fail" ).hide();
|
||||
});
|
||||
|
||||
$("#cancel").on("click", function() {
|
||||
selectedSSID = "";
|
||||
$( "#connect" ).slideUp( "fast", function() {});
|
||||
$( "#connect_manual" ).slideUp( "fast", function() {});
|
||||
$( "#wifi" ).slideDown( "fast", function() {});
|
||||
});
|
||||
|
||||
$("#manual_cancel").on("click", function() {
|
||||
selectedSSID = "";
|
||||
$( "#connect" ).slideUp( "fast", function() {});
|
||||
$( "#connect_manual" ).slideUp( "fast", function() {});
|
||||
$( "#wifi" ).slideDown( "fast", function() {});
|
||||
});
|
||||
|
||||
$("#join").on("click", function() {
|
||||
performConnect();
|
||||
});
|
||||
|
||||
$("#manual_join").on("click", function() {
|
||||
performConnect($(this).data('connect'));
|
||||
});
|
||||
|
||||
$("#ok-details").on("click", function() {
|
||||
$( "#connect-details" ).slideUp( "fast", function() {});
|
||||
$( "#wifi" ).slideDown( "fast", function() {});
|
||||
|
||||
});
|
||||
|
||||
$("#ok-credits").on("click", function() {
|
||||
$( "#credits" ).slideUp( "fast", function() {});
|
||||
$( "#app" ).slideDown( "fast", function() {});
|
||||
});
|
||||
|
||||
$("#acredits").on("click", function(event) {
|
||||
event.preventDefault();
|
||||
$( "#app" ).slideUp( "fast", function() {});
|
||||
$( "#credits" ).slideDown( "fast", function() {});
|
||||
});
|
||||
|
||||
$("#ok-connect").on("click", function() {
|
||||
$( "#connect-wait" ).slideUp( "fast", function() {});
|
||||
$( "#wifi" ).slideDown( "fast", function() {});
|
||||
});
|
||||
|
||||
$("#disconnect").on("click", function() {
|
||||
$( "#connect-details-wrap" ).addClass('blur');
|
||||
$( "#diag-disconnect" ).slideDown( "fast", function() {});
|
||||
});
|
||||
|
||||
$("#no-disconnect").on("click", function() {
|
||||
$( "#diag-disconnect" ).slideUp( "fast", function() {});
|
||||
$( "#connect-details-wrap" ).removeClass('blur');
|
||||
});
|
||||
|
||||
$("#yes-disconnect").on("click", function() {
|
||||
|
||||
stopCheckStatusInterval();
|
||||
selectedSSID = "";
|
||||
|
||||
$( "#diag-disconnect" ).slideUp( "fast", function() {});
|
||||
$( "#connect-details-wrap" ).removeClass('blur');
|
||||
|
||||
$.ajax({
|
||||
url: '/connect.json',
|
||||
dataType: 'json',
|
||||
method: 'DELETE',
|
||||
cache: false,
|
||||
data: { 'timestamp': Date.now()}
|
||||
});
|
||||
|
||||
startCheckStatusInterval();
|
||||
|
||||
$( "#connect-details" ).slideUp( "fast", function() {});
|
||||
$( "#wifi" ).slideDown( "fast", function() {})
|
||||
});
|
||||
|
||||
$("#update-command").click(function() {
|
||||
updateAutoexec();
|
||||
});
|
||||
|
||||
$("#generate-command").click(function() {
|
||||
generateCommand();
|
||||
});
|
||||
|
||||
$('[name=audio]').click(function(){
|
||||
selectOutput(this);
|
||||
});
|
||||
|
||||
//first time the page loads: attempt get the connection status and start the wifi scan
|
||||
refreshAP();
|
||||
startCheckStatusInterval();
|
||||
startRefreshAPInterval();
|
||||
getConfig();
|
||||
});
|
||||
|
||||
function performConnect(conntype){
|
||||
|
||||
//stop the status refresh. This prevents a race condition where a status
|
||||
//request would be refreshed with wrong ip info from a previous connection
|
||||
//and the request would automatically shows as succesful.
|
||||
stopCheckStatusInterval();
|
||||
|
||||
//stop refreshing wifi list
|
||||
stopRefreshAPInterval();
|
||||
|
||||
var pwd;
|
||||
if (conntype == 'manual') {
|
||||
//Grab the manual SSID and PWD
|
||||
selectedSSID=$('#manual_ssid').val();
|
||||
pwd = $("#manual_pwd").val();
|
||||
}else{
|
||||
pwd = $("#pwd").val();
|
||||
}
|
||||
//reset connection
|
||||
$( "#loading" ).show();
|
||||
$( "#connect-success" ).hide();
|
||||
$( "#connect-fail" ).hide();
|
||||
|
||||
$( "#ok-connect" ).prop("disabled",true);
|
||||
$( "#ssid-wait" ).text(selectedSSID);
|
||||
$( "#connect" ).slideUp( "fast", function() {});
|
||||
$( "#connect_manual" ).slideUp( "fast", function() {});
|
||||
$( "#connect-wait" ).slideDown( "fast", function() {});
|
||||
|
||||
|
||||
$.ajax({
|
||||
url: '/connect.json',
|
||||
dataType: 'json',
|
||||
method: 'POST',
|
||||
cache: false,
|
||||
headers: { 'X-Custom-ssid': selectedSSID, 'X-Custom-pwd': pwd },
|
||||
data: { 'timestamp': Date.now()}
|
||||
});
|
||||
|
||||
|
||||
//now we can re-set the intervals regardless of result
|
||||
startCheckStatusInterval();
|
||||
startRefreshAPInterval();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function rssiToIcon(rssi){
|
||||
if(rssi >= -60){
|
||||
return 'w0';
|
||||
}
|
||||
else if(rssi >= -67){
|
||||
return 'w1';
|
||||
}
|
||||
else if(rssi >= -75){
|
||||
return 'w2';
|
||||
}
|
||||
else{
|
||||
return 'w3';
|
||||
}
|
||||
}
|
||||
|
||||
function refreshAP(){
|
||||
$.getJSON( "/ap.json", function( data ) {
|
||||
if(data.length > 0){
|
||||
//sort by signal strength
|
||||
data.sort(function (a, b) {
|
||||
var x = a["rssi"]; var y = b["rssi"];
|
||||
return ((x < y) ? 1 : ((x > y) ? -1 : 0));
|
||||
});
|
||||
apList = data;
|
||||
refreshAPHTML(apList);
|
||||
|
||||
}
|
||||
});
|
||||
RepeatRefreshAPInterval();
|
||||
}
|
||||
|
||||
function refreshAPHTML(data){
|
||||
var h = "";
|
||||
data.forEach(function(e, idx, array) {
|
||||
h += '<div class="ape{0}"><div class="{1}"><div class="{2}">{3}</div></div></div>'.format(idx === array.length - 1?'':' brdb', rssiToIcon(e.rssi), e.auth==0?'':'pw',e.ssid);
|
||||
h += "\n";
|
||||
});
|
||||
|
||||
$( "#wifi-list" ).html(h)
|
||||
}
|
||||
|
||||
function checkStatus(){
|
||||
$.getJSON( "/status.json", function( data ) {
|
||||
if(data.hasOwnProperty('autoexec1') && data['autoexec1'] != ""){
|
||||
$("#autoexec1_current").text(data["autoexec1"]);
|
||||
}
|
||||
if(data.hasOwnProperty('ssid') && data['ssid'] != ""){
|
||||
if(data["ssid"] === selectedSSID){
|
||||
//that's a connection attempt
|
||||
if(data["urc"] === 0){
|
||||
//got connection
|
||||
$("#connected-to span").text(data["ssid"]);
|
||||
$("#connect-details h1").text(data["ssid"]);
|
||||
$("#ip").text(data["ip"]);
|
||||
$("#netmask").text(data["netmask"]);
|
||||
$("#gw").text(data["gw"]);
|
||||
$("#wifi-status").slideDown( "fast", function() {});
|
||||
|
||||
//unlock the wait screen if needed
|
||||
$( "#ok-connect" ).prop("disabled",false);
|
||||
|
||||
//update wait screen
|
||||
$( "#loading" ).hide();
|
||||
$( "#connect-success" ).show();
|
||||
$( "#connect-fail" ).hide();
|
||||
}
|
||||
else if(data["urc"] === 1){
|
||||
//failed attempt
|
||||
$("#connected-to span").text('');
|
||||
$("#connect-details h1").text('');
|
||||
$("#ip").text('0.0.0.0');
|
||||
$("#netmask").text('0.0.0.0');
|
||||
$("#gw").text('0.0.0.0');
|
||||
|
||||
//don't show any connection
|
||||
$("#wifi-status").slideUp( "fast", function() {});
|
||||
|
||||
//unlock the wait screen
|
||||
$( "#ok-connect" ).prop("disabled",false);
|
||||
|
||||
//update wait screen
|
||||
$( "#loading" ).hide();
|
||||
$( "#connect-fail" ).show();
|
||||
$( "#connect-success" ).hide();
|
||||
}
|
||||
}
|
||||
else if(data.hasOwnProperty('urc') && data['urc'] === 0){
|
||||
//ESP32 is already connected to a wifi without having the user do anything
|
||||
if( !($("#wifi-status").is(":visible")) ){
|
||||
$("#connected-to span").text(data["ssid"]);
|
||||
$("#connect-details h1").text(data["ssid"]);
|
||||
$("#ip").text(data["ip"]);
|
||||
$("#netmask").text(data["netmask"]);
|
||||
$("#gw").text(data["gw"]);
|
||||
$("#wifi-status").slideDown( "fast", function() {});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(data.hasOwnProperty('urc') && data['urc'] === 2){
|
||||
//that's a manual disconnect
|
||||
if($("#wifi-status").is(":visible")){
|
||||
$("#wifi-status").slideUp( "fast", function() {});
|
||||
}
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
//don't do anything, the server might be down while esp32 recalibrates radio
|
||||
});
|
||||
|
||||
RepeatCheckStatusInterval();
|
||||
}
|
||||
|
||||
function getConfig() {
|
||||
$.getJSON("/config.json", function(data) {
|
||||
if (data.hasOwnProperty('autoexec')) {
|
||||
if (data["autoexec"] === 1) {
|
||||
console.log('turn on autoexec');
|
||||
$("#autoexec-cb")[0].checked=true;
|
||||
} else {
|
||||
console.log('turn off autoexec');
|
||||
$("#autoexec-cb")[0].checked=false;
|
||||
$("#autoexec-command").hide(200);
|
||||
}
|
||||
}
|
||||
if (data.hasOwnProperty('list')) {
|
||||
data.list.forEach(function(line) {
|
||||
let key = Object.keys(line)[0];
|
||||
let val = Object.values(line)[0];
|
||||
console.log(key, val);
|
||||
if (key == 'autoexec1') {
|
||||
$("#autoexec1").val(val);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
console.log("failed to fetch config!");
|
||||
});
|
||||
}
|
||||
|
||||
function updateAutoexec(){
|
||||
autoexec = ($("#autoexec-cb")[0].checked)?1:0;
|
||||
autoexec1 = $("#autoexec1").val();
|
||||
|
||||
$.ajax({
|
||||
url: '/config.json',
|
||||
dataType: 'json',
|
||||
method: 'POST',
|
||||
cache: false,
|
||||
headers: { "X-Custom-autoexec": autoexec, "X-Custom-autoexec1": autoexec1 },
|
||||
data: { 'timestamp': Date.now() }
|
||||
});
|
||||
console.log('sent config JSON with headers:', autoexec, autoexec1);
|
||||
}
|
||||
|
||||
function performFactory(){
|
||||
// $( "#ok-connect" ).prop("disabled",true);
|
||||
// $( "#ssid-wait" ).text(selectedSSID);
|
||||
// $( "#connect" ).slideUp( "fast", function() {});
|
||||
// $( "#connect_manual" ).slideUp( "fast", function() {});
|
||||
// $( "#connect-wait" ).slideDown( "fast", function() {});
|
||||
// // todo: should we update the UI here?
|
||||
|
||||
$.ajax({
|
||||
url: '/factory.json',
|
||||
dataType: 'json',
|
||||
method: 'POST',
|
||||
cache: false,
|
||||
data: { 'timestamp': Date.now()}
|
||||
});
|
||||
}
|
||||
|
||||
var output = '';
|
||||
function selectOutput(el) {
|
||||
if ($(el).attr('id') == 'bt') {
|
||||
$("#btsinkdiv").show(200);
|
||||
output = 'bt';
|
||||
} else {
|
||||
$("#btsinkdiv").hide(200);
|
||||
output = 'i2s';
|
||||
}
|
||||
}
|
||||
|
||||
function generateCommand() {
|
||||
var commandLine = commandHeader + '-n ' + $("#player").val();
|
||||
|
||||
if (output == 'bt') {
|
||||
commandLine += ' -o "BT -n \'' + $("#btsink").val() + '\'" -R -u m -Z 192000 -r "44100-44100"';
|
||||
} else {
|
||||
commandLine += ' -o I2S';
|
||||
}
|
||||
if ($("#optional").val() != '') {
|
||||
commandLine += ' ' + $("#optional").val();
|
||||
}
|
||||
$("#autoexec1").val(commandLine);
|
||||
}
|
||||
11
components/wifi-manager/component.mk
Normal file
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# Component Makefile
|
||||
#
|
||||
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
|
||||
# this will take the sources in the src/ directory, compile them and link them into
|
||||
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
|
||||
# please read the SDK documents if you need to do this.
|
||||
#
|
||||
COMPONENT_EMBED_FILES := style.css jquery.gz code.js index.html
|
||||
CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
2
components/wifi-manager/compress.bat
Normal file
@@ -0,0 +1,2 @@
|
||||
gzip index.html style.css jquery.js --best --keep --force
|
||||
pause
|
||||
2
components/wifi-manager/connect
Normal file
@@ -0,0 +1,2 @@
|
||||
<html>
|
||||
</html>
|
||||
184
components/wifi-manager/dns_server.c
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
Copyright (c) 2019 Tony Pottier
|
||||
|
||||
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.
|
||||
|
||||
@file dns_server.c
|
||||
@author Tony Pottier
|
||||
@brief Defines an extremely basic DNS server for captive portal functionality.
|
||||
It's basically a DNS hijack that replies to the esp's address no matter which
|
||||
request is sent to it.
|
||||
|
||||
Contains the freeRTOS task for the DNS server that processes the requests.
|
||||
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
*/
|
||||
|
||||
#include "dns_server.h"
|
||||
|
||||
#include <lwip/sockets.h>
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_event_loop.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_err.h>
|
||||
#include <nvs_flash.h>
|
||||
|
||||
#include <lwip/err.h>
|
||||
#include <lwip/sockets.h>
|
||||
#include <lwip/sys.h>
|
||||
#include <lwip/netdb.h>
|
||||
#include <lwip/dns.h>
|
||||
|
||||
#include <byteswap.h>
|
||||
|
||||
#include "wifi_manager.h"
|
||||
|
||||
static const char TAG[] = "dns_server";
|
||||
static TaskHandle_t task_dns_server = NULL;
|
||||
int socket_fd;
|
||||
|
||||
void dns_server_start() {
|
||||
xTaskCreate(&dns_server, "dns_server", 3072, NULL, WIFI_MANAGER_TASK_PRIORITY-1, &task_dns_server);
|
||||
}
|
||||
|
||||
void dns_server_stop(){
|
||||
if(task_dns_server){
|
||||
vTaskDelete(task_dns_server);
|
||||
close(socket_fd);
|
||||
task_dns_server = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dns_server(void *pvParameters) {
|
||||
|
||||
|
||||
|
||||
struct sockaddr_in sa, ra;
|
||||
|
||||
/* Set redirection DNS hijack to the access point IP */
|
||||
ip4_addr_t ip_resolved;
|
||||
inet_pton(AF_INET, DEFAULT_AP_IP, &ip_resolved);
|
||||
|
||||
|
||||
/* Create UDP socket */
|
||||
socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (socket_fd < 0){
|
||||
ESP_LOGE(TAG, "Failed to create socket");
|
||||
exit(0);
|
||||
}
|
||||
memset(&sa, 0, sizeof(struct sockaddr_in));
|
||||
|
||||
/* Bind to port 53 (typical DNS Server port) */
|
||||
tcpip_adapter_ip_info_t ip;
|
||||
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip);
|
||||
ra.sin_family = AF_INET;
|
||||
ra.sin_addr.s_addr = ip.ip.addr;
|
||||
ra.sin_port = htons(53);
|
||||
if (bind(socket_fd, (struct sockaddr *)&ra, sizeof(struct sockaddr_in)) == -1) {
|
||||
ESP_LOGE(TAG, "Failed to bind to 53/udp");
|
||||
close(socket_fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct sockaddr_in client;
|
||||
socklen_t client_len;
|
||||
client_len = sizeof(client);
|
||||
int length;
|
||||
uint8_t data[DNS_QUERY_MAX_SIZE]; /* dns query buffer */
|
||||
uint8_t response[DNS_ANSWER_MAX_SIZE]; /* dns response buffer */
|
||||
char ip_address[INET_ADDRSTRLEN]; /* buffer to store IPs as text. This is only used for debug and serves no other purpose */
|
||||
char *domain; /* This is only used for debug and serves no other purpose */
|
||||
int err;
|
||||
|
||||
ESP_LOGI(TAG, "DNS Server listening on 53/udp");
|
||||
|
||||
/* Start loop to process DNS requests */
|
||||
for(;;) {
|
||||
|
||||
memset(data, 0x00, sizeof(data)); /* reset buffer */
|
||||
length = recvfrom(socket_fd, data, sizeof(data), 0, (struct sockaddr *)&client, &client_len); /* read udp request */
|
||||
|
||||
/*if the query is bigger than the buffer size we simply ignore it. This case should only happen in case of multiple
|
||||
* queries within the same DNS packet and is not supported by this simple DNS hijack. */
|
||||
if ( length > 0 && ((length + sizeof(dns_answer_t)-1) < DNS_ANSWER_MAX_SIZE) ) {
|
||||
|
||||
data[length] = '\0'; /*in case there's a bogus domain name that isn't null terminated */
|
||||
|
||||
/* Generate header message */
|
||||
memcpy(response, data, sizeof(dns_header_t));
|
||||
dns_header_t *dns_header = (dns_header_t*)response;
|
||||
dns_header->QR = 1; /*response bit */
|
||||
dns_header->OPCode = DNS_OPCODE_QUERY; /* no support for other type of response */
|
||||
dns_header->AA = 1; /*authoritative answer */
|
||||
dns_header->RCode = DNS_REPLY_CODE_NO_ERROR; /* no error */
|
||||
dns_header->TC = 0; /*no truncation */
|
||||
dns_header->RD = 0; /*no recursion */
|
||||
dns_header->ANCount = dns_header->QDCount; /* set answer count = question count -- duhh! */
|
||||
dns_header->NSCount = 0x0000; /* name server resource records = 0 */
|
||||
dns_header->ARCount = 0x0000; /* resource records = 0 */
|
||||
|
||||
|
||||
/* copy the rest of the query in the response */
|
||||
memcpy(response + sizeof(dns_header_t), data + sizeof(dns_header_t), length - sizeof(dns_header_t));
|
||||
|
||||
|
||||
/* extract domain name and request IP for debug */
|
||||
inet_ntop(AF_INET, &(client.sin_addr), ip_address, INET_ADDRSTRLEN);
|
||||
domain = (char*) &data[sizeof(dns_header_t) + 1];
|
||||
for(char* c=domain; *c != '\0'; c++){
|
||||
if(*c < ' ' || *c > 'z') *c = '.'; /* technically we should test if the first two bits are 00 (e.g. if( (*c & 0xC0) == 0x00) *c = '.') but this makes the code a lot more readable */
|
||||
}
|
||||
ESP_LOGI(TAG, "Replying to DNS request for %s from %s", domain, ip_address);
|
||||
|
||||
|
||||
/* create DNS answer at the end of the query*/
|
||||
dns_answer_t *dns_answer = (dns_answer_t*)&response[length];
|
||||
dns_answer->NAME = __bswap_16(0xC00C); /* This is a pointer to the beginning of the question. As per DNS standard, first two bits must be set to 11 for some odd reason hence 0xC0 */
|
||||
dns_answer->TYPE = __bswap_16(DNS_ANSWER_TYPE_A);
|
||||
dns_answer->CLASS = __bswap_16(DNS_ANSWER_CLASS_IN);
|
||||
dns_answer->TTL = (uint32_t)0x00000000; /* no caching. Avoids DNS poisoning since this is a DNS hijack */
|
||||
dns_answer->RDLENGTH = __bswap_16(0x0004); /* 4 byte => size of an ipv4 address */
|
||||
dns_answer->RDATA = ip_resolved.addr;
|
||||
|
||||
err = sendto(socket_fd, response, length+sizeof(dns_answer_t), 0, (struct sockaddr *)&client, client_len);
|
||||
if (err < 0) {
|
||||
ESP_LOGE(TAG, "UDP sendto failed: %d", err);
|
||||
}
|
||||
}
|
||||
|
||||
taskYIELD(); /* allows the freeRTOS scheduler to take over if needed. DNS daemon should not be taxing on the system */
|
||||
|
||||
}
|
||||
close(socket_fd);
|
||||
|
||||
vTaskDelete ( NULL );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
140
components/wifi-manager/dns_server.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
Copyright (c) 2019 Tony Pottier
|
||||
|
||||
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.
|
||||
|
||||
@file dns_server.h
|
||||
@author Tony Pottier
|
||||
@brief Defines an extremly basic DNS server for captive portal functionality.
|
||||
|
||||
Contains the freeRTOS task for the DNS server that processes the requests.
|
||||
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
@see http://www.zytrax.com/books/dns/ch15
|
||||
*/
|
||||
|
||||
#ifndef MAIN_DNS_SERVER_H_
|
||||
#define MAIN_DNS_SERVER_H_
|
||||
#include <esp_system.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/** 12 byte header, 64 byte domain name, 4 byte qtype/qclass. This NOT compliant with the RFC, but it's good enough for a captive portal
|
||||
* if a DNS query is too big it just wont be processed. */
|
||||
#define DNS_QUERY_MAX_SIZE 80
|
||||
|
||||
/** Query + 2 byte ptr, 2 byte type, 2 byte class, 4 byte TTL, 2 byte len, 4 byte data */
|
||||
#define DNS_ANSWER_MAX_SIZE (DNS_QUERY_MAX_SIZE+16)
|
||||
|
||||
|
||||
/**
|
||||
* @brief RCODE values used in a DNS header message
|
||||
*/
|
||||
typedef enum dns_reply_code_t {
|
||||
DNS_REPLY_CODE_NO_ERROR = 0,
|
||||
DNS_REPLY_CODE_FORM_ERROR = 1,
|
||||
DNS_REPLY_CODE_SERVER_FAILURE = 2,
|
||||
DNS_REPLY_CODE_NON_EXISTANT_DOMAIN = 3,
|
||||
DNS_REPLY_CODE_NOT_IMPLEMENTED = 4,
|
||||
DNS_REPLY_CODE_REFUSED = 5,
|
||||
DNS_REPLY_CODE_YXDOMAIN = 6,
|
||||
DNS_REPLY_CODE_YXRRSET = 7,
|
||||
DNS_REPLY_CODE_NXRRSET = 8
|
||||
}dns_reply_code_t;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief OPCODE values used in a DNS header message
|
||||
*/
|
||||
typedef enum dns_opcode_code_t {
|
||||
DNS_OPCODE_QUERY = 0,
|
||||
DNS_OPCODE_IQUERY = 1,
|
||||
DNS_OPCODE_STATUS = 2
|
||||
}dns_opcode_code_t;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Represents a 12 byte DNS header.
|
||||
* __packed__ is needed to prevent potential unwanted memory alignments
|
||||
*/
|
||||
typedef struct __attribute__((__packed__)) dns_header_t{
|
||||
uint16_t ID; // identification number
|
||||
uint8_t RD : 1; // recursion desired
|
||||
uint8_t TC : 1; // truncated message
|
||||
uint8_t AA : 1; // authoritive answer
|
||||
uint8_t OPCode : 4; // message_type
|
||||
uint8_t QR : 1; // query/response flag
|
||||
uint8_t RCode : 4; // response code
|
||||
uint8_t Z : 3; // its z! reserved
|
||||
uint8_t RA : 1; // recursion available
|
||||
uint16_t QDCount; // number of question entries
|
||||
uint16_t ANCount; // number of answer entries
|
||||
uint16_t NSCount; // number of authority entries
|
||||
uint16_t ARCount; // number of resource entries
|
||||
}dns_header_t;
|
||||
|
||||
|
||||
|
||||
typedef enum dns_answer_type_t {
|
||||
DNS_ANSWER_TYPE_A = 1,
|
||||
DNS_ANSWER_TYPE_NS = 2,
|
||||
DNS_ANSWER_TYPE_CNAME = 5,
|
||||
DNS_ANSWER_TYPE_SOA = 6,
|
||||
DNS_ANSWER_TYPE_WKS = 11,
|
||||
DNS_ANSWER_TYPE_PTR = 12,
|
||||
DNS_ANSWER_TYPE_MX = 15,
|
||||
DNS_ANSWER_TYPE_SRV = 33,
|
||||
DNS_ANSWER_TYPE_AAAA = 28
|
||||
}dns_answer_type_t;
|
||||
|
||||
typedef enum dns_answer_class_t {
|
||||
DNS_ANSWER_CLASS_IN = 1
|
||||
}dns_answer_class_t;
|
||||
|
||||
|
||||
|
||||
typedef struct __attribute__((__packed__)) dns_answer_t{
|
||||
uint16_t NAME; /* for the sake of simplicity only 16 bit pointers are supported */
|
||||
uint16_t TYPE; /* Unsigned 16 bit value. The resource record types - determines the content of the RDATA field. */
|
||||
uint16_t CLASS; /* Class of response. */
|
||||
uint32_t TTL; /* The time in seconds that the record may be cached. A value of 0 indicates the record should not be cached. */
|
||||
uint16_t RDLENGTH; /* Unsigned 16-bit value that defines the length in bytes of the RDATA record. */
|
||||
uint32_t RDATA; /* For the sake of simplicity only ipv4 is supported, and as such it's a unsigned 32 bit */
|
||||
}dns_answer_t;
|
||||
|
||||
void dns_server(void *pvParameters);
|
||||
void dns_server_start();
|
||||
void dns_server_stop();
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* MAIN_DNS_SERVER_H_ */
|
||||
399
components/wifi-manager/http_server.c
Normal file
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
Copyright (c) 2017-2019 Tony Pottier
|
||||
|
||||
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.
|
||||
|
||||
@file http_server.c
|
||||
@author Tony Pottier
|
||||
@brief Defines all functions necessary for the HTTP server to run.
|
||||
|
||||
Contains the freeRTOS task for the HTTP listener and all necessary support
|
||||
function to process requests, decode URLs, serve files, etc. etc.
|
||||
|
||||
@note http_server task cannot run without the wifi_manager task!
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
*/
|
||||
|
||||
#include "http_server.h"
|
||||
#include "cmd_system.h"
|
||||
|
||||
|
||||
/* @brief tag used for ESP serial console messages */
|
||||
static const char TAG[] = "http_server";
|
||||
static const char json_start[] = "{ \"autoexec\": %u, \"list\": [";
|
||||
static const char json_end[] = "]}";
|
||||
static const char template[] = "{ \"%s\": \"%s\" }";
|
||||
static const char array_separator[]=",";
|
||||
|
||||
/* @brief task handle for the http server */
|
||||
static TaskHandle_t task_http_server = NULL;
|
||||
|
||||
|
||||
/**
|
||||
* @brief embedded binary data.
|
||||
* @see file "component.mk"
|
||||
* @see https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html#embedding-binary-data
|
||||
*/
|
||||
extern const uint8_t style_css_start[] asm("_binary_style_css_start");
|
||||
extern const uint8_t style_css_end[] asm("_binary_style_css_end");
|
||||
extern const uint8_t jquery_gz_start[] asm("_binary_jquery_gz_start");
|
||||
extern const uint8_t jquery_gz_end[] asm("_binary_jquery_gz_end");
|
||||
extern const uint8_t code_js_start[] asm("_binary_code_js_start");
|
||||
extern const uint8_t code_js_end[] asm("_binary_code_js_end");
|
||||
extern const uint8_t index_html_start[] asm("_binary_index_html_start");
|
||||
extern const uint8_t index_html_end[] asm("_binary_index_html_end");
|
||||
|
||||
|
||||
/* const http headers stored in ROM */
|
||||
const static char http_html_hdr[] = "HTTP/1.1 200 OK\nContent-type: text/html\n\n";
|
||||
const static char http_css_hdr[] = "HTTP/1.1 200 OK\nContent-type: text/css\nCache-Control: public, max-age=31536000\n\n";
|
||||
const static char http_js_hdr[] = "HTTP/1.1 200 OK\nContent-type: text/javascript\n\n";
|
||||
const static char http_jquery_gz_hdr[] = "HTTP/1.1 200 OK\nContent-type: text/javascript\nAccept-Ranges: bytes\nContent-Length: 29995\nContent-Encoding: gzip\n\n";
|
||||
const static char http_400_hdr[] = "HTTP/1.1 400 Bad Request\nContent-Length: 0\n\n";
|
||||
const static char http_404_hdr[] = "HTTP/1.1 404 Not Found\nContent-Length: 0\n\n";
|
||||
const static char http_503_hdr[] = "HTTP/1.1 503 Service Unavailable\nContent-Length: 0\n\n";
|
||||
const static char http_ok_json_no_cache_hdr[] = "HTTP/1.1 200 OK\nContent-type: application/json\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\nPragma: no-cache\n\n";
|
||||
const static char http_redirect_hdr_start[] = "HTTP/1.1 302 Found\nLocation: http://";
|
||||
const static char http_redirect_hdr_end[] = "/\n\n";
|
||||
|
||||
|
||||
|
||||
void http_server_start(){
|
||||
if(task_http_server == NULL){
|
||||
xTaskCreate(&http_server, "http_server", 1024*3, NULL, WIFI_MANAGER_TASK_PRIORITY-1, &task_http_server);
|
||||
}
|
||||
}
|
||||
|
||||
void http_server(void *pvParameters) {
|
||||
|
||||
struct netconn *conn, *newconn;
|
||||
err_t err;
|
||||
conn = netconn_new(NETCONN_TCP);
|
||||
netconn_bind(conn, IP_ADDR_ANY, 80);
|
||||
netconn_listen(conn);
|
||||
ESP_LOGI(TAG, "HTTP Server listening on 80/tcp");
|
||||
do {
|
||||
err = netconn_accept(conn, &newconn);
|
||||
if (err == ERR_OK) {
|
||||
http_server_netconn_serve(newconn);
|
||||
netconn_delete(newconn);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG,"Error accepting new connection. Terminating HTTP server");
|
||||
}
|
||||
taskYIELD(); /* allows the freeRTOS scheduler to take over if needed. */
|
||||
} while(err == ERR_OK);
|
||||
|
||||
netconn_close(conn);
|
||||
netconn_delete(conn);
|
||||
|
||||
vTaskDelete( NULL );
|
||||
}
|
||||
|
||||
|
||||
char* http_server_get_header(char *request, char *header_name, int *len) {
|
||||
*len = 0;
|
||||
char *ret = NULL;
|
||||
char *ptr = NULL;
|
||||
|
||||
ptr = strstr(request, header_name);
|
||||
if (ptr) {
|
||||
ret = ptr + strlen(header_name);
|
||||
ptr = ret;
|
||||
while (*ptr != '\0' && *ptr != '\n' && *ptr != '\r') {
|
||||
(*len)++;
|
||||
ptr++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void http_server_netconn_serve(struct netconn *conn) {
|
||||
|
||||
struct netbuf *inbuf;
|
||||
char *buf = NULL;
|
||||
u16_t buflen;
|
||||
err_t err;
|
||||
const char new_line[2] = "\n";
|
||||
|
||||
err = netconn_recv(conn, &inbuf);
|
||||
if (err == ERR_OK) {
|
||||
|
||||
netbuf_data(inbuf, (void**)&buf, &buflen);
|
||||
|
||||
/* extract the first line of the request */
|
||||
char *save_ptr = buf;
|
||||
char *line = strtok_r(save_ptr, new_line, &save_ptr);
|
||||
ESP_LOGD(TAG,"Processing line %s",line);
|
||||
|
||||
if(line) {
|
||||
|
||||
/* captive portal functionality: redirect to access point IP for HOST that are not the access point IP OR the STA IP */
|
||||
int lenH = 0;
|
||||
char *host = http_server_get_header(save_ptr, "Host: ", &lenH);
|
||||
/* determine if Host is from the STA IP address */
|
||||
wifi_manager_lock_sta_ip_string(portMAX_DELAY);
|
||||
bool access_from_sta_ip = lenH > 0?strstr(host, wifi_manager_get_sta_ip_string()):false;
|
||||
wifi_manager_unlock_sta_ip_string();
|
||||
|
||||
if (lenH > 0 && !strstr(host, DEFAULT_AP_IP) && !access_from_sta_ip) {
|
||||
ESP_LOGI(TAG,"Redirecting to default AP IP Address : %s", DEFAULT_AP_IP);
|
||||
netconn_write(conn, http_redirect_hdr_start, sizeof(http_redirect_hdr_start) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, DEFAULT_AP_IP, sizeof(DEFAULT_AP_IP) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, http_redirect_hdr_end, sizeof(http_redirect_hdr_end) - 1, NETCONN_NOCOPY);
|
||||
|
||||
}
|
||||
else{
|
||||
/* default page */
|
||||
if(strstr(line, "GET / ")) {
|
||||
netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, index_html_start, index_html_end - index_html_start, NETCONN_NOCOPY);
|
||||
}
|
||||
else if(strstr(line, "GET /jquery.js ")) {
|
||||
netconn_write(conn, http_jquery_gz_hdr, sizeof(http_jquery_gz_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, jquery_gz_start, jquery_gz_end - jquery_gz_start, NETCONN_NOCOPY);
|
||||
}
|
||||
else if(strstr(line, "GET /code.js ")) {
|
||||
netconn_write(conn, http_js_hdr, sizeof(http_js_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, code_js_start, code_js_end - code_js_start, NETCONN_NOCOPY);
|
||||
}
|
||||
else if(strstr(line, "GET /ap.json ")) {
|
||||
/* if we can get the mutex, write the last version of the AP list */
|
||||
ESP_LOGI(TAG,"Processing ap.json request");
|
||||
if(wifi_manager_lock_json_buffer(( TickType_t ) 10)){
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY);
|
||||
char *buff = wifi_manager_get_ap_list_json();
|
||||
netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY);
|
||||
wifi_manager_unlock_json_buffer();
|
||||
}
|
||||
else{
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "http_server_netconn_serve: GET /ap.json failed to obtain mutex");
|
||||
}
|
||||
/* request a wifi scan */
|
||||
ESP_LOGI(TAG,"Starting wifi scan");
|
||||
wifi_manager_scan_async();
|
||||
}
|
||||
else if(strstr(line, "GET /style.css ")) {
|
||||
netconn_write(conn, http_css_hdr, sizeof(http_css_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, style_css_start, style_css_end - style_css_start, NETCONN_NOCOPY);
|
||||
}
|
||||
else if(strstr(line, "GET /status.json ")){
|
||||
ESP_LOGI(TAG,"Serving status.json");
|
||||
if(wifi_manager_lock_json_buffer(( TickType_t ) 10)){
|
||||
char *buff = wifi_manager_get_ip_info_json();
|
||||
if(buff){
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY);
|
||||
|
||||
wifi_manager_unlock_json_buffer();
|
||||
}
|
||||
else{
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
}
|
||||
}
|
||||
else{
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "http_server_netconn_serve: GET /status failed to obtain mutex");
|
||||
}
|
||||
}
|
||||
else if(strstr(line, "GET /config.json ")){
|
||||
ESP_LOGI(TAG,"Serving config.json");
|
||||
char autoexec_name[21]={0};
|
||||
char * autoexec_value=NULL;
|
||||
uint8_t autoexec_flag=0;
|
||||
int buflen=MAX_COMMAND_LINE_SIZE+strlen(template)+1;
|
||||
char * buff = malloc(buflen);
|
||||
char *s = "\"";
|
||||
char *r = "\\\"";
|
||||
if(!buff)
|
||||
{
|
||||
ESP_LOGE(TAG,"Unable to allocate buffer for config.json!");
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i=1;
|
||||
size_t l = 0;
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY);
|
||||
|
||||
autoexec_flag = wifi_manager_get_flag();
|
||||
snprintf(buff,buflen-1, json_start, autoexec_flag);
|
||||
netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY);
|
||||
do {
|
||||
snprintf(autoexec_name,sizeof(autoexec_name)-1,"autoexec%u",i);
|
||||
ESP_LOGD(TAG,"Getting command name %s", autoexec_name);
|
||||
autoexec_value = wifi_manager_alloc_get_config(autoexec_name, &l);
|
||||
if(autoexec_value!=NULL ){
|
||||
if(i>1)
|
||||
{
|
||||
netconn_write(conn, array_separator, strlen(array_separator), NETCONN_NOCOPY);
|
||||
ESP_LOGD(TAG,"%s", array_separator);
|
||||
}
|
||||
ESP_LOGI(TAG,"found command %s = %s", autoexec_name, autoexec_value);
|
||||
strreplace(autoexec_value, s, r);
|
||||
snprintf(buff, buflen-1, template, autoexec_name, autoexec_value);
|
||||
netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY);
|
||||
ESP_LOGD(TAG,"%s", buff);
|
||||
ESP_LOGD(TAG,"Freeing memory for command %s name", autoexec_name);
|
||||
free(autoexec_value);
|
||||
}
|
||||
else {
|
||||
ESP_LOGD(TAG,"No matching command found for name %s", autoexec_name);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
} while(1);
|
||||
free(buff);
|
||||
netconn_write(conn, json_end, strlen(json_end), NETCONN_NOCOPY);
|
||||
ESP_LOGD(TAG,"%s", json_end);
|
||||
}
|
||||
}
|
||||
else if(strstr(line, "POST /factory.json ")){
|
||||
guided_factory();
|
||||
}
|
||||
else if(strstr(line, "POST /config.json ")){
|
||||
ESP_LOGI(TAG,"Serving POST config.json");
|
||||
|
||||
if(wifi_manager_lock_json_buffer(( TickType_t ) 10)){
|
||||
int i=1;
|
||||
int lenS = 0, lenA=0;
|
||||
char autoexec_name[22]={0};
|
||||
char autoexec_key[12]={0};
|
||||
char * autoexec_value=NULL;
|
||||
char * autoexec_flag_s=NULL;
|
||||
uint8_t autoexec_flag=0;
|
||||
autoexec_flag_s = http_server_get_header(save_ptr, "X-Custom-autoexec: ", &lenA);
|
||||
if(autoexec_flag_s!=NULL && lenA > 0)
|
||||
{
|
||||
autoexec_flag = atoi(autoexec_flag_s);
|
||||
wifi_manager_save_autoexec_flag(autoexec_flag);
|
||||
}
|
||||
|
||||
do {
|
||||
if(snprintf(autoexec_name,sizeof(autoexec_name)-1,"X-Custom-autoexec%u: ",i)<0)
|
||||
{
|
||||
ESP_LOGE(TAG,"Unable to process autoexec%u. Name length overflow.",i);
|
||||
break;
|
||||
}
|
||||
if(snprintf(autoexec_key,sizeof(autoexec_key)-1,"autoexec%u",i++)<0)
|
||||
{
|
||||
ESP_LOGE(TAG,"Unable to process autoexec%u. Name length overflow.",i);
|
||||
break;
|
||||
}
|
||||
ESP_LOGD(TAG,"Looking for command name %s.", autoexec_name);
|
||||
autoexec_value = http_server_get_header(save_ptr, autoexec_name, &lenS);
|
||||
|
||||
|
||||
if(autoexec_value ){
|
||||
if(lenS < MAX_COMMAND_LINE_SIZE ){
|
||||
ESP_LOGD(TAG, "http_server_netconn_serve: config.json/ call, with %s: %s, length %i", autoexec_key, autoexec_value, lenS);
|
||||
wifi_manager_save_autoexec_config(autoexec_value,autoexec_key,lenS);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG,"command line length is too long : %s = %s", autoexec_name, autoexec_value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ESP_LOGD(TAG,"No matching command found for name %s", autoexec_name);
|
||||
break;
|
||||
}
|
||||
} while(1);
|
||||
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); //200ok
|
||||
|
||||
}
|
||||
else{
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "http_server_netconn_serve: GET /status failed to obtain mutex");
|
||||
}
|
||||
}
|
||||
|
||||
else if(strstr(line, "DELETE /connect.json ")) {
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: DELETE /connect.json");
|
||||
/* request a disconnection from wifi and forget about it */
|
||||
wifi_manager_disconnect_async();
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); /* 200 ok */
|
||||
}
|
||||
else if(strstr(line, "POST /connect.json ")) {
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: POST /connect.json");
|
||||
bool found = false;
|
||||
int lenS = 0, lenP = 0;
|
||||
char *ssid = NULL, *password = NULL;
|
||||
ssid = http_server_get_header(save_ptr, "X-Custom-ssid: ", &lenS);
|
||||
password = http_server_get_header(save_ptr, "X-Custom-pwd: ", &lenP);
|
||||
|
||||
if(ssid && lenS <= MAX_SSID_SIZE && password && lenP <= MAX_PASSWORD_SIZE){
|
||||
wifi_config_t* config = wifi_manager_get_wifi_sta_config();
|
||||
memset(config, 0x00, sizeof(wifi_config_t));
|
||||
memcpy(config->sta.ssid, ssid, lenS);
|
||||
memcpy(config->sta.password, password, lenP);
|
||||
ESP_LOGD(TAG, "http_server_netconn_serve: wifi_manager_connect_async() call, with ssid: %s, password: %s", ssid, password);
|
||||
wifi_manager_connect_async();
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); //200ok
|
||||
found = true;
|
||||
}
|
||||
|
||||
if(!found){
|
||||
/* bad request the authentification header is not complete/not the correct format */
|
||||
netconn_write(conn, http_400_hdr, sizeof(http_400_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "bad request the authentification header is not complete/not the correct format");
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
netconn_write(conn, http_400_hdr, sizeof(http_400_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "bad request");
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
ESP_LOGE(TAG, "URL Not found. Sending 404.");
|
||||
netconn_write(conn, http_404_hdr, sizeof(http_404_hdr) - 1, NETCONN_NOCOPY);
|
||||
}
|
||||
}
|
||||
|
||||
/* free the buffer */
|
||||
netbuf_delete(inbuf);
|
||||
}
|
||||
|
||||
void strreplace(char *src, char *str, char *rep)
|
||||
{
|
||||
char *p = strstr(src, str);
|
||||
if (p)
|
||||
{
|
||||
int len = strlen(src)+strlen(rep)-strlen(str);
|
||||
char r[len];
|
||||
memset(r, 0, len);
|
||||
if ( p >= src ){
|
||||
strncpy(r, src, p-src);
|
||||
r[p-src]='\0';
|
||||
strncat(r, rep, strlen(rep));
|
||||
strncat(r, p+strlen(str), p+strlen(str)-src+strlen(src));
|
||||
strcpy(src, r);
|
||||
strreplace(p+strlen(rep), str, rep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
98
components/wifi-manager/http_server.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright (c) 2017-2019 Tony Pottier
|
||||
|
||||
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.
|
||||
|
||||
@file http_server.h
|
||||
@author Tony Pottier
|
||||
@brief Defines all functions necessary for the HTTP server to run.
|
||||
|
||||
Contains the freeRTOS task for the HTTP listener and all necessary support
|
||||
function to process requests, decode URLs, serve files, etc. etc.
|
||||
|
||||
@note http_server task cannot run without the wifi_manager task!
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
*/
|
||||
|
||||
#ifndef HTTP_SERVER_H_INCLUDED
|
||||
#define HTTP_SERVER_H_INCLUDED
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "wifi_manager.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event_loop.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "mdns.h"
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/raw.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/priv/api_msg.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief RTOS task for the HTTP server. Do not start manually.
|
||||
* @see void http_server_start()
|
||||
*/
|
||||
void http_server(void *pvParameters);
|
||||
|
||||
/* @brief helper function that processes one HTTP request at a time */
|
||||
void http_server_netconn_serve(struct netconn *conn);
|
||||
|
||||
/* @brief create the task for the http server */
|
||||
void http_server_start();
|
||||
|
||||
/**
|
||||
* @brief gets a char* pointer to the first occurence of header_name withing the complete http request request.
|
||||
*
|
||||
* For optimization purposes, no local copy is made. memcpy can then be used in coordination with len to extract the
|
||||
* data.
|
||||
*
|
||||
* @param request the full HTTP raw request.
|
||||
* @param header_name the header that is being searched.
|
||||
* @param len the size of the header value if found.
|
||||
* @return pointer to the beginning of the header value.
|
||||
*/
|
||||
char* http_server_get_header(char *request, char *header_name, int *len);
|
||||
|
||||
void strreplace(char *src, char *str, char *rep);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
351
components/wifi-manager/index.html
Normal file
@@ -0,0 +1,351 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<script src="/jquery.js"></script>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<script src="/code.js"></script>
|
||||
<title>esp32-wifi-manager</title>
|
||||
</head>
|
||||
|
||||
<script>
|
||||
var ws, sel, host, old, once = 0, jso, m;
|
||||
var to = 0, set_int = 0;
|
||||
|
||||
|
||||
|
||||
function get_radio(name)
|
||||
{
|
||||
var s = document.getElementsByName(name), sel;
|
||||
for ( var i = 0; i < s.length; i++)
|
||||
if (s[i].checked) {
|
||||
sel = s[i].value;
|
||||
break;
|
||||
}
|
||||
|
||||
return sel;
|
||||
}
|
||||
|
||||
function get_radio_index(name)
|
||||
{
|
||||
var s = document.getElementsByName(name), i;
|
||||
|
||||
for (i = 0; i < s.length; i++)
|
||||
if (s[i].checked)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
function do_reset()
|
||||
{
|
||||
var s = "{\"reset\":\"1\"}";
|
||||
try {
|
||||
ws.send(s);
|
||||
} catch(exception) {
|
||||
alert('Sorry, there was a problem' + exception);
|
||||
}
|
||||
|
||||
ws.close();
|
||||
alert("Rebooting...");
|
||||
}
|
||||
|
||||
function file_change()
|
||||
{
|
||||
document.getElementById('update').disabled = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function do_upload(f)
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
document.getElementById('update').disabled = 1;
|
||||
//ws.close();
|
||||
document.getElementById("progr").class = "progr-ok";
|
||||
|
||||
xhr.upload.addEventListener("progress", function(e) {
|
||||
document.getElementById("progr").value = parseInt(e.loaded / e.total * 100);
|
||||
|
||||
if (e.loaded == e.total) {
|
||||
// document.getElementById("realpage").style.display = "none";
|
||||
// document.getElementById("waiting").style.display = "block";
|
||||
}
|
||||
|
||||
}, false);
|
||||
|
||||
xhr.onreadystatechange = function(e) {
|
||||
console.log("rs" + xhr.readyState + " status " + xhr.status);
|
||||
if (xhr.readyState == 4) {
|
||||
/* it completed, for good or for ill */
|
||||
// document.getElementById("realpage").style.display = "none";
|
||||
// document.getElementById("waiting").style.display = "block";
|
||||
document.getElementById("progr").class = "progr-ok";
|
||||
console.log("upload reached state 4: xhr status " + xhr.status);
|
||||
setTimeout(function() { window.location.href = location.origin + "/"; }, 9000 );
|
||||
}
|
||||
};
|
||||
|
||||
/* kill the heart timer */
|
||||
clearInterval(set_int);
|
||||
|
||||
xhr.open("POST", f.action, true);
|
||||
xhr.send(new FormData(f));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function do_settings(f)
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.onreadystatechange = function(e) {
|
||||
console.log("do_settings" + xhr.readyState + " status " + xhr.status);
|
||||
if (xhr.readyState == 4) {
|
||||
document.getElementById("updsettings").style.opacity = "1.0";
|
||||
document.getElementById("updsettings").disabled = 0;
|
||||
}
|
||||
};
|
||||
|
||||
xhr.open("POST", f.action, true);
|
||||
document.getElementById("updsettings").style.opacity = "0.3";
|
||||
document.getElementById("updsettings").disabled = 1;
|
||||
xhr.send(new FormData(f));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function get_latest(n)
|
||||
{
|
||||
if (n == 0)
|
||||
ws.send("update-ota");
|
||||
else
|
||||
ws.send("update-factory");
|
||||
}
|
||||
|
||||
|
||||
function heart_timer() {
|
||||
var s;
|
||||
|
||||
s = Math.round((95 * to) / (40 * 10)) / 100;
|
||||
|
||||
if (s < 0) {
|
||||
clearInterval(set_int);
|
||||
set_int = 0;
|
||||
|
||||
ws.close();
|
||||
|
||||
document.getElementById("realpage").style.opacity = "0.3";
|
||||
}
|
||||
|
||||
|
||||
to--;
|
||||
document.getElementById("heart").style.opacity = s;
|
||||
}
|
||||
|
||||
|
||||
function heartbeat()
|
||||
{
|
||||
to = 40 * 10;
|
||||
if (!set_int) {
|
||||
set_int = setInterval(heart_timer, 100);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function handleClick(cb) {
|
||||
if (cb.checked) {
|
||||
$("#autoexec-command").show(200);
|
||||
} else {
|
||||
$("#autoexec-command").hide(200);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<div id="app-wrap">
|
||||
<div id="command_line">
|
||||
<header>
|
||||
<h1>Startup command</h1>
|
||||
</header>
|
||||
<h2>
|
||||
<div id="autoexec" class="toggle">
|
||||
<label>Run automatically at boot
|
||||
<input id="autoexec-cb" type="checkbox" checked="checked" onclick='handleClick(this);'/><span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</h2>
|
||||
|
||||
<div id="autoexec-command">
|
||||
<div id="audioout" class="toggle-buttons">
|
||||
<h2>Audio output</h2>
|
||||
<input type="radio" id="i2s" name="audio" />
|
||||
<label for="i2s">I2S</label>
|
||||
<input type="radio" id="bt" name="audio" />
|
||||
<label for="bt">Bluetooth</label>
|
||||
</div>
|
||||
<div id="btsinkdiv">
|
||||
<input id="btsink" type="text" value="BT sink name" />
|
||||
</div>
|
||||
<div>
|
||||
<h2>Player name</h2>
|
||||
<input id="player" type="text" value="squeezelite" />
|
||||
</div>
|
||||
<div>
|
||||
<h2>Optional setting (e.g. for LMS IP address)</h2>
|
||||
<input id="optional" type="text" value="" placeholder="-s 192.168.0.1" />
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
<input id="generate-command" type="button" value="Generate" />
|
||||
</div>
|
||||
|
||||
<h2>Command to run</h2>
|
||||
<section id="command-list">
|
||||
<textarea id="autoexec1" maxlength="120">squeezelite -o I2S -b 500:2000 -d all=info -M esp32</textarea>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
<input id="update-command" type="button" value="Update" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="otadiv">
|
||||
<header><h1>Firmware upgrade</h1></header>
|
||||
<form name="multipart" action="otaform" method="post" enctype="multipart/form-data" onsubmit="do_upload(this); return false;">
|
||||
<progress id="progr" value="0" max="100" >Upload Progress</progress>
|
||||
<input type="file" name="ota" id="ota" size="20" accept=".bin" onchange="file_change();" style="font-size: 12pt">
|
||||
<span id="file_info" style="font-size:12pt;"></span>
|
||||
<input type="submit" id="update" disabled="" value="upload">
|
||||
<input type="submit" id="factory" disabled="" value="factory">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="wifi">
|
||||
<header>
|
||||
<h1>Wi-Fi</h1>
|
||||
</header>
|
||||
<div id="wifi-status">
|
||||
<h2>Connected to:</h2>
|
||||
<section id="connected-to">
|
||||
<div class="ape"><div class="w0"><div class="pw"><span></span></div></div></div>
|
||||
</section>
|
||||
</div>
|
||||
<h2>Manual connect</h2>
|
||||
<section id="manual_add">
|
||||
<div class="ape">ADD (HIDDEN) SSID<div>
|
||||
</section>
|
||||
<h2>or choose a network...</h2>
|
||||
<section id="wifi-list">
|
||||
</section>
|
||||
<div id="pwrdby"><em>Powered by </em><a id="acredits" href="#"><strong>esp32-wifi-manager</strong></a>.</div>
|
||||
</div>
|
||||
<div id="connect_manual">
|
||||
<header>
|
||||
<h1>Enter Details</h1>
|
||||
</header>
|
||||
<h2>Manual Connection</h2>
|
||||
<section>
|
||||
<input id="manual_ssid" type="text" placeholder="SSID" value="">
|
||||
<input id="manual_pwd" type="password" placeholder="Password" value="">
|
||||
</section>
|
||||
<div class="buttons">
|
||||
<input id="manual_join" type="button" value="Join" data-connect="manual" />
|
||||
<input id="manual_cancel" type="button" value="Cancel"/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="connect">
|
||||
<header>
|
||||
<h1>Enter Password</h1>
|
||||
</header>
|
||||
<h2>Password for <span id="ssid-pwd"></span></h2>
|
||||
<section>
|
||||
<input id="pwd" type="password" placeholder="Password" value="">
|
||||
</section>
|
||||
<div class="buttons">
|
||||
<input id="join" type="button" value="Join" />
|
||||
<input id="cancel" type="button" value="Cancel"/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="connect-wait">
|
||||
<header>
|
||||
<h1>Please wait...</h1>
|
||||
</header>
|
||||
<h2>Connecting to <span id="ssid-wait"></span></h2>
|
||||
<section>
|
||||
<div id="loading">
|
||||
<div class="spinner"><div class="double-bounce1"></div><div class="double-bounce2"></div></div>
|
||||
<p class="tctr">You may lose wifi access while the esp32 recalibrates its radio. Please wait until your device automatically reconnects. This can take up to 30s.</p>
|
||||
</div>
|
||||
<div id="connect-success">
|
||||
<h3 class="gr">Success!</h3>
|
||||
</div>
|
||||
<div id="connect-fail">
|
||||
<h3 class="rd">Connection failed</h3>
|
||||
<p class="tctr">Please double-check wifi password if any and make sure the access point has good signal.</p>
|
||||
</div>
|
||||
</section>
|
||||
<div class="buttons">
|
||||
<input id="ok-connect" type="button" value="OK" class="ctr" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="connect-details">
|
||||
<div id="connect-details-wrap">
|
||||
<header>
|
||||
<h1></h1>
|
||||
</header>
|
||||
<h2></h2>
|
||||
<section>
|
||||
<div class="buttons">
|
||||
<input id="disconnect" type="button" value="Disconnect" class="ctr"/>
|
||||
</div>
|
||||
</section>
|
||||
<h2>IP Address</h2>
|
||||
<section>
|
||||
<div class="ape brdb">IP Address:<div id="ip" class="fr"></div></div>
|
||||
<div class="ape brdb">Subnet Mask:<div id="netmask" class="fr"></div></div>
|
||||
<div class="ape">Default Gateway:<div id="gw" class="fr"></div></div>
|
||||
</section>
|
||||
<div class="buttons">
|
||||
<input id="ok-details" type="button" value="OK" class="ctr" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="diag-disconnect" class="diag-box">
|
||||
<div class="diag-box-win">
|
||||
<p>Are you sure you would like to disconnect from this wifi?</p>
|
||||
<div class="buttons">
|
||||
<input id="no-disconnect" type="button" value="No" />
|
||||
<input id="yes-disconnect" type="button" value="Yes" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="credits">
|
||||
<header>
|
||||
<h1>About this app...</h1>
|
||||
</header>
|
||||
<h2></h2>
|
||||
<section>
|
||||
<p><strong>esp32-wifi-manager</strong>, © 2017-2019, Tony Pottier<br />Licender under the MIT License.</p>
|
||||
<p>
|
||||
This app would not be possible without the following libraries:
|
||||
</p>
|
||||
<ul>
|
||||
<li>SpinKit, © 2015, Tobias Ahlin. Licensed under the MIT License.</li>
|
||||
<li>jQuery, The jQuery Foundation. Licensed under the MIT License.</li>
|
||||
<li>cJSON, © 2009-2017, Dave Gamble and cJSON contributors. Licensed under the MIT License.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<div class="buttons">
|
||||
<input id="ok-credits" type="button" value="OK" class="ctr" />
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<html>
|
||||
BIN
components/wifi-manager/jquery.gz
Normal file
4
components/wifi-manager/jquery.js
vendored
Normal file
144
components/wifi-manager/json.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
@file json.c
|
||||
@brief handles very basic JSON with a minimal footprint on the system
|
||||
|
||||
This code is a lightly modified version of cJSON 1.4.7. cJSON is licensed under the MIT license:
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@see https://github.com/DaveGamble/cJSON
|
||||
*/
|
||||
|
||||
#include "json.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
bool json_print_string(const unsigned char *input, unsigned char *output_buffer)
|
||||
{
|
||||
const unsigned char *input_pointer = NULL;
|
||||
unsigned char *output = NULL;
|
||||
unsigned char *output_pointer = NULL;
|
||||
size_t output_length = 0;
|
||||
/* numbers of additional characters needed for escaping */
|
||||
size_t escape_characters = 0;
|
||||
|
||||
if (output_buffer == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* empty string */
|
||||
if (input == NULL)
|
||||
{
|
||||
//output = ensure(output_buffer, sizeof("\"\""), hooks);
|
||||
if (output == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
strcpy((char*)output, "\"\"");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* set "flag" to 1 if something needs to be escaped */
|
||||
for (input_pointer = input; *input_pointer; input_pointer++)
|
||||
{
|
||||
if (strchr("\"\\\b\f\n\r\t", *input_pointer))
|
||||
{
|
||||
/* one character escape sequence */
|
||||
escape_characters++;
|
||||
}
|
||||
else if (*input_pointer < 32)
|
||||
{
|
||||
/* UTF-16 escape sequence uXXXX */
|
||||
escape_characters += 5;
|
||||
}
|
||||
}
|
||||
output_length = (size_t)(input_pointer - input) + escape_characters;
|
||||
|
||||
/* in the original cJSON it is possible to realloc here in case output buffer is too small.
|
||||
* This is overkill for an embedded system. */
|
||||
output = output_buffer;
|
||||
|
||||
/* no characters have to be escaped */
|
||||
if (escape_characters == 0)
|
||||
{
|
||||
output[0] = '\"';
|
||||
memcpy(output + 1, input, output_length);
|
||||
output[output_length + 1] = '\"';
|
||||
output[output_length + 2] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
output[0] = '\"';
|
||||
output_pointer = output + 1;
|
||||
/* copy the string */
|
||||
for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
|
||||
{
|
||||
if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
|
||||
{
|
||||
/* normal character, copy */
|
||||
*output_pointer = *input_pointer;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* character needs to be escaped */
|
||||
*output_pointer++ = '\\';
|
||||
switch (*input_pointer)
|
||||
{
|
||||
case '\\':
|
||||
*output_pointer = '\\';
|
||||
break;
|
||||
case '\"':
|
||||
*output_pointer = '\"';
|
||||
break;
|
||||
case '\b':
|
||||
*output_pointer = 'b';
|
||||
break;
|
||||
case '\f':
|
||||
*output_pointer = 'f';
|
||||
break;
|
||||
case '\n':
|
||||
*output_pointer = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
*output_pointer = 'r';
|
||||
break;
|
||||
case '\t':
|
||||
*output_pointer = 't';
|
||||
break;
|
||||
default:
|
||||
/* escape and print as unicode codepoint */
|
||||
sprintf((char*)output_pointer, "u%04x", *input_pointer);
|
||||
output_pointer += 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
output[output_length + 1] = '\"';
|
||||
output[output_length + 2] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
47
components/wifi-manager/json.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
@file json.h
|
||||
@brief handles very basic JSON with a minimal footprint on the system
|
||||
|
||||
This code is a lightly modified version of cJSON 1.4.7. cJSON is licensed under the MIT license:
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@see https://github.com/DaveGamble/cJSON
|
||||
*/
|
||||
|
||||
#ifndef JSON_H_INCLUDED
|
||||
#define JSON_H_INCLUDED
|
||||
#include <stdbool.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Render the cstring provided to a JSON escaped version that can be printed.
|
||||
* @param input the input buffer to be escaped.
|
||||
* @param output_buffer the output buffer to write to. You must ensure it is big enough to contain the final string.
|
||||
* @see cJSON equivlaent static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
|
||||
*/
|
||||
bool json_print_string(const unsigned char *input, unsigned char *output_buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* JSON_H_INCLUDED */
|
||||
BIN
components/wifi-manager/lock.png
Normal file
|
After Width: | Height: | Size: 433 B |
29
components/wifi-manager/main.c.txt
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright (c) 2017-2019 Tony Pottier
|
||||
|
||||
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.
|
||||
|
||||
@file main.c
|
||||
@author Tony Pottier
|
||||
@brief Entry point for the ESP32 application.
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
*/
|
||||
|
||||
|
||||
BIN
components/wifi-manager/settings.png
Normal file
|
After Width: | Height: | Size: 901 B |
1
components/wifi-manager/status
Normal file
@@ -0,0 +1 @@
|
||||
{"ssid":"zodmgbbq","ip":"192.168.1.119","netmask":"255.255.255.0","gw":"192.168.1.1","urc":0}
|
||||
378
components/wifi-manager/style.css
Normal file
@@ -0,0 +1,378 @@
|
||||
body {
|
||||
background-color: #eee;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
font: 1.1em tahoma, arial, sans-serif;
|
||||
}
|
||||
a {
|
||||
color: darkblue;
|
||||
transition: color .2s ease-out;
|
||||
text-decoration: none
|
||||
}
|
||||
a:hover {
|
||||
color: red;
|
||||
}
|
||||
input {
|
||||
font: 1.1em tahoma, arial, sans-serif;
|
||||
}
|
||||
input:focus,
|
||||
select:focus,
|
||||
textarea:focus,
|
||||
button:focus {
|
||||
outline: none;
|
||||
}
|
||||
input[type="button"] {
|
||||
width: 100px;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
p {
|
||||
padding: 10px;
|
||||
}
|
||||
#credits {
|
||||
display: none;
|
||||
}
|
||||
#app {} #app-wrap {} #disconnect {
|
||||
width: 150px;
|
||||
}
|
||||
.diag-box {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: none;
|
||||
}
|
||||
.diag-box-win {
|
||||
position: absolute;
|
||||
left: 10%;
|
||||
width: 80%;
|
||||
text-align: center;
|
||||
border: 2px outset #888;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
top: 20%;
|
||||
}
|
||||
.blur {
|
||||
-webkit-filter: blur(2px);
|
||||
-moz-filter: blur(2px);
|
||||
-ms-filter: blur(2px);
|
||||
-o-filter: blur(2px);
|
||||
filter: blur(2px);
|
||||
}
|
||||
.ape {
|
||||
margin-left: 20px;
|
||||
padding: 10px 0px 10px 10px;
|
||||
}
|
||||
.ape:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.brdb {
|
||||
border-bottom: 1px solid #888;
|
||||
}
|
||||
header {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #888;
|
||||
border-top: 1px solid #888;
|
||||
}
|
||||
section {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #888;
|
||||
border-top: 1px solid #888;
|
||||
}
|
||||
h1 {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 15px;
|
||||
font-size: 1.4em
|
||||
}
|
||||
h2 {
|
||||
margin: 0;
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
text-transform: uppercase;
|
||||
color: #888;
|
||||
font-size: 1.0em
|
||||
}
|
||||
h3 {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
padding: 20px 0px 20px 0px;
|
||||
}
|
||||
.gr {
|
||||
color: green;
|
||||
}
|
||||
.rd {
|
||||
color: red;
|
||||
}
|
||||
#wifi-status {
|
||||
display: none;
|
||||
}
|
||||
#connect {
|
||||
display: none;
|
||||
}
|
||||
#connect_manual {
|
||||
display: none;
|
||||
}
|
||||
#manual_ssid {
|
||||
border: none;
|
||||
width: 80%;
|
||||
margin-left: 35px;
|
||||
padding: 10px 0px 10px 10px;
|
||||
display: block
|
||||
}
|
||||
#manual_pwd {
|
||||
border: none;
|
||||
width: 80%;
|
||||
margin-left: 35px;
|
||||
padding: 10px 0px 10px 10px;
|
||||
display: block
|
||||
}
|
||||
#pwd {
|
||||
border: none;
|
||||
width: 80%;
|
||||
margin-left: 35px;
|
||||
padding: 10px 0px 10px 10px;
|
||||
display: block
|
||||
}
|
||||
.buttons {
|
||||
padding: 15px;
|
||||
}
|
||||
#join {
|
||||
float: right;
|
||||
}
|
||||
#manual_join {
|
||||
float: right;
|
||||
}
|
||||
#yes-disconnect {
|
||||
display: inline-block;
|
||||
margin-left: 20px;
|
||||
}
|
||||
#no-disconnect {
|
||||
display: inline-block;
|
||||
}
|
||||
.ctr {
|
||||
margin: 0 auto;
|
||||
}
|
||||
.tctr {
|
||||
text-align: center;
|
||||
}
|
||||
#connect-wait {
|
||||
display: none;
|
||||
}
|
||||
#connect-success {
|
||||
display: none;
|
||||
}
|
||||
#connect-fail {
|
||||
display: none;
|
||||
}
|
||||
#connect-details {
|
||||
display: none;
|
||||
}
|
||||
.fr {
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.w0 {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTJDBGvsAAABzUlEQVRIS+WUTShEURTH3zyRhjQ+8hWxmCJMoSzEwsbCgi1LZRYW9pONptiwka9iI81CWFpYaEqNMkVKmpWN1IhYKN9ZDL/z3p3mxZh5g9X4168799xz/vPefedeLeuVC+3gdTgc07CsmCQ2DI2gg21Jci30wSpGt/CeghickTsHPVACDkgqp67rPgpO4E0ZZMIj7OHhxSvPtEyomcVDeFXJv+EZNvEsNa01rZfAuSUhThR2wU+ObJkbyhRNMMDaDIThBqy1MdZ3wAPawqfFC2Lj0Ab5kpBGxdAJs9TeW72ITUhCPZMjFYwwbwXpnkwlDzOIx50yXwP5c0MeggHGanNqSDqqBqQ7/Kxvg2zHAfMN8IE8uZhYO6eBnBXGKnOakLWfaQZ9jMRjSPXhZUuC5A9JjVFpKkeNSVVA0Tq8KJN0yFl4gilqbW2tm+SQKoybXIG8jcT34RSsh1Byt6iVg2ZLlRCg6JpROqEDpFheXZ5S9rcLFsl5YJwHad+MVA5y13w5lRY5oRsKjdm/Vz/7LR86zG+5wr+9NX+iOowjEO+aELEic+lv1ILppeUPosRst6QduTANgnE2mC+BnYswI1VwfYzCCL9dZij7pWkf6UeSTYAuE/QAAAAASUVORK5CYII=') no-repeat right top;
|
||||
height: 24px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.w1 {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEQAACxEBf2RfkQAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xNkRpr/UAAAHiSURBVEhL5dRPKINxHMfxPVskpA35F3FYEVasHBYHFwcHrhyVZ8vBfblIceEi/4qLtINwdHCQUpQVKcnJRYqIg/J3OYz399nv0YPNtuzEt149+31/v+/n4fGYLVHpup4Rnyregd+K27TIghe63+8fx7wySqsPdbAj3qzha0MOV6ETiwTd4u0HUZxydgrtKISGj0xreG4gEAgycIRXFZCOR2yTQZSebeaa4Q1s7iOiDv/GM1bJLDJv0EHjzHLAdIFNjHBGHpkbxUo9utmbQBg3sM5G2d+AR24w82XznN4QmpGjXrCExRkXfJhk9t6aRW9YDtSwOFDNE9ZNyFLzKRczOegh406FL8ElG8JDM8S1Qtaq7KhEO0Y0TVtHGHusVxCEDy5oMLNqyVrgWm5kqaYw3mdVdmqQsENE8JbAPbY43yszMqiyHOr66QayL5XH0DJeVEgyUTxhjNmPR/vtBpZyc3hHDZohV5DfRvq7OMYtrDdZY7YwFpG8yhBi6JrrMFogww7IT1mOVsxy5oHrNIqRVpWgDtnGKn7log35xurfVxfPW/7QYT57Ybz7mapqgk9gvjU79ApiW5mpRkIvLTe4oJfyK5lKOQndgvG/wXoOSb8I061Svj4G0M9nZ6z198tmeweYtIrMYP17VAAAAABJRU5ErkJggg==') no-repeat right top;
|
||||
height: 24px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.w2 {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEQAACxEBf2RfkQAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xNkRpr/UAAAHkSURBVEhL3dRLKERRGMDxuSPSkLzyilgo8iiUhVjYWFiwZancmSzsZSPFho28io1kISwtLKQURZGSrGykiFgo7yyG/zdzznRm5iK5Sk79uvd85/u++5hzx2Pb9q9yDLrJMWhIRB1sv98/ghlliFAXyuGFU21IbECSi9CKORrd4O0TQZyQO45mZMJCpKfZ3BcIBPooOMSravAdD9ikB63sJN1XN69kcQ8vKvknnrBMzyx9gRYCp0aCdo51DJIjr6wU2UoF2lkbxS6uYdYGWV9DtVxgMmbxjFg/apEM/ZQfyUADxqi9M3sRG5CEEib7KnjMvAaye2IbfUVupoMet6r5PDL0YjXBBY4Fai5kRxVCdscg66uQ17HDfAl9kDuXJzB3Thk5sxzzZa6DumHknN3QS+IBPvvh5ZVskN8ZU5+gz3XAlELRIp5Vk6/It/CIYWrjXm3URCkleUsV6iaXkKeR+DaOYH6EkrtCrXxoUf2iJoY8LFB0xXEA9ZBieXS5S3m/jZgi557jBGT7xvWKCxhyIP81ka/SgQ9NSDViURyDbvpTo82yrAPscl4HKxR1aRTT+BhvyhaxtPCSO6OKphfGBc6JZYaX3BnpNN1AUC7AfBrJoRUXR67X6+1BN+fp4dD/Hx7PO4o9VGuAapKIAAAAAElFTkSuQmCC') no-repeat right top;
|
||||
height: 24px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.w3 {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTZEaa/1AAACAElEQVRIS7XUP0gbYRjH8VSpiBZJWvEflXYQKtqACg6ig4uDg651LPQSHLpLlyDoUhdpVdBFxEG0YwcHCQgRFJSCFKcuUohY6iC0VYtD9PuE9w3vXZ74h16HD3fv733e53KX9y7ied5/pYZhUkPHQ3TBSyQS7zFvTBC9RivKoK3NCwZS3IxBLNLoBFc3yOEbtR/Qj8d4gEJPt3lVMpkcY8E+Lk2D+/iDTXrQyquwfW3zdiZ38dcU/4tzrNHzib3AAMGhU2BlsYFxauSRtaDWaMMwc1PYwU+4a3PMryMuF5gJTH4ne4dOVMLeZSkx9GCatb/cXmQpKXjOYM+EB4w7ILsn2Og28mNe0ePUNF9CzE7GCZc5NpmxkB31FLI7xpn/DHkc24xXMQb55XIH7s55Qc0Cx0YZ29A2LJyzG95S+AU3/fHySNLUjwTWl9tzG7iqWbSCC9PkNvIunGGStUWP1jcwWijOmIW2yTHkbiTfwle4L6HUfmKtvGi+fr6BowHLLPrBMYVuyGK5dfmV8nx7MUvNb44fIdu3qFdR4KiDfGsKb6WiCn145GQ+ahgmNQyTGpYwxPOWP3qHc/mE+76apaih4hmND2B3TYasJlCjUkPFS5oeORfIkhVtSY0aKqI0TSP/bjCew10+hPf6D+r5fIziDefRwFxJahgmNQyPF7kGEsc1es+A2E4AAAAASUVORK5CYII=') no-repeat right top;
|
||||
height: 24px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.pw {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTJDBGvsAAABIUlEQVRIS+3VsU7CUBTGcYhBJCwqwcmEJ2DkCQgzb8ADmLgYWXTzMVjcGNjYGEAXgoSRhTg5OroYIyFY/h+hWGwvtzQ0LpzkF8i5l/uRQ2kTjuPEKrC5T79vzHWJO4wxwzeGuMY5AitsQBFvmEObvNQboQBfhQk4gQ5wD+zgBrcYrHrSwzE2KkxAHVrQWB6QgiqJLB7xA+2pYaNsAWm8QAsa0Sn+1gU+oT1NHGFdtoAcJtBCSw1DuaPqQiNdly0gj1doQaMwleavPc+IJUDffKeADO7Rxxe08A4dEOQD2qPXJ1xh+VuYAirQVaNGFFPov2MM0OXm/UAUZRwCtjoEWP1vQBXuLTgKPYRKMAacoY0oIboDNLB8+PgC4hLY3B8nsQCQEf56jLJoQAAAAABJRU5ErkJggg==') no-repeat right top;
|
||||
height: 24px;
|
||||
margin-right: 20px;
|
||||
height: 24px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
/* SpinKit is licensed under the MIT License. Copyright (c) 2015 Tobias Ahlin */
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
position: relative;
|
||||
margin: 100px auto;
|
||||
}
|
||||
.double-bounce1,
|
||||
.double-bounce2 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background-color: #333;
|
||||
opacity: 0.6;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
-webkit-animation: sk-bounce 2.0s infinite ease-in-out;
|
||||
animation: sk-bounce 2.0s infinite ease-in-out;
|
||||
}
|
||||
.double-bounce2 {
|
||||
-webkit-animation-delay: -1.0s;
|
||||
animation-delay: -1.0s;
|
||||
}
|
||||
@-webkit-keyframes sk-bounce {
|
||||
0%, 100% {
|
||||
-webkit-transform: scale(0.0)
|
||||
}
|
||||
50% {
|
||||
-webkit-transform: scale(1.0)
|
||||
}
|
||||
}
|
||||
@keyframes sk-bounce {
|
||||
0%, 100% {
|
||||
transform: scale(0.0);
|
||||
-webkit-transform: scale(0.0);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.0);
|
||||
-webkit-transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
/* end of SpinKit */
|
||||
|
||||
.toggle label {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: 3.5em;
|
||||
}
|
||||
|
||||
.toggle input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.toggle .slider {
|
||||
/* Grundfläche */
|
||||
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 1.5em;
|
||||
|
||||
left: 8px;
|
||||
|
||||
width: 4em;
|
||||
height: 2em;
|
||||
background-color: #c32e04;
|
||||
/* red */
|
||||
|
||||
transition: all .3s ease-in-out;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
.toggle .slider:before {
|
||||
/* verschiebbarer Button */
|
||||
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 1.6em;
|
||||
width: 1.6em;
|
||||
left: 0.2em;
|
||||
bottom: 0.2em;
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
transition: all .3s ease-in-out;
|
||||
}
|
||||
|
||||
.toggle input:checked + .slider {
|
||||
background-color: #5a9900;
|
||||
/* green */
|
||||
}
|
||||
|
||||
.toggle input:focus + .slider {
|
||||
background-color: pink;
|
||||
box-shadow: 0 0 1px #5a9900;
|
||||
}
|
||||
|
||||
.toggle input:checked + .slider:before {
|
||||
-webkit-transform: translateX(1.9em);
|
||||
/* Android 4 */
|
||||
|
||||
-ms-transform: translateX(1.9em);
|
||||
/* IE9 */
|
||||
|
||||
transform: translateX(1.9em);
|
||||
}
|
||||
|
||||
.text .slider:after {
|
||||
/* Text vor dem FlipFlop-Schalter */
|
||||
|
||||
position: absolute;
|
||||
content: "AUS";
|
||||
color: #c32e04;
|
||||
font-weight: bold;
|
||||
height: 1.6em;
|
||||
left: -2.5em;
|
||||
bottom: 0.2em;
|
||||
}
|
||||
|
||||
.text input:checked + .slider:after {
|
||||
/* Text hinter dem FlipFlop-Schalter */
|
||||
|
||||
position: absolute;
|
||||
content: "AN";
|
||||
color: #5a9900;
|
||||
left: 4.5em;
|
||||
}
|
||||
|
||||
input#autoexec1 {
|
||||
border: none;
|
||||
margin-left: 35px;
|
||||
padding: 10px 0px 10px 10px;
|
||||
}
|
||||
|
||||
input#ota {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#otadiv {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#btsink, #player, #optional {
|
||||
margin-left: 13px;
|
||||
}
|
||||
|
||||
#btsinkdiv {
|
||||
display: none;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
textarea#autoexec1 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.toggle-buttons input[type="radio"] {
|
||||
visibility: hidden;
|
||||
width: 3px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.toggle-buttons label {
|
||||
border: 1px solid #000;
|
||||
border-radius: 0.5em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.toggle-buttons input:checked + label {
|
||||
background: #5a9900;
|
||||
box-shadow: none;
|
||||
}
|
||||
BIN
components/wifi-manager/wifi0.png
Normal file
|
After Width: | Height: | Size: 605 B |
BIN
components/wifi-manager/wifi1.png
Normal file
|
After Width: | Height: | Size: 613 B |
BIN
components/wifi-manager/wifi2.png
Normal file
|
After Width: | Height: | Size: 615 B |
BIN
components/wifi-manager/wifi24.png
Normal file
|
After Width: | Height: | Size: 605 B |
BIN
components/wifi-manager/wifi3.png
Normal file
|
After Width: | Height: | Size: 656 B |
1140
components/wifi-manager/wifi_manager.c
Normal file
397
components/wifi-manager/wifi_manager.h
Normal file
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
Copyright (c) 2017-2019 Tony Pottier
|
||||
|
||||
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.
|
||||
|
||||
@file wifi_manager.h
|
||||
@author Tony Pottier
|
||||
@brief Defines all functions necessary for esp32 to connect to a wifi/scan wifis
|
||||
|
||||
Contains the freeRTOS task and all necessary support
|
||||
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
*/
|
||||
|
||||
#ifndef WIFI_MANAGER_H_INCLUDED
|
||||
#define WIFI_MANAGER_H_INCLUDED
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_wifi_types.h"
|
||||
|
||||
|
||||
#define DEFAULT_COMMAND_LINE CONFIG_DEFAULT_COMMAND_LINE
|
||||
|
||||
/**
|
||||
* @brief Defines the maximum size of a SSID name. 32 is IEEE standard.
|
||||
* @warning limit is also hard coded in wifi_config_t. Never extend this value.
|
||||
*/
|
||||
#define MAX_SSID_SIZE 32
|
||||
|
||||
/**
|
||||
* @brief Defines the maximum size of a WPA2 passkey. 64 is IEEE standard.
|
||||
* @warning limit is also hard coded in wifi_config_t. Never extend this value.
|
||||
*/
|
||||
#define MAX_PASSWORD_SIZE 64
|
||||
#define MAX_COMMAND_LINE_SIZE 201
|
||||
|
||||
/**
|
||||
* @brief Defines the maximum number of access points that can be scanned.
|
||||
*
|
||||
* To save memory and avoid nasty out of memory errors,
|
||||
* we can limit the number of APs detected in a wifi scan.
|
||||
*/
|
||||
#define MAX_AP_NUM 15
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines when a connection is lost/attempt to connect is made, how many retries should be made before giving up.
|
||||
* Setting it to 2 for instance means there will be 3 attempts in total (original request + 2 retries)
|
||||
*/
|
||||
#define WIFI_MANAGER_MAX_RETRY CONFIG_WIFI_MANAGER_MAX_RETRY
|
||||
|
||||
/** @brief Defines the task priority of the wifi_manager.
|
||||
*
|
||||
* Tasks spawn by the manager will have a priority of WIFI_MANAGER_TASK_PRIORITY-1.
|
||||
* For this particular reason, minimum task priority is 1. It it highly not recommended to set
|
||||
* it to 1 though as the sub-tasks will now have a priority of 0 which is the priority
|
||||
* of freeRTOS' idle task.
|
||||
*/
|
||||
#define WIFI_MANAGER_TASK_PRIORITY CONFIG_WIFI_MANAGER_TASK_PRIORITY
|
||||
|
||||
/** @brief Defines the auth mode as an access point
|
||||
* Value must be of type wifi_auth_mode_t
|
||||
* @see esp_wifi_types.h
|
||||
* @warning if set to WIFI_AUTH_OPEN, passowrd me be empty. See DEFAULT_AP_PASSWORD.
|
||||
*/
|
||||
#define AP_AUTHMODE WIFI_AUTH_WPA2_PSK
|
||||
|
||||
/** @brief Defines visibility of the access point. 0: visible AP. 1: hidden */
|
||||
#define DEFAULT_AP_SSID_HIDDEN 0
|
||||
|
||||
/** @brief Defines access point's name. Default value: esp32. Run 'make menuconfig' to setup your own value or replace here by a string */
|
||||
#define DEFAULT_AP_SSID CONFIG_DEFAULT_AP_SSID
|
||||
|
||||
/** @brief Defines access point's password.
|
||||
* @warning In the case of an open access point, the password must be a null string "" or "\0" if you want to be verbose but waste one byte.
|
||||
* In addition, the AP_AUTHMODE must be WIFI_AUTH_OPEN
|
||||
*/
|
||||
#define DEFAULT_AP_PASSWORD CONFIG_DEFAULT_AP_PASSWORD
|
||||
|
||||
/** @brief Defines the hostname broadcasted by mDNS */
|
||||
#define DEFAULT_HOSTNAME "esp32"
|
||||
|
||||
/** @brief Defines access point's bandwidth.
|
||||
* Value: WIFI_BW_HT20 for 20 MHz or WIFI_BW_HT40 for 40 MHz
|
||||
* 20 MHz minimize channel interference but is not suitable for
|
||||
* applications with high data speeds
|
||||
*/
|
||||
#define DEFAULT_AP_BANDWIDTH WIFI_BW_HT20
|
||||
|
||||
/** @brief Defines access point's channel.
|
||||
* Channel selection is only effective when not connected to another AP.
|
||||
* Good practice for minimal channel interference to use
|
||||
* For 20 MHz: 1, 6 or 11 in USA and 1, 5, 9 or 13 in most parts of the world
|
||||
* For 40 MHz: 3 in USA and 3 or 11 in most parts of the world
|
||||
*/
|
||||
#define DEFAULT_AP_CHANNEL CONFIG_DEFAULT_AP_CHANNEL
|
||||
|
||||
|
||||
|
||||
/** @brief Defines the access point's default IP address. Default: "10.10.0.1 */
|
||||
#define DEFAULT_AP_IP CONFIG_DEFAULT_AP_IP
|
||||
|
||||
/** @brief Defines the access point's gateway. This should be the same as your IP. Default: "10.10.0.1" */
|
||||
#define DEFAULT_AP_GATEWAY CONFIG_DEFAULT_AP_GATEWAY
|
||||
|
||||
/** @brief Defines the access point's netmask. Default: "255.255.255.0" */
|
||||
#define DEFAULT_AP_NETMASK CONFIG_DEFAULT_AP_NETMASK
|
||||
|
||||
/** @brief Defines access point's maximum number of clients. Default: 4 */
|
||||
#define DEFAULT_AP_MAX_CONNECTIONS CONFIG_DEFAULT_AP_MAX_CONNECTIONS
|
||||
|
||||
/** @brief Defines access point's beacon interval. 100ms is the recommended default. */
|
||||
#define DEFAULT_AP_BEACON_INTERVAL CONFIG_DEFAULT_AP_BEACON_INTERVAL
|
||||
|
||||
/** @brief Defines if esp32 shall run both AP + STA when connected to another AP.
|
||||
* Value: 0 will have the own AP always on (APSTA mode)
|
||||
* Value: 1 will turn off own AP when connected to another AP (STA only mode when connected)
|
||||
* Turning off own AP when connected to another AP minimize channel interference and increase throughput
|
||||
*/
|
||||
#define DEFAULT_STA_ONLY 1
|
||||
|
||||
/** @brief Defines if wifi power save shall be enabled.
|
||||
* Value: WIFI_PS_NONE for full power (wifi modem always on)
|
||||
* Value: WIFI_PS_MODEM for power save (wifi modem sleep periodically)
|
||||
* Note: Power save is only effective when in STA only mode
|
||||
*/
|
||||
#define DEFAULT_STA_POWER_SAVE WIFI_PS_NONE
|
||||
|
||||
/**
|
||||
* @brief Defines the maximum length in bytes of a JSON representation of an access point.
|
||||
*
|
||||
* maximum ap string length with full 32 char ssid: 75 + \\n + \0 = 77\n
|
||||
* example: {"ssid":"abcdefghijklmnopqrstuvwxyz012345","chan":12,"rssi":-100,"auth":4},\n
|
||||
* BUT: we need to escape JSON. Imagine a ssid full of \" ? so it's 32 more bytes hence 77 + 32 = 99.\n
|
||||
* this is an edge case but I don't think we should crash in a catastrophic manner just because
|
||||
* someone decided to have a funny wifi name.
|
||||
*/
|
||||
#define JSON_ONE_APP_SIZE 99
|
||||
|
||||
/**
|
||||
* @brief Defines the maximum length in bytes of a JSON representation of the IP information
|
||||
* assuming all ips are 4*3 digits, and all characters in the ssid require to be escaped.
|
||||
* example: {"ssid":"abcdefghijklmnopqrstuvwxyz012345","ip":"192.168.1.119","netmask":"255.255.255.0","gw":"192.168.1.1","urc":0}
|
||||
*/
|
||||
#define JSON_IP_INFO_SIZE 150
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines the complete list of all messages that the wifi_manager can process.
|
||||
*
|
||||
* Some of these message are events ("EVENT"), and some of them are action ("ORDER")
|
||||
* Each of these messages can trigger a callback function and each callback function is stored
|
||||
* in a function pointer array for convenience. Because of this behavior, it is extremely important
|
||||
* to maintain a strict sequence and the top level special element 'MESSAGE_CODE_COUNT'
|
||||
*
|
||||
* @see wifi_manager_set_callback
|
||||
*/
|
||||
typedef enum message_code_t {
|
||||
NONE = 0,
|
||||
ORDER_START_HTTP_SERVER = 1,
|
||||
ORDER_STOP_HTTP_SERVER = 2,
|
||||
ORDER_START_DNS_SERVICE = 3,
|
||||
ORDER_STOP_DNS_SERVICE = 4,
|
||||
ORDER_START_WIFI_SCAN = 5,
|
||||
ORDER_LOAD_AND_RESTORE_STA = 6,
|
||||
ORDER_CONNECT_STA = 7,
|
||||
ORDER_DISCONNECT_STA = 8,
|
||||
ORDER_START_AP = 9,
|
||||
ORDER_START_HTTP = 10,
|
||||
ORDER_START_DNS_HIJACK = 11,
|
||||
EVENT_STA_DISCONNECTED = 12,
|
||||
EVENT_SCAN_DONE = 13,
|
||||
EVENT_STA_GOT_IP = 14,
|
||||
MESSAGE_CODE_COUNT = 15 /* important for the callback array */
|
||||
|
||||
}message_code_t;
|
||||
|
||||
/**
|
||||
* @brief simplified reason codes for a lost connection.
|
||||
*
|
||||
* esp-idf maintains a big list of reason codes which in practice are useless for most typical application.
|
||||
*/
|
||||
typedef enum update_reason_code_t {
|
||||
UPDATE_CONNECTION_OK = 0,
|
||||
UPDATE_FAILED_ATTEMPT = 1,
|
||||
UPDATE_USER_DISCONNECT = 2,
|
||||
UPDATE_LOST_CONNECTION = 3
|
||||
}update_reason_code_t;
|
||||
|
||||
typedef enum connection_request_made_by_code_t{
|
||||
CONNECTION_REQUEST_NONE = 0,
|
||||
CONNECTION_REQUEST_USER = 1,
|
||||
CONNECTION_REQUEST_AUTO_RECONNECT = 2,
|
||||
CONNECTION_REQUEST_RESTORE_CONNECTION = 3,
|
||||
CONNECTION_REQUEST_MAX = 0x7fffffff /*force the creation of this enum as a 32 bit int */
|
||||
}connection_request_made_by_code_t;
|
||||
|
||||
/**
|
||||
* The actual WiFi settings in use
|
||||
*/
|
||||
struct wifi_settings_t{
|
||||
uint8_t ap_ssid[MAX_SSID_SIZE];
|
||||
uint8_t ap_pwd[MAX_PASSWORD_SIZE];
|
||||
uint8_t ap_channel;
|
||||
uint8_t ap_ssid_hidden;
|
||||
wifi_bandwidth_t ap_bandwidth;
|
||||
bool sta_only;
|
||||
wifi_ps_type_t sta_power_save;
|
||||
bool sta_static_ip;
|
||||
tcpip_adapter_ip_info_t sta_static_ip_config;
|
||||
};
|
||||
extern struct wifi_settings_t wifi_settings;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Structure used to store one message in the queue.
|
||||
*/
|
||||
typedef struct{
|
||||
message_code_t code;
|
||||
void *param;
|
||||
} queue_message;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Allocate heap memory for the wifi manager and start the wifi_manager RTOS task
|
||||
*/
|
||||
void wifi_manager_start();
|
||||
|
||||
/**
|
||||
* Frees up all memory allocated by the wifi_manager and kill the task.
|
||||
*/
|
||||
void wifi_manager_destroy();
|
||||
|
||||
/**
|
||||
* Filters the AP scan list to unique SSIDs
|
||||
*/
|
||||
void filter_unique( wifi_ap_record_t * aplist, uint16_t * ap_num);
|
||||
|
||||
/**
|
||||
* Main task for the wifi_manager
|
||||
*/
|
||||
void wifi_manager( void * pvParameters );
|
||||
|
||||
|
||||
char* wifi_manager_get_ap_list_json();
|
||||
char* wifi_manager_get_ip_info_json();
|
||||
|
||||
uint8_t wifi_manager_get_flag();
|
||||
char * wifi_manager_alloc_get_config(char * name, size_t * l);
|
||||
|
||||
|
||||
/**
|
||||
* @brief saves the current STA wifi config to flash ram storage.
|
||||
*/
|
||||
esp_err_t wifi_manager_save_sta_config();
|
||||
|
||||
/**
|
||||
* @brief saves the current configuration to flash ram storage
|
||||
*/
|
||||
esp_err_t wifi_manager_save_autoexec_config(char * value, char * name, int len);
|
||||
esp_err_t wifi_manager_save_autoexec_flag(uint8_t flag);
|
||||
|
||||
|
||||
/**
|
||||
* @brief fetch a previously STA wifi config in the flash ram storage.
|
||||
* @return true if a previously saved config was found, false otherwise.
|
||||
*/
|
||||
bool wifi_manager_fetch_wifi_sta_config();
|
||||
|
||||
wifi_config_t* wifi_manager_get_wifi_sta_config();
|
||||
|
||||
/**
|
||||
* @brief A standard wifi event handler as recommended by Espressif
|
||||
*/
|
||||
esp_err_t wifi_manager_event_handler(void *ctx, system_event_t *event);
|
||||
|
||||
|
||||
/**
|
||||
* @brief requests a connection to an access point that will be process in the main task thread.
|
||||
*/
|
||||
void wifi_manager_connect_async();
|
||||
|
||||
/**
|
||||
* @brief requests a wifi scan
|
||||
*/
|
||||
void wifi_manager_scan_async();
|
||||
|
||||
/**
|
||||
* @brief requests to disconnect and forget about the access point.
|
||||
*/
|
||||
void wifi_manager_disconnect_async();
|
||||
|
||||
/**
|
||||
* @brief Tries to get access to json buffer mutex.
|
||||
*
|
||||
* The HTTP server can try to access the json to serve clients while the wifi manager thread can try
|
||||
* to update it. These two tasks are synchronized through a mutex.
|
||||
*
|
||||
* The mutex is used by both the access point list json and the connection status json.\n
|
||||
* These two resources should technically have their own mutex but we lose some flexibility to save
|
||||
* on memory.
|
||||
*
|
||||
* This is a simple wrapper around freeRTOS function xSemaphoreTake.
|
||||
*
|
||||
* @param xTicksToWait The time in ticks to wait for the semaphore to become available.
|
||||
* @return true in success, false otherwise.
|
||||
*/
|
||||
bool wifi_manager_lock_json_buffer(TickType_t xTicksToWait);
|
||||
|
||||
/**
|
||||
* @brief Releases the json buffer mutex.
|
||||
*/
|
||||
void wifi_manager_unlock_json_buffer();
|
||||
|
||||
/**
|
||||
* @brief Generates the connection status json: ssid and IP addresses.
|
||||
* @note This is not thread-safe and should be called only if wifi_manager_lock_json_buffer call is successful.
|
||||
*/
|
||||
void wifi_manager_generate_ip_info_json(update_reason_code_t update_reason_code);
|
||||
/**
|
||||
* @brief Clears the connection status json.
|
||||
* @note This is not thread-safe and should be called only if wifi_manager_lock_json_buffer call is successful.
|
||||
*/
|
||||
void wifi_manager_clear_ip_info_json();
|
||||
|
||||
/**
|
||||
* @brief Generates the list of access points after a wifi scan.
|
||||
* @note This is not thread-safe and should be called only if wifi_manager_lock_json_buffer call is successful.
|
||||
*/
|
||||
void wifi_manager_generate_acess_points_json();
|
||||
|
||||
/**
|
||||
* @brief Clear the list of access points.
|
||||
* @note This is not thread-safe and should be called only if wifi_manager_lock_json_buffer call is successful.
|
||||
*/
|
||||
void wifi_manager_clear_access_points_json();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Start the mDNS service
|
||||
*/
|
||||
void wifi_manager_initialise_mdns();
|
||||
|
||||
|
||||
bool wifi_manager_lock_sta_ip_string(TickType_t xTicksToWait);
|
||||
void wifi_manager_unlock_sta_ip_string();
|
||||
|
||||
/**
|
||||
* @brief gets the string representation of the STA IP address, e.g.: "192.168.1.69"
|
||||
*/
|
||||
char* wifi_manager_get_sta_ip_string();
|
||||
|
||||
/**
|
||||
* @brief thread safe char representation of the STA IP update
|
||||
*/
|
||||
void wifi_manager_safe_update_sta_ip_string(uint32_t ip);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Register a callback to a custom function when specific event message_code happens.
|
||||
*/
|
||||
void wifi_manager_set_callback(message_code_t message_code, void (*func_ptr)(void*) );
|
||||
|
||||
|
||||
BaseType_t wifi_manager_send_message(message_code_t code, void *param);
|
||||
BaseType_t wifi_manager_send_message_to_front(message_code_t code, void *param);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WIFI_MANAGER_H_INCLUDED */
|
||||
@@ -4,4 +4,5 @@ set(COMPONENT_SRCS "esp_app_main.c" "platform_esp32.c" "cmd_wifi.c" "console.c"
|
||||
set(REQUIRES esp_common)
|
||||
set(REQUIRES_COMPONENTS freertos squeezelite nvs_flash esp32 spi_flash newlib log console )
|
||||
|
||||
|
||||
register_component()
|
||||
|
||||
@@ -21,72 +21,6 @@ menu "Squeezelite-ESP32"
|
||||
help
|
||||
Set logging level info|debug|sdebug
|
||||
endmenu
|
||||
menu "Wifi Configuration"
|
||||
config WIFI_SSID
|
||||
string "WiFi SSID"
|
||||
default "myssid"
|
||||
help
|
||||
SSID (network name) for the example to connect to.
|
||||
|
||||
config WIFI_PASSWORD
|
||||
string "WiFi Password"
|
||||
default "mypassword"
|
||||
help
|
||||
WiFi password (WPA or WPA2) for the example to use.
|
||||
|
||||
choice SCAN_METHOD
|
||||
prompt "scan method"
|
||||
default WIFI_FAST_SCAN
|
||||
help
|
||||
scan method for the esp32 to use
|
||||
|
||||
config WIFI_FAST_SCAN
|
||||
bool "fast"
|
||||
config WIFI_ALL_CHANNEL_SCAN
|
||||
bool "all"
|
||||
endchoice
|
||||
|
||||
choice SORT_METHOD
|
||||
prompt "sort method"
|
||||
default WIFI_CONNECT_AP_BY_SIGNAL
|
||||
help
|
||||
sort method for the esp32 to use
|
||||
|
||||
config WIFI_CONNECT_AP_BY_SIGNAL
|
||||
bool "rssi"
|
||||
config WIFI_CONNECT_AP_BY_SECURITY
|
||||
bool "authmode"
|
||||
endchoice
|
||||
|
||||
config FAST_SCAN_THRESHOLD
|
||||
bool "fast scan threshold"
|
||||
default y
|
||||
help
|
||||
wifi fast scan threshold
|
||||
|
||||
config FAST_SCAN_MINIMUM_SIGNAL
|
||||
int "fast scan minimum rssi"
|
||||
depends on FAST_SCAN_THRESHOLD
|
||||
range -127 0
|
||||
default -127
|
||||
help
|
||||
rssi is use to measure the signal
|
||||
|
||||
choice FAST_SCAN_WEAKEST_AUTHMODE
|
||||
prompt "fast scan weakest authmode"
|
||||
depends on FAST_SCAN_THRESHOLD
|
||||
default EXAMPLE_OPEN
|
||||
|
||||
config EXAMPLE_OPEN
|
||||
bool "open"
|
||||
config EXAMPLE_WEP
|
||||
bool "wep"
|
||||
config EXAMPLE_WPA
|
||||
bool "wpa"
|
||||
config EXAMPLE_WPA2
|
||||
bool "wpa2"
|
||||
endchoice
|
||||
endmenu
|
||||
menu "Audio CODEC libraries"
|
||||
config INCLUDE_FLAC
|
||||
bool "FLAC"
|
||||
|
||||
@@ -45,8 +45,9 @@ static void * squeezelite_thread(){
|
||||
return NULL;
|
||||
}
|
||||
isRunning=true;
|
||||
ESP_LOGI(TAG,"Waiting for WiFi.");
|
||||
while(!wait_for_wifi()){usleep(100000);};
|
||||
// Let's not wait on WiFi to allow squeezelite to run in bluetooth mode
|
||||
// ESP_LOGI(TAG,"Waiting for WiFi.");
|
||||
// while(!wait_for_wifi()){usleep(100000);};
|
||||
ESP_LOGD(TAG ,"Number of args received: %u",thread_parms.argc );
|
||||
ESP_LOGD(TAG ,"Values:");
|
||||
for(int i = 0;i<thread_parms.argc; i++){
|
||||
|
||||
181
main/cmd_wifi.c
@@ -7,183 +7,4 @@
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include "cmd_wifi.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cmd_decl.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_console.h"
|
||||
#include "argtable3/argtable3.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "tcpip_adapter.h"
|
||||
#include "esp_event.h"
|
||||
#include "led.h"
|
||||
|
||||
#define JOIN_TIMEOUT_MS (10000)
|
||||
|
||||
static EventGroupHandle_t wifi_event_group;
|
||||
const int CONNECTED_BIT = BIT0;
|
||||
static const char * TAG = "cmd_wifi";
|
||||
/** Arguments used by 'join' function */
|
||||
static struct {
|
||||
struct arg_int *timeout;
|
||||
struct arg_str *ssid;
|
||||
struct arg_str *password;
|
||||
struct arg_end *end;
|
||||
} join_args;
|
||||
|
||||
///** Arguments used by 'join' function */
|
||||
//static struct {
|
||||
// struct arg_int *autoconnect;
|
||||
// struct arg_end *end;
|
||||
//} auto_connect_args;
|
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
led_blink_pushed(LED_GREEN, 250, 250);
|
||||
esp_wifi_connect();
|
||||
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
led_unpush(LED_GREEN);
|
||||
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
|
||||
}
|
||||
}
|
||||
bool wait_for_wifi(){
|
||||
|
||||
bool connected=(xEventGroupGetBits(wifi_event_group) & CONNECTED_BIT)!=0;
|
||||
|
||||
if(!connected){
|
||||
ESP_LOGD(TAG,"Waiting for WiFi...");
|
||||
connected = (xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
|
||||
pdFALSE, pdTRUE, JOIN_TIMEOUT_MS / portTICK_PERIOD_MS)& CONNECTED_BIT)!=0;
|
||||
if(!connected){
|
||||
ESP_LOGD(TAG,"wifi timeout.");
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG,"WiFi Connected!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return connected;
|
||||
|
||||
}
|
||||
static void initialise_wifi(void)
|
||||
{
|
||||
static bool initialized = false;
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
tcpip_adapter_init();
|
||||
wifi_event_group = xEventGroupCreate();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
|
||||
ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &event_handler, NULL) );
|
||||
ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
|
||||
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
|
||||
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_NULL) );
|
||||
ESP_ERROR_CHECK( esp_wifi_start() );
|
||||
initialized = true;
|
||||
led_blink(LED_GREEN, 250, 250);
|
||||
}
|
||||
|
||||
static bool wifi_join(const char *ssid, const char *pass, int timeout_ms)
|
||||
{
|
||||
initialise_wifi();
|
||||
wifi_config_t wifi_config = { 0 };
|
||||
strncpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
|
||||
if (pass) {
|
||||
strncpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
|
||||
ESP_ERROR_CHECK( esp_wifi_connect() );
|
||||
|
||||
int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
|
||||
pdFALSE, pdTRUE, timeout_ms / portTICK_PERIOD_MS);
|
||||
return (bits & CONNECTED_BIT) != 0;
|
||||
}
|
||||
|
||||
|
||||
static int set_auto_connect(int argc, char **argv)
|
||||
{
|
||||
// int nerrors = arg_parse(argc, argv, (void **) &join_args);
|
||||
// if (nerrors != 0) {
|
||||
// arg_print_errors(stderr, join_args.end, argv[0]);
|
||||
// return 1;
|
||||
// }
|
||||
// ESP_LOGI(__func__, "Connecting to '%s'",
|
||||
// join_args.ssid->sval[0]);
|
||||
//
|
||||
// /* set default value*/
|
||||
// if (join_args.timeout->count == 0) {
|
||||
// join_args.timeout->ival[0] = JOIN_TIMEOUT_MS;
|
||||
// }
|
||||
//
|
||||
// bool connected = wifi_join(join_args.ssid->sval[0],
|
||||
// join_args.password->sval[0],
|
||||
// join_args.timeout->ival[0]);
|
||||
// if (!connected) {
|
||||
// ESP_LOGW(__func__, "Connection timed out");
|
||||
// return 1;
|
||||
// }
|
||||
// ESP_LOGI(__func__, "Connected");
|
||||
return 0;
|
||||
}
|
||||
static int connect(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **) &join_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, join_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
ESP_LOGI(__func__, "Connecting to '%s'",
|
||||
join_args.ssid->sval[0]);
|
||||
|
||||
/* set default value*/
|
||||
if (join_args.timeout->count == 0) {
|
||||
join_args.timeout->ival[0] = JOIN_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
bool connected = wifi_join(join_args.ssid->sval[0],
|
||||
join_args.password->sval[0],
|
||||
join_args.timeout->ival[0]);
|
||||
if (!connected) {
|
||||
ESP_LOGW(__func__, "Connection timed out");
|
||||
return 1;
|
||||
}
|
||||
ESP_LOGI(__func__, "Connected");
|
||||
return 0;
|
||||
}
|
||||
void register_wifi_join()
|
||||
{
|
||||
join_args.timeout = arg_int0(NULL, "timeout", "<t>", "Connection timeout, ms");
|
||||
join_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
|
||||
join_args.password = arg_str0(NULL, NULL, "<pass>", "PSK of AP");
|
||||
join_args.end = arg_end(2);
|
||||
|
||||
const esp_console_cmd_t join_cmd = {
|
||||
.command = "join",
|
||||
.help = "Join WiFi AP as a station",
|
||||
.hint = NULL,
|
||||
.func = &connect,
|
||||
.argtable = &join_args
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&join_cmd) );
|
||||
}
|
||||
|
||||
void register_wifi()
|
||||
{
|
||||
register_wifi_join();
|
||||
initialise_wifi();
|
||||
}
|
||||
|
||||
// cmd_wifi has been replaced by wifi-manager
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Register WiFi functions
|
||||
void register_wifi();
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -145,12 +145,9 @@ void process_autoexec(){
|
||||
{
|
||||
ESP_LOGD(TAG,"No matching command found for name autoexec. Adding default entries");
|
||||
uint8_t autoexec_dft=0;
|
||||
char autoexec1_dft[64];
|
||||
char autoexec2_dft[256]="squeezelite -o \"I2S\" -b 500:2000 -d all=info -M esp32";
|
||||
snprintf(autoexec1_dft, 64, "join %s %s", CONFIG_WIFI_SSID, CONFIG_WIFI_PASSWORD);
|
||||
char autoexec1_dft[256]="squeezelite -o I2S -b 500:2000 -d all=info -M esp32";
|
||||
store_nvs_value(NVS_TYPE_U8,"autoexec",&autoexec_dft);
|
||||
store_nvs_value(NVS_TYPE_STR,"autoexec1",autoexec1_dft);
|
||||
store_nvs_value(NVS_TYPE_STR,"autoexec2",autoexec2_dft);
|
||||
}
|
||||
}
|
||||
static void initialize_filesystem() {
|
||||
@@ -237,7 +234,6 @@ void console_start() {
|
||||
/* Register commands */
|
||||
esp_console_register_help_command();
|
||||
register_system();
|
||||
register_wifi();
|
||||
register_nvs();
|
||||
register_squeezelite();
|
||||
register_i2ctools();
|
||||
|
||||
@@ -20,6 +20,33 @@
|
||||
*/
|
||||
#include "platform_esp32.h"
|
||||
#include "led.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_event_loop.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "mdns.h"
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/netdb.h"
|
||||
|
||||
#include "http_server.h"
|
||||
#include "wifi_manager.h"
|
||||
static EventGroupHandle_t wifi_event_group;
|
||||
const int CONNECTED_BIT = BIT0;
|
||||
#define JOIN_TIMEOUT_MS (10000)
|
||||
|
||||
static const char TAG[] = "esp_app_main";
|
||||
|
||||
|
||||
#ifdef CONFIG_SQUEEZEAMP
|
||||
#define LED_GREEN_GPIO 12
|
||||
@@ -29,10 +56,48 @@
|
||||
#define LED_RED_GPIO 0
|
||||
#endif
|
||||
|
||||
/* brief this is an exemple of a callback that you can setup in your own app to get notified of wifi manager event */
|
||||
void cb_connection_got_ip(void *pvParameter){
|
||||
ESP_LOGI(TAG, "I have a connection!");
|
||||
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
|
||||
led_unpush(LED_GREEN);
|
||||
}
|
||||
void cb_connection_sta_disconnected(void *pvParameter){
|
||||
led_blink_pushed(LED_GREEN, 250, 250);
|
||||
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
|
||||
}
|
||||
bool wait_for_wifi(){
|
||||
bool connected=(xEventGroupGetBits(wifi_event_group) & CONNECTED_BIT)!=0;
|
||||
if(!connected){
|
||||
ESP_LOGD(TAG,"Waiting for WiFi...");
|
||||
connected = (xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
|
||||
pdFALSE, pdTRUE, JOIN_TIMEOUT_MS / portTICK_PERIOD_MS)& CONNECTED_BIT)!=0;
|
||||
if(!connected){
|
||||
ESP_LOGW(TAG,"wifi timeout.");
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG,"WiFi Connected!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return connected;
|
||||
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
led_config(LED_GREEN, LED_GREEN_GPIO, 0);
|
||||
led_config(LED_RED, LED_RED_GPIO, 0);
|
||||
wifi_event_group = xEventGroupCreate();
|
||||
|
||||
/* start the wifi manager */
|
||||
led_blink(LED_GREEN, 250, 250);
|
||||
wifi_manager_start();
|
||||
wifi_manager_set_callback(EVENT_STA_GOT_IP, &cb_connection_got_ip);
|
||||
wifi_manager_set_callback(WIFI_EVENT_STA_DISCONNECTED, &cb_connection_sta_disconnected);
|
||||
|
||||
|
||||
console_start();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 2M,
|
||||
factory, app, factory, 0x10000, 3M,
|
||||
storage, data, fat, , 819200,
|
||||
coredump, data, coredump,, 64K
|
||||
|
@@ -77,10 +77,12 @@ CONFIG_SPIRAM_SIZE=-1
|
||||
CONFIG_SPIRAM_SPEED_80M=y
|
||||
CONFIG_SPIRAM_MEMTEST=y
|
||||
CONFIG_SPIRAM_CACHE_WORKAROUND=y
|
||||
|
||||
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=256
|
||||
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=65536
|
||||
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
|
||||
CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y
|
||||
|
||||
CONFIG_SPIRAM_OCCUPY_VSPI_HOST=y
|
||||
CONFIG_SPIRAM_BANKSWITCH_ENABLE=n
|
||||
CONFIG_D0WD_PSRAM_CLK_IO=17
|
||||
@@ -114,7 +116,6 @@ CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20
|
||||
CONFIG_ESP32_PHY_MAX_TX_POWER=20
|
||||
CONFIG_FREERTOS_HZ=100
|
||||
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32
|
||||
CONFIG_LWIP_UDP_RECVMBOX_SIZE=32
|
||||
CONFIG_LWIP_NETIF_LOOPBACK=y
|
||||
CONFIG_LWIP_TCP_MSL=60000
|
||||
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=8192
|
||||
@@ -131,3 +132,10 @@ CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread"
|
||||
CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y
|
||||
CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y
|
||||
|
||||
# wifi-manager
|
||||
CONFIG_DEFAULT_AP_SSID="squeezelite"
|
||||
CONFIG_DEFAULT_AP_PASSWORD="squeezelite"
|
||||
CONFIG_DEFAULT_AP_IP="192.168.4.1"
|
||||
CONFIG_DEFAULT_AP_GATEWAY="192.168.4.1"
|
||||
CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
|
||||
CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info"
|
||||
|
||||