Merge remote-tracking branch 'origin/httpd' into master-cmake

Conflicts:
	.cproject
	.gitmodules
	.project
	.pydevproject
	.settings/language.settings.xml
	.settings/org.eclipse.cdt.core.prefs
	components/cmd_i2c/CMakeLists.txt
	components/cmd_i2c/cmd_i2ctools.c
	components/cmd_i2c/component.mk
	components/cmd_nvs/cmd_nvs.c
	components/cmd_nvs/component.mk
	components/cmd_system/cmd_system.c
	components/cmd_system/component.mk
	components/config/config.c
	components/config/config.h
	components/config/nvs_utilities.c
	components/display/CMakeLists.txt
	components/driver_bt/CMakeLists.txt
	components/driver_bt/component.mk
	components/raop/raop.c
	components/services/CMakeLists.txt
	components/squeezelite-ota/cmd_ota.c
	components/squeezelite-ota/squeezelite-ota.c
	components/squeezelite-ota/squeezelite-ota.h
	components/squeezelite/component.mk
	components/telnet/CMakeLists.txt
	components/wifi-manager/CMakeLists.txt
	components/wifi-manager/dns_server.c
	components/wifi-manager/http_server.c
	components/wifi-manager/http_server.h
	components/wifi-manager/wifi_manager.c
	components/wifi-manager/wifi_manager.h
	main/CMakeLists.txt
	main/console.c
	main/esp_app_main.c
	main/platform_esp32.h
This commit is contained in:
Sebastien
2020-03-10 13:55:22 -04:00
53 changed files with 4302 additions and 1431 deletions

3
.gitignore vendored
View File

@@ -66,3 +66,6 @@ libs/
/cdump.cmd
/_*
squeezelite-esp32-jsonblob.zip
/flash_cmd.txt
/writeSequeezeEsp.bat
/writeSequeezeEsp.sh

4
.gitmodules vendored
View File

@@ -2,6 +2,6 @@
path = components/telnet/libtelnet
url = https://github.com/seanmiddleditch/libtelnet
branch = develop
[submodule "esp-dsp"]
[submodule "components/esp-dsp"]
path = components/esp-dsp
url = https://github.com/philippe44/esp-dsp
url = https://github.com/philippe44/esp-dsp.git

View File

@@ -1,8 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
</pydev_project>

View File

@@ -0,0 +1,2 @@
eclipse.preferences.version=1
suppressions=rO0ABXNyAAxqYXZhLmlvLkZpbGUELaRFDg3k/wMAAUwABHBhdGh0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAJWNvbXBvbmVudHNcd2lmaS1tYW5hZ2VyXGh0dHBfc2VydmVyLmN3AgBceA\=\=;comparePointers;395\!rO0ABXNyAAxqYXZhLmlvLkZpbGUELaRFDg3k/wMAAUwABHBhdGh0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAE21haW5cZXNwX2FwcF9tYWluLmN3AgBceA\=\=;comparePointers;176\!rO0ABXNyAAxqYXZhLmlvLkZpbGUELaRFDg3k/wMAAUwABHBhdGh0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAJ2NvbXBvbmVudHNcdGVsbmV0XGxpYnRlbG5ldFxsaWJ0ZWxuZXQuY3cCAFx4;va_list_usedBeforeStarted;2147483647\!

View File

@@ -0,0 +1,89 @@
eclipse.preferences.version=1
org.eclipse.cdt.codan.checkers.errnoreturn=Warning
org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No return\\")",implicit\=>false}
org.eclipse.cdt.codan.checkers.errreturnvalue=Error
org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused return value\\")"}
org.eclipse.cdt.codan.checkers.nocommentinside=-Error
org.eclipse.cdt.codan.checkers.nocommentinside.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Nesting comments\\")"}
org.eclipse.cdt.codan.checkers.nolinecomment=-Error
org.eclipse.cdt.codan.checkers.nolinecomment.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Line comments\\")"}
org.eclipse.cdt.codan.checkers.noreturn=Error
org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No return value\\")",implicit\=>false}
org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error
org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Abstract class cannot be instantiated\\")"}
org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error
org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Ambiguous problem\\")"}
org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning
org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Assignment in condition\\")"}
org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error
org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Assignment to itself\\")"}
org.eclipse.cdt.codan.internal.checkers.CStyleCastProblem=-Warning
org.eclipse.cdt.codan.internal.checkers.CStyleCastProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"C-Style cast instead of C++ cast\\")"}
org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning
org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No break at end of case\\")",no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false,enable_fallthrough_quickfix_param\=>false}
org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning
org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Catching by reference is recommended\\")",unknown\=>false,exceptions\=>()}
org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error
org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Circular inheritance\\")"}
org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning
org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Class members should be properly initialized\\")",skip\=>true}
org.eclipse.cdt.codan.internal.checkers.CopyrightProblem=-Warning
org.eclipse.cdt.codan.internal.checkers.CopyrightProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Lack of copyright information\\")",regex\=>".*Copyright.*"}
org.eclipse.cdt.codan.internal.checkers.DecltypeAutoProblem=Error
org.eclipse.cdt.codan.internal.checkers.DecltypeAutoProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid 'decltype(auto)' specifier\\")"}
org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Field cannot be resolved\\")"}
org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Function cannot be resolved\\")"}
org.eclipse.cdt.codan.internal.checkers.GotoStatementProblem=-Warning
org.eclipse.cdt.codan.internal.checkers.GotoStatementProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Goto statement used\\")"}
org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error
org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid arguments\\")"}
org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error
org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid template argument\\")"}
org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error
org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Label statement not found\\")"}
org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error
org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Member declaration not found\\")"}
org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Method cannot be resolved\\")"}
org.eclipse.cdt.codan.internal.checkers.MissCaseProblem=-Warning
org.eclipse.cdt.codan.internal.checkers.MissCaseProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Missing cases in switch\\")"}
org.eclipse.cdt.codan.internal.checkers.MissDefaultProblem=-Warning
org.eclipse.cdt.codan.internal.checkers.MissDefaultProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Missing default in switch\\")",defaultWithAllEnums\=>false}
org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info
org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Name convention for function\\")",pattern\=>"^[a-z]",macro\=>true,exceptions\=>()}
org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning
org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Class has a virtual method and non-virtual destructor\\")"}
org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error
org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid overload\\")"}
org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error
org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid redeclaration\\")"}
org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error
org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid redefinition\\")"}
org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning
org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Return with parenthesis\\")"}
org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning
org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Format String Vulnerability\\")"}
org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning
org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Statement has no effect\\")",macro\=>true,exceptions\=>()}
org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning
org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Suggested parenthesis around expression\\")",paramNot\=>false}
org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning
org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Suspicious semicolon\\")",else\=>false,afterelse\=>false}
org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Type cannot be resolved\\")"}
org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning
org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused function declaration\\")",macro\=>true}
org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning
org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused static function\\")",macro\=>true}
org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning
org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused variable declaration in file scope\\")",macro\=>true,exceptions\=>("@(\#)","$Id")}
org.eclipse.cdt.codan.internal.checkers.UsingInHeaderProblem=-Warning
org.eclipse.cdt.codan.internal.checkers.UsingInHeaderProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Using directive in header\\")"}
org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Symbol is not resolved\\")"}
org.eclipse.cdt.codan.internal.checkers.VirtualMethodCallProblem=-Error
org.eclipse.cdt.codan.internal.checkers.VirtualMethodCallProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Virtual method call in constructor/destructor\\")"}
org.eclipse.cdt.qt.core.qtproblem=Warning
org.eclipse.cdt.qt.core.qtproblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_ON_FILE_OPEN\=>true,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>null}

View File

@@ -10,9 +10,13 @@
#recovery: PROJECT_NAME:=recovery.$(PROJECT_CONFIG_TARGET)
#recovery: CPPFLAGS+=-DRECOVERY_APPLICATION=1
#recovery: EXTRA_CPPFLAGS+=-DRECOVERY_APPLICATION=1
PROJECT_NAME?=squeezelite
EXTRA_COMPONENT_DIRS := esp-dsp
EXTRA_CPPFLAGS+= -I$(PROJECT_PATH)/main
#/-Wno-error=maybe-uninitialized
include $(IDF_PATH)/make/project.mk
CPPFLAGS+= -Wno-error=maybe-uninitialized
# for future gcc version, this could be needed: CPPFLAGS+= -Wno-error=format-overflow -Wno-error=stringop-truncation

View File

@@ -512,7 +512,7 @@ CONFIG_ESP_EVENT_POST_FROM_ISR=y
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
CONFIG_HTTPD_MAX_REQ_HDR_LEN=512
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_MAX_URI_LEN=512
CONFIG_HTTPD_ERR_RESP_NO_DELAY=y
CONFIG_HTTPD_PURGE_BUF_LEN=32
@@ -596,6 +596,7 @@ CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF
CONFIG_FREERTOS_CORETIMER_0=y
CONFIG_FREERTOS_HZ=100
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y

View File

@@ -512,7 +512,7 @@ CONFIG_ESP_EVENT_POST_FROM_ISR=y
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
CONFIG_HTTPD_MAX_REQ_HDR_LEN=512
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_MAX_URI_LEN=512
CONFIG_HTTPD_ERR_RESP_NO_DELAY=y
CONFIG_HTTPD_PURGE_BUF_LEN=32
@@ -594,7 +594,7 @@ CONFIG_FMB_TIMER_INDEX=0
CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF
CONFIG_FREERTOS_CORETIMER_0=y
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
CONFIG_FREERTOS_HZ=100
CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y

View File

@@ -513,7 +513,7 @@ CONFIG_ESP_EVENT_POST_FROM_ISR=y
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
CONFIG_HTTPD_MAX_REQ_HDR_LEN=512
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_MAX_URI_LEN=512
CONFIG_HTTPD_ERR_RESP_NO_DELAY=y
CONFIG_HTTPD_PURGE_BUF_LEN=32
@@ -597,6 +597,7 @@ CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF
CONFIG_FREERTOS_CORETIMER_0=y
CONFIG_FREERTOS_HZ=100
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y

View File

@@ -512,7 +512,7 @@ CONFIG_ESP_EVENT_POST_FROM_ISR=y
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
CONFIG_HTTPD_MAX_REQ_HDR_LEN=512
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_MAX_URI_LEN=512
CONFIG_HTTPD_ERR_RESP_NO_DELAY=y
CONFIG_HTTPD_PURGE_BUF_LEN=32
@@ -596,6 +596,7 @@ CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF
CONFIG_FREERTOS_CORETIMER_0=y
CONFIG_FREERTOS_HZ=100
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y

View File

@@ -512,7 +512,7 @@ CONFIG_ESP_EVENT_POST_FROM_ISR=y
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
CONFIG_HTTPD_MAX_REQ_HDR_LEN=512
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_MAX_URI_LEN=512
CONFIG_HTTPD_ERR_RESP_NO_DELAY=y
CONFIG_HTTPD_PURGE_BUF_LEN=32
@@ -596,6 +596,7 @@ CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF
CONFIG_FREERTOS_CORETIMER_0=y
CONFIG_FREERTOS_HZ=100
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y

View File

@@ -506,7 +506,7 @@ CONFIG_ESP_EVENT_POST_FROM_ISR=y
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
CONFIG_HTTPD_MAX_REQ_HDR_LEN=512
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_MAX_URI_LEN=512
CONFIG_HTTPD_ERR_RESP_NO_DELAY=y
CONFIG_HTTPD_PURGE_BUF_LEN=32
@@ -590,6 +590,7 @@ CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF
CONFIG_FREERTOS_CORETIMER_0=y
CONFIG_FREERTOS_HZ=100
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y

113
build_flash_cmd.sh Normal file
View File

@@ -0,0 +1,113 @@
#!/bin/bash
echo
echo =================================================================
echo Build flash command
echo =================================================================
# Location of partitions.csv relative to this script
partitionsCsv="../partitions.csv"
# File to output readme instructions to
outputReadme="./flash_cmd.txt"
# File to output bash script to
outputBashScript="./writeSequeezeEsp.sh"
# File to output bat script to
outputBatScript="./writeSequeezeEsp.bat"
# The name of partitions to ignore from partitions.csv
paritionsToIgnore=(
"nvs"
"phy_init"
"storage"
"coredump"
"settings"
)
# Function that maps partition name to actual bin file
# defaults to "[PARTION_NAME_FROM_CSV].bin"
function partitionNameToBinFile {
if [[ "$1" == "otadata" ]]; then
echo "ota_data_initial.bin"
elif [[ "$1" == "ota_0" || "$1" == "factory" ]]; then
echo "squeezelite.bin"
else
echo $1.bin
fi
}
# write parameters for esptool.py
writeParameters="$writeParameters write_flash"
writeParameters="$writeParameters --flash_mode dio --flash_freq 80m --flash_size detect"
# bootloader.bin and partitions.bin not in partitions.csv so manually add here
partitionsParameters=" 0x1000 bootloader/bootloader.bin"
partitionsParameters="$partitionsParameters 0x8000 partitions.bin"
# ==============================================================================
# Loop over partitions.csv and add partition bins and offsets to partitionsParameters
for line in $($IDF_PATH/components/partition_table/gen_esp32part.py --quiet build/partitions.bin | grep '^[^#]')
do
partitionName=$(echo $line | awk -F',' '{printf "%s", $1}' )
partitionOffset=$(echo $line |awk -F',' '{printf "%s", $4}' )
partitionFile=$(partitionNameToBinFile $partitionName)
if [[ " ${paritionsToIgnore[@]} " =~ " ${partitionName} " ]]; then
continue
fi
partitionsParameters="$partitionsParameters $partitionOffset $partitionFile"
echo "$partitionsParameters"
done
# Write README Instructions
if [ ! -f "$outputReadme" ]; then
touch $outputReadme
fi
echo "" >> $outputReadme
echo "====LINUX====" >> $outputReadme
echo "To flash sequeezelite run the following script:" >> $outputReadme
echo "$outputBashScript [PORT_HERE] [BAUD_RATE]" >> $outputReadme
echo "e.g. $outputBashScript /dev/ttyUSB0 115200" >> $outputReadme
echo "" >> $outputReadme
echo "====WINDOWS====" >> $outputReadme
echo "To flash sequeezelite run the following script:" >> $outputReadme
echo "$outputBatScript [PORT_HERE] [BAUD_RATE]" >> $outputReadme
echo "e.g. $outputBatScript COM11 115200" >> $outputReadme
echo "" >> $outputReadme
echo "If you don't know how to run the BAT file with arguments then you can" >> $outputReadme
echo "edit the bat file in Notepad. Open the file up and edit the following:" >> $outputReadme
echo "Change 'set port=%1' to 'set port=[PORT_HERE]'. E.g. 'set port=COM11'" >> $outputReadme
echo "Change 'set baud=%2' to 'set baud=[BAUD_RATE]'. E.g. 'set baud=115200'" >> $outputReadme
echo "" >> $outputReadme
echo "====MANUAL====" >> $outputReadme
echo "Python esptool.py --port [PORT_HERE] --baud [BAUD_RATE] $writeParameters $partitionsParameters" >> $outputReadme
# Write Linux BASH File
if [ ! -f "$outputBashScript" ]; then
touch $outputBashScript
fi
echo "#!/bin/bash" >> $outputBashScript
echo >> $outputBashScript
echo "port=\$1" >> $outputBashScript
echo "baud=\$2" >> $outputBashScript
linuxFlashCommand="Python esptool.py --port \$port --baud \$baud"
echo "$linuxFlashCommand $writeParameters $partitionsParameters" >> $outputBashScript
# Write Windows BAT File
if [ ! -f "$outputBatScript" ]; then
touch $outputBatScript
fi
echo "echo off" >> $outputBatScript
echo "" >> $outputBatScript
echo "set port=%1" >> $outputBatScript
echo "set baud=%2" >> $outputBatScript
windowsFlashCommand="Python esptool.py --port %port% --baud %baud%"
echo "$windowsFlashCommand $writeParameters $partitionsParameters" >> $outputBatScript

View File

@@ -21,6 +21,6 @@ COMPONENT_ADD_LDFLAGS=-l$(COMPONENT_NAME) \
#$(COMPONENT_PATH)/lib/libesp-tremor.a
#$(COMPONENT_PATH)/lib/libesp-ogg-container.a
COMPONENT_ADD_INCLUDEDIRS := /inc

View File

@@ -559,7 +559,7 @@ void config_delete_key(const char *key){
ESP_LOGD(TAG, "Deleting nvs entry for [%s]", key);
if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){
ESP_LOGE(TAG, "Unable to lock config for delete");
return false;
return ;
}
esp_err_t err = nvs_open_from_partition(settings_partition, current_namespace, NVS_READWRITE, &nvs);
if (err == ESP_OK) {
@@ -663,7 +663,7 @@ char * config_alloc_get_json(bool bFormatted){
config_unlock();
return json_buffer;
}
esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, void * value){
esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, const void * value){
esp_err_t result = ESP_OK;
if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){
ESP_LOGE(TAG, "Unable to lock config after %d ms",LOCK_MAX_WAIT);

View File

@@ -3,6 +3,7 @@
#include <string.h>
#include "nvs.h"
#include "assert.h"
#include "cJSON.h"
#ifdef __cplusplus
extern "C" {
@@ -12,7 +13,9 @@ extern "C" {
#endif
#define DECLARE_SET_DEFAULT(t) void config_set_default_## t (const char *key, t value);
#define DECLARE_GET_NUM(t) esp_err_t config_get_## t (const char *key, t * value);
#ifndef FREE_RESET
#define FREE_RESET(p) if(p!=NULL) { free(p); p=NULL; }
#endif
DECLARE_SET_DEFAULT(uint8_t);
DECLARE_SET_DEFAULT(uint16_t);
@@ -37,5 +40,7 @@ void config_set_default(nvs_type_t type, const char *key, void * default_value,
void * config_alloc_get(nvs_type_t nvs_type, const char *key) ;
bool wait_for_commit();
char * config_alloc_get_json(bool bFormatted);
esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, void * value);
esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, const void * value);
nvs_type_t config_get_item_type(cJSON * entry);
void * config_safe_alloc_get_entry_value(nvs_type_t nvs_type, cJSON * entry);

View File

@@ -29,6 +29,7 @@
#include "platform_config.h"
#include "esp_sleep.h"
#include "driver/uart.h" // for the uart driver access
#include "messaging.h"
#ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS
#define WITH_TASKS_INFO 1
@@ -105,8 +106,8 @@ if(is_recovery_running){
if(!wait_for_commit()){
ESP_LOGW(TAG,"Unable to commit configuration. ");
}
ESP_LOGW(TAG, "Restarting after tx complete");
uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS);
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return ESP_OK;
}
@@ -117,8 +118,8 @@ else {
if(!wait_for_commit()){
ESP_LOGW(TAG,"Unable to commit configuration. ");
}
ESP_LOGW(TAG, "Restarting after tx complete");
uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS);
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return ESP_OK;
}
@@ -131,7 +132,7 @@ else {
if(it == NULL){
ESP_LOGE(TAG,"Unable initialize partition iterator!");
set_status_message(ERROR, "Reboot failed. Cannot iterate through partitions");
messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Reboot failed. Cannot iterate through partitions");
}
else
{
@@ -145,18 +146,19 @@ else {
if(err!=ESP_OK){
ESP_LOGE(TAG,"Unable to set partition as active for next boot. %s",esp_err_to_name(err));
bFound=false;
set_status_message(ERROR, "Unable to select partition for reboot.");
messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Unable to select partition for reboot.");
}
else{
ESP_LOGW(TAG, "Application partition %s sub type %u is selected for boot", partition->label,partition_subtype);
bFound=true;
set_status_message(WARNING, "Rebooting!");
messaging_post_message(MESSAGING_WARNING,MESSAGING_CLASS_SYSTEM,"Reboot failed. Cannot iterate through partitions");
}
}
else
{
ESP_LOGE(TAG,"partition type %u not found! Unable to reboot to recovery.",partition_subtype);
set_status_message(ERROR, "Partition not found.");
messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"partition type %u not found! Unable to reboot to recovery.",partition_subtype);
}
ESP_LOGD(TAG, "Yielding to other processes");
taskYIELD();
@@ -165,8 +167,7 @@ else {
if(!wait_for_commit()){
ESP_LOGW(TAG,"Unable to commit configuration. ");
}
ESP_LOGW(TAG, "Restarting after tx complete");
uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS);
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
}
}
@@ -180,8 +181,7 @@ static int restart(int argc, char **argv)
if(!wait_for_commit()){
ESP_LOGW(TAG,"Unable to commit configuration. ");
}
ESP_LOGW(TAG, "Restarting after tx complete");
uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS);
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return 0;
}
@@ -193,8 +193,8 @@ void simple_restart()
ESP_LOGW(TAG,"Unable to commit configuration. ");
}
ESP_LOGW(TAG, "Restarting after tx complete");
uart_wait_tx_done(UART_NUM_1, 500 / portTICK_RATE_MS);
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
}

View File

@@ -28,6 +28,15 @@
#include "wifi_manager.h"
#include "platform_config.h"
#include "telnet.h"
#include "gds.h"
#include "gds_default_if.h"
#include "gds_draw.h"
#include "gds_text.h"
#include "gds_font.h"
#include "display.h"
#include "cmd_squeezelite.h"
#include "config.h"
pthread_t thread_console;
static void * console_thread();
void console_start();
@@ -152,9 +161,24 @@ void initialize_console() {
}
void console_start() {
initialize_console();
if(is_recovery_running){
GDS_ClearExt(display, true);
GDS_SetFont(display, &Font_droid_sans_fallback_15x17 );
GDS_TextPos(display, GDS_FONT_MEDIUM, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "RECOVERY");
}
if(!is_serial_suppressed()){
initialize_console();
}
else {
/* Initialize the console */
esp_console_config_t console_config = { .max_cmdline_args = 22,
.max_cmdline_length = 600,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
#endif
};
ESP_ERROR_CHECK(esp_console_init(&console_config));
}
/* Register commands */
esp_console_register_help_command();
register_system();
@@ -167,53 +191,59 @@ void console_start() {
register_ota_cmd();
}
register_i2ctools();
printf("\n");
if(is_recovery_running){
printf("****************************************************************\n"
"RECOVERY APPLICATION\n"
"This mode is used to flash Squeezelite into the OTA partition\n"
"****\n\n");
}
printf("Type 'help' to get the list of commands.\n"
"Use UP/DOWN arrows to navigate through command history.\n"
"Press TAB when typing command name to auto-complete.\n"
"\n");
if(!is_recovery_running){
printf("To automatically execute lines at startup:\n"
"\tSet NVS variable autoexec (U8) = 1 to enable, 0 to disable automatic execution.\n"
"\tSet NVS variable autoexec[1~9] (string)to a command that should be executed automatically\n");
}
printf("\n\n");
if(!is_serial_suppressed()){
printf("\n");
if(is_recovery_running){
printf("****************************************************************\n"
"RECOVERY APPLICATION\n"
"This mode is used to flash Squeezelite into the OTA partition\n"
"****\n\n");
}
printf("Type 'help' to get the list of commands.\n"
"Use UP/DOWN arrows to navigate through command history.\n"
"Press TAB when typing command name to auto-complete.\n"
"\n");
if(!is_recovery_running){
printf("To automatically execute lines at startup:\n"
"\tSet NVS variable autoexec (U8) = 1 to enable, 0 to disable automatic execution.\n"
"\tSet NVS variable autoexec[1~9] (string)to a command that should be executed automatically\n");
}
printf("\n\n");
/* Figure out if the terminal supports escape sequences */
int probe_status = linenoiseProbe();
if (probe_status) { /* zero indicates success */
printf("\n****************************\n"
"Your terminal application does not support escape sequences.\n"
"Line editing and history features are disabled.\n"
"On Windows, try using Putty instead.\n"
"****************************\n");
linenoiseSetDumbMode(1);
#if CONFIG_LOG_COLORS
/* Since the terminal doesn't support escape sequences,
* don't use color codes in the prompt.
*/
prompt = "squeezelite-esp32> ";
#endif //CONFIG_LOG_COLORS
/* Figure out if the terminal supports escape sequences */
int probe_status = linenoiseProbe();
if (probe_status) { /* zero indicates success */
printf("\n****************************\n"
"Your terminal application does not support escape sequences.\n"
"Line editing and history features are disabled.\n"
"On Windows, try using Putty instead.\n"
"****************************\n");
linenoiseSetDumbMode(1);
#if CONFIG_LOG_COLORS
/* Since the terminal doesn't support escape sequences,
* don't use color codes in the prompt.
*/
prompt = "squeezelite-esp32> ";
#endif //CONFIG_LOG_COLORS
}
esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
cfg.thread_name= "console";
cfg.inherit_cfg = true;
if(is_recovery_running){
cfg.stack_size = 4096 ;
}
esp_pthread_set_cfg(&cfg);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&thread_console, &attr, console_thread, NULL);
pthread_attr_destroy(&attr);
}
else if(!is_recovery_running){
process_autoexec();
}
esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
cfg.thread_name= "console";
cfg.inherit_cfg = true;
if(is_recovery_running){
cfg.stack_size = 4096 ;
}
esp_pthread_set_cfg(&cfg);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&thread_console, &attr, console_thread, NULL);
pthread_attr_destroy(&attr);
}
void run_command(char * line){
/* Try to run the command */
@@ -221,15 +251,16 @@ void run_command(char * line){
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG,"Unrecognized command: %s\n", line);
ESP_LOGE(TAG,"Unrecognized command: %s", line);
} else if (err == ESP_ERR_INVALID_ARG) {
// command was empty
} else if (err == ESP_OK && ret != ESP_OK) {
ESP_LOGW(TAG,"Command returned non-zero error code: 0x%x (%s)\n", ret,
ESP_LOGW(TAG,"Command returned non-zero error code: 0x%x (%s)", ret,
esp_err_to_name(err));
} else if (err != ESP_OK) {
ESP_LOGE(TAG,"Internal error: %s\n", esp_err_to_name(err));
ESP_LOGE(TAG,"Internal error: %s", esp_err_to_name(err));
}
}
static void * console_thread() {
if(!is_recovery_running){

View File

@@ -7,7 +7,9 @@
# please read the SDK documents if you need to do this.
#
CFLAGS += -fstack-usage \
-I$(COMPONENT_PATH)/../tools \
-I$(COMPONENT_PATH)/../codecs/inc/alac \
CFLAGS += -fstack-usage\
-I$(PROJECT_PATH)/components/tools \
-I$(PROJECT_PATH)/components/codecs/inc/alac \
-I$(PROJECT_PATH)/main/
COMPONENT_ADD_INCLUDEDIRS := .
COMPONENT_SRCDIRS := .

View File

@@ -957,5 +957,4 @@ static void on_dmap_string(void *ctx, const char *code, const char *name, const
return q - (unsigned char *) data;
}
/*----------------------------------------------------------------------------*/
static void on_dmap_string(void *ctx, const char *code, const char *name, const char *buf, size_t len) {
/*----------------------------------------------------------------------------*/

File diff suppressed because it is too large Load Diff

View File

@@ -588,7 +588,8 @@ void free_metadata(struct metadata_s *metadata)
NFREE(metadata->remote_title);
}
/*----------------------------------------------------------------------------*/
int _fprintf(FILE *file, ...)
{

View File

@@ -18,7 +18,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

View File

@@ -9,3 +9,4 @@
COMPONENT_SRCDIRS := .
COMPONENT_ADD_INCLUDEDIRS := .
CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG

View File

@@ -0,0 +1,230 @@
/**
*
*/
#include <stdlib.h> // Required for libtelnet.h
#include <esp_log.h>
#include "stdbool.h"
#include <lwip/def.h>
#include <lwip/sockets.h>
#include <errno.h>
#include <string.h>
#include "esp_app_trace.h"
#include "esp_attr.h"
#include "config.h"
#include "nvs_utilities.h"
#include "platform_esp32.h"
#include "messaging.h"
#include "trace.h"
/************************************
* Globals
*/
const static char tag[] = "messaging";
typedef struct {
struct messaging_list_t * next;
char * subscriber_name;
size_t max_count;
RingbufHandle_t buf_handle;
} messaging_list_t;
static messaging_list_t top;
#define MSG_LENGTH_AVG 1024
messaging_list_t * get_struct_ptr(messaging_handle_t handle){
return (messaging_list_t *)handle;
}
messaging_handle_t get_handle_ptr(messaging_list_t * handle){
return (messaging_handle_t )handle;
}
RingbufHandle_t messaging_create_ring_buffer(uint8_t max_count){
RingbufHandle_t buf_handle = NULL;
StaticRingbuffer_t *buffer_struct = malloc(sizeof(StaticRingbuffer_t));
if (buffer_struct != NULL) {
size_t buf_size = (size_t )(sizeof(single_message_t)+8+MSG_LENGTH_AVG)*(size_t )(max_count>0?max_count:5); // no-split buffer requires an additional 8 bytes
buf_size = buf_size - (buf_size % 4);
uint8_t *buffer_storage = (uint8_t *)heap_caps_malloc(buf_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_32BIT);
if (buffer_storage== NULL) {
ESP_LOGE(tag,"buff alloc failed");
}
else {
buf_handle = xRingbufferCreateStatic(buf_size, RINGBUF_TYPE_NOSPLIT, buffer_storage, buffer_struct);
}
}
else {
ESP_LOGE(tag,"ringbuf alloc failed");
}
return buf_handle;
}
void messaging_fill_messages(messaging_list_t * target_subscriber){
single_message_t * message=NULL;
UBaseType_t uxItemsWaiting;
vRingbufferGetInfo(top.buf_handle, NULL, NULL, NULL, NULL, &uxItemsWaiting);
for(size_t i=0;i<uxItemsWaiting;i++){
message= messaging_retrieve_message(top.buf_handle);
if(message){
//re-post to original queue so it is available to future subscribers
messaging_post_to_queue(get_handle_ptr(&top), message, message->msg_size);
// post to new subscriber
messaging_post_to_queue(get_handle_ptr(target_subscriber) , message, message->msg_size);
FREE_AND_NULL(message);
}
}
}
messaging_handle_t messaging_register_subscriber(uint8_t max_count, char * name){
messaging_list_t * cur=&top;
while(cur->next){
cur = get_struct_ptr(cur->next);
}
cur->next=heap_caps_malloc(sizeof(messaging_list_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if(!cur->next){
ESP_LOGE(tag,"subscriber alloc failed");
return NULL;
}
memset(cur->next,0x00,sizeof(messaging_list_t));
cur = get_struct_ptr(cur->next);
cur->max_count=max_count;
cur->subscriber_name=strdup(name);
cur->buf_handle = messaging_create_ring_buffer(max_count);
if(cur->buf_handle){
messaging_fill_messages(cur);
}
return cur->buf_handle;
}
void messaging_service_init(){
size_t max_count=15;
top.buf_handle = messaging_create_ring_buffer(max_count);
if(!top.buf_handle){
ESP_LOGE(tag, "messaging service init failed.");
}
else {
top.max_count = max_count;
top.subscriber_name = strdup("messaging");
}
return;
}
const char * messaging_get_type_desc(messaging_types msg_type){
switch (msg_type) {
CASE_TO_STR(MESSAGING_INFO);
CASE_TO_STR(MESSAGING_WARNING);
CASE_TO_STR(MESSAGING_ERROR);
default:
return "Unknown";
break;
}
}
const char * messaging_get_class_desc(messaging_classes msg_class){
switch (msg_class) {
CASE_TO_STR(MESSAGING_CLASS_OTA);
CASE_TO_STR(MESSAGING_CLASS_SYSTEM);
CASE_TO_STR(MESSAGING_CLASS_STATS);
default:
return "Unknown";
break;
}
}
cJSON * messaging_retrieve_messages(RingbufHandle_t buf_handle){
single_message_t * message=NULL;
cJSON * json_messages=cJSON_CreateArray();
cJSON * json_message=NULL;
size_t item_size;
UBaseType_t uxItemsWaiting;
vRingbufferGetInfo(buf_handle, NULL, NULL, NULL, NULL, &uxItemsWaiting);
for(int i = 0;i<uxItemsWaiting;i++){
message = (single_message_t *)xRingbufferReceive(buf_handle, &item_size, pdMS_TO_TICKS(50));
//Check received data
if (message== NULL) {
ESP_LOGE(tag,"received null ptr");
}
else {
json_message = cJSON_CreateObject();
cJSON_AddStringToObject(json_message, "message", message->message);
vRingbufferReturnItem(buf_handle, (void *)message);
cJSON_AddStringToObject(json_message, "type", messaging_get_type_desc(message->type));
cJSON_AddStringToObject(json_message, "class", messaging_get_class_desc(message->msg_class));
cJSON_AddNumberToObject(json_message,"sent_time",message->sent_time);
cJSON_AddNumberToObject(json_message,"current_time",esp_timer_get_time() / 1000);
cJSON_AddItemToArray(json_messages,json_message);
}
}
return json_messages;
}
single_message_t * messaging_retrieve_message(RingbufHandle_t buf_handle){
single_message_t * message=NULL;
single_message_t * message_copy=NULL;
size_t item_size;
UBaseType_t uxItemsWaiting;
vRingbufferGetInfo(buf_handle, NULL, NULL, NULL, NULL, &uxItemsWaiting);
if(uxItemsWaiting>0){
message = (single_message_t *)xRingbufferReceive(buf_handle, &item_size, pdMS_TO_TICKS(50));
message_copy = heap_caps_malloc(item_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if(message_copy){
memcpy(message_copy,message,item_size);
}
vRingbufferReturnItem(buf_handle, (void *)message);
}
return message_copy;
}
esp_err_t messaging_post_to_queue(messaging_handle_t subscriber_handle, single_message_t * message, size_t message_size){
size_t item_size=0;
messaging_list_t * subscriber=get_struct_ptr(subscriber_handle);
if(!subscriber->buf_handle){
ESP_LOGE(tag,"post failed: null buffer for %s", str_or_unknown(subscriber->subscriber_name));
return ESP_FAIL;
}
void * pItem=NULL;
UBaseType_t res=pdFALSE;
while(1){
ESP_LOGD(tag,"Attempting to reserve %d bytes for %s",message_size, str_or_unknown(subscriber->subscriber_name));
res = xRingbufferSendAcquire(subscriber->buf_handle, &pItem, message_size, pdMS_TO_TICKS(50));
if(res == pdTRUE && pItem){
ESP_LOGD(tag,"Reserving complete for %s", str_or_unknown(subscriber->subscriber_name));
memcpy(pItem,message,message_size);
xRingbufferSendComplete(subscriber->buf_handle, pItem);
break;
}
ESP_LOGD(tag,"Dropping for %s",str_or_unknown(subscriber->subscriber_name));
single_message_t * dummy = (single_message_t *)xRingbufferReceive(subscriber->buf_handle, &item_size, pdMS_TO_TICKS(50));
if (dummy== NULL) {
ESP_LOGE(tag,"Dropping message failed");
break;
}
else {
ESP_LOGD(tag,"Dropping message of %d bytes for %s",item_size, str_or_unknown(subscriber->subscriber_name));
vRingbufferReturnItem(subscriber->buf_handle, (void *)dummy);
}
}
if (res != pdTRUE) {
ESP_LOGE(tag,"post to %s failed",str_or_unknown(subscriber->subscriber_name));
return ESP_FAIL;
}
return ESP_OK;
}
void messaging_post_message(messaging_types type,messaging_classes msg_class, char *fmt, ...){
single_message_t * message=NULL;
size_t msg_size=0;
size_t ln =0;
messaging_list_t * cur=&top;
va_list va;
va_start(va, fmt);
ln = vsnprintf(NULL, 0, fmt, va)+1;
msg_size = sizeof(single_message_t)+ln;
message = (single_message_t *)heap_caps_malloc(msg_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
vsprintf(message->message, fmt, va);
va_end(va);
message->msg_size = msg_size;
message->type = type;
message->msg_class = msg_class;
message->sent_time = esp_timer_get_time() / 1000;
ESP_LOGD(tag,"Post: %s",message->message);
while(cur){
messaging_post_to_queue(get_handle_ptr(cur), message, msg_size);
cur = get_struct_ptr(cur->next);
}
FREE_AND_NULL(message);
return;
}

View File

@@ -0,0 +1,32 @@
#include "sdkconfig.h"
#include "freertos/ringbuf.h"
#include "cJSON.h"
#pragma once
typedef enum {
MESSAGING_INFO,
MESSAGING_WARNING,
MESSAGING_ERROR
} messaging_types;
typedef enum {
MESSAGING_CLASS_OTA,
MESSAGING_CLASS_SYSTEM,
MESSAGING_CLASS_STATS
} messaging_classes;
typedef struct messaging_list_t *messaging_handle_t;
typedef struct {
time_t sent_time;
messaging_types type;
messaging_classes msg_class;
size_t msg_size;
char message[];
} single_message_t;
cJSON * messaging_retrieve_messages(RingbufHandle_t buf_handle);
messaging_handle_t messaging_register_subscriber(uint8_t max_count, char * name);
esp_err_t messaging_post_to_queue(messaging_handle_t subscriber_handle, single_message_t * message, size_t message_size);
void messaging_post_message(messaging_types type,messaging_classes msg_class, char * fmt, ...);
cJSON * messaging_retrieve_messages(RingbufHandle_t buf_handle);
single_message_t * messaging_retrieve_message(RingbufHandle_t buf_handle);
void messaging_service_init();

View File

@@ -21,6 +21,9 @@
#include "globdefs.h"
#include "platform_config.h"
#include "accessors.h"
#include "messaging.h"
#include "cJSON.h"
#include "trace.h"
#define MONITOR_TIMER (10*1000)
#define SCRATCH_SIZE 256
@@ -44,16 +47,17 @@ bool spkfault_svc(void);
/****************************************************************************************
*
*/
static void task_stats( void ) {
static void task_stats( cJSON* top ) {
#ifdef CONFIG_FREERTOS_USE_TRACE_FACILITY
static struct {
TaskStatus_t *tasks;
uint32_t total, n;
} current, previous;
cJSON * tlist=cJSON_CreateArray();
current.n = uxTaskGetNumberOfTasks();
current.tasks = malloc( current.n * sizeof( TaskStatus_t ) );
current.n = uxTaskGetSystemState( current.tasks, current.n, &current.total );
cJSON_AddNumberToObject(top,"ntasks",current.n);
static EXT_RAM_ATTR char scratch[SCRATCH_SIZE];
*scratch = '\0';
@@ -68,6 +72,15 @@ static void task_stats( void ) {
current.tasks[i].eCurrentState,
100 * (current.tasks[i].ulRunTimeCounter - previous.tasks[j].ulRunTimeCounter) / elapsed,
current.tasks[i].usStackHighWaterMark);
cJSON * t=cJSON_CreateObject();
cJSON_AddNumberToObject(t,"cpu",100 * (current.tasks[i].ulRunTimeCounter - previous.tasks[j].ulRunTimeCounter) / elapsed);
cJSON_AddNumberToObject(t,"minstk",current.tasks[i].usStackHighWaterMark);
cJSON_AddNumberToObject(t,"bprio",current.tasks[i].uxBasePriority);
cJSON_AddNumberToObject(t,"cprio",current.tasks[i].uxCurrentPriority);
cJSON_AddStringToObject(t,"nme",current.tasks[i].pcTaskName);
cJSON_AddNumberToObject(t,"st",current.tasks[i].eCurrentState);
cJSON_AddNumberToObject(t,"num",current.tasks[i].xTaskNumber);
cJSON_AddItemToArray(tlist,t);
if (i % 3 == 2 || i == current.n - 1) {
ESP_LOGI(TAG, "%s", scratch);
n = 0;
@@ -79,13 +92,21 @@ static void task_stats( void ) {
#else
for (int i = 0, n = 0; i < current.n; i ++) {
n += sprintf(scratch + n, "%16s s:%5u\t", current.tasks[i].pcTaskName, current.tasks[i].usStackHighWaterMark);
cJSON * t=cJSON_CreateObject();
cJSON_AddNumberToObject(t,"minstk",current.tasks[i].usStackHighWaterMark);
cJSON_AddNumberToObject(t,"bprio",current.tasks[i].uxBasePriority);
cJSON_AddNumberToObject(t,"cprio",current.tasks[i].uxCurrentPriority);
cJSON_AddStringToObject(t,"nme",current.tasks[i].pcTaskName);
cJSON_AddStringToObject(t,"st",current.tasks[i].eCurrentState);
cJSON_AddNumberToObject(t,"num",current.tasks[i].xTaskNumber);
cJSON_AddItemToArray(tlist,t);
if (i % 3 == 2 || i == current.n - 1) {
ESP_LOGI(TAG, "%s", scratch);
n = 0;
}
}
#endif
cJSON_AddItemToObject(top,"tasks",tlist);
if (previous.tasks) free(previous.tasks);
previous = current;
#endif
@@ -95,13 +116,26 @@ static void task_stats( void ) {
*
*/
static void monitor_callback(TimerHandle_t xTimer) {
cJSON * top=cJSON_CreateObject();
cJSON_AddNumberToObject(top,"free_iram",heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
cJSON_AddNumberToObject(top,"min_free_iram",heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL));
cJSON_AddNumberToObject(top,"free_spiram",heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
cJSON_AddNumberToObject(top,"min_free_spiram",heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
ESP_LOGI(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu)",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
task_stats();
task_stats(top);
char * top_a= cJSON_PrintUnformatted(top);
if(top_a){
messaging_post_message(MESSAGING_INFO, MESSAGING_CLASS_STATS,top_a);
FREE_AND_NULL(top_a);
}
cJSON_free(top);
}
/****************************************************************************************
@@ -109,6 +143,7 @@ static void monitor_callback(TimerHandle_t xTimer) {
*/
static void jack_handler_default(void *id, button_event_e event, button_press_e mode, bool long_press) {
ESP_LOGD(TAG, "Jack %s", event == BUTTON_PRESSED ? "inserted" : "removed");
messaging_post_message(MESSAGING_INFO, MESSAGING_CLASS_SYSTEM,"jack is %s",BUTTON_PRESSED ? "inserted" : "removed");
if (jack_handler_svc) (*jack_handler_svc)(event == BUTTON_PRESSED);
}

View File

@@ -16,6 +16,7 @@
#include "monitor.h"
#include "globdefs.h"
#include "accessors.h"
#include "messaging.h"
extern void battery_svc_init(void);
extern void monitor_svc_init(void);
@@ -52,6 +53,7 @@ void set_power_gpio(int gpio, char *value) {
*
*/
void services_init(void) {
messaging_service_init();
gpio_install_isr_service(0);
#ifdef CONFIG_I2C_LOCKED

View File

@@ -5,9 +5,6 @@
# todo: add support for https
COMPONENT_ADD_INCLUDEDIRS := .
COMPONENT_ADD_INCLUDEDIRS += include
COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/main/
COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/components/tools
CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO -DCONFIG_OTA_ALLOW_HTTP=1
#CFLAGS += -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCONFIG_OTA_ALLOW_HTTP=1

View File

@@ -6,9 +6,6 @@
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef LOG_LOCAL_LEVEL
#define LOG_LOCAL_LEVEL ESP_LOG_INFO
#endif
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
@@ -23,46 +20,69 @@
#include "esp_err.h"
#include "tcpip_adapter.h"
#include "squeezelite-ota.h"
#include "platform_config.h"
#include "platform_esp32.h"
#include "config.h"
#include <time.h>
#include <sys/time.h>
#include <stdarg.h>
#include "esp_secure_boot.h"
#include "esp_flash_encrypt.h"
#include "esp_spi_flash.h"
#include "platform_esp32.h"
#include "sdkconfig.h"
#include "messaging.h"
#include "trace.h"
#include "esp_ota_ops.h"
#include "display.h"
#include "gds.h"
#include "gds_text.h"
#include "gds_draw.h"
extern const char * get_certificate();
#ifdef CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1
#define OTA_CORE 0
#else
#define OTA_CORE 1
#endif
static const char *TAG = "squeezelite-ota";
char * ota_write_data = NULL;
esp_http_client_handle_t ota_http_client = NULL;
#define IMAGE_HEADER_SIZE sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t) + 1
#define BUFFSIZE 4096
#define HASH_LEN 32 /* SHA-256 digest length */
typedef struct {
char * url;
char * bin;
uint32_t length;
} ota_thread_parms_t ;
static ota_thread_parms_t ota_thread_parms;
typedef enum {
OTA_TYPE_HTTP,
OTA_TYPE_BUFFER,
OTA_TYPE_INVALID
} ota_type_t;
static struct {
char status_text[81];
uint32_t ota_actual_len;
uint32_t ota_total_len;
char * redirected_url;
char * current_url;
uint32_t actual_image_len;
uint32_t total_image_len;
uint32_t remain_image_len;
ota_type_t ota_type;
char * ota_write_data;
char * bin_data;
bool bOTAStarted;
bool bInitialized;
size_t buffer_size;
uint8_t lastpct;
uint8_t newpct;
struct timeval OTA_start;
bool bOTAThreadStarted;
const esp_partition_t *configured;
const esp_partition_t *running;
const esp_partition_t * update_partition;
const esp_partition_t* last_invalid_app ;
const esp_partition_t * ota_partition;
} ota_status;
struct timeval tv;
static esp_http_client_config_t ota_config;
extern void wifi_manager_refresh_ota_json();
struct timeval tv;
static esp_http_client_config_t http_client_config;
void _printMemStats(){
ESP_LOGD(TAG,"Heap internal:%zu (min:%zu) external:%zu (min:%zu)",
@@ -71,35 +91,121 @@ void _printMemStats(){
heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
}
void triggerStatusJsonRefresh(bool bDelay,const char * status, ...){
va_list args;
va_start(args, status);
vsnprintf(ota_status.status_text,sizeof(ota_status.status_text)-1,status, args);
va_end(args);
_printMemStats();
wifi_manager_refresh_ota_json();
if(bDelay){
ESP_LOGD(TAG,"Holding task...");
vTaskDelay(200 / portTICK_PERIOD_MS); // wait here for a short amount of time. This will help with refreshing the UI status
ESP_LOGD(TAG,"Done holding task...");
uint8_t ota_get_pct_complete(){
return ota_status.total_image_len==0?0:
(uint8_t)((float)ota_status.actual_image_len/(float)ota_status.total_image_len*100.0f);
}
typedef struct {
int x1,y1,x2,y2,width,height;
} rect_t;
typedef struct _progress {
int border_thickness;
int sides_margin;
int vertical_margin;
int bar_tot_height;
int bar_fill_height;
rect_t border;
rect_t filler;
} progress_t;
static progress_t * loc_displayer_get_progress_dft(){
int start_coord_offset=0;
static progress_t def={
.border_thickness = 2,
.sides_margin = 2,
.bar_tot_height = 7,
};
def.bar_fill_height= def.bar_tot_height-(def.border_thickness*2);
def.border.x1=start_coord_offset+def.sides_margin;
def.border.x2=GDS_GetWidth(display)-def.sides_margin;
// progress bar will be drawn at the bottom of the display
def.border.y2= GDS_GetHeight(display)-def.border_thickness;
def.border.y1= def.border.y2-def.bar_tot_height;
def.border.width=def.border.x2-def.border.x1;
def.border.height=def.border.y2-def.border.y1;
def.filler.x1= def.border.x1+def.border_thickness;
def.filler.x2= def.border.x2-def.border_thickness;
def.filler.y1= def.border.y1+def.border_thickness;
def.filler.y2= def.border.y2-def.border_thickness;
def.filler.width=def.filler.x2-def.filler.x1;
def.filler.height=def.filler.y2-def.filler.y1;
assert(def.filler.width>0);
assert(def.filler.height>0);
assert(def.border.width>0);
assert(def.border.height>0);
assert(def.border.width>def.filler.width);
assert(def.border.height>def.filler.height);
return &def;
}
static void loc_displayer_progressbar(uint8_t pct){
static progress_t * progress_coordinates;
if(!progress_coordinates) progress_coordinates = loc_displayer_get_progress_dft();
int filler_x=progress_coordinates->filler.x1+(int)((float)progress_coordinates->filler.width*(float)pct/(float)100);
ESP_LOGD(TAG,"Drawing %d,%d,%d,%d",progress_coordinates->border.x1,progress_coordinates->border.y1,progress_coordinates->border.x2,progress_coordinates->border.y2);
GDS_DrawBox(display,progress_coordinates->border.x1,progress_coordinates->border.y1,progress_coordinates->border.x2,progress_coordinates->border.y2,GDS_COLOR_WHITE,false);
ESP_LOGD(TAG,"Drawing %d,%d,%d,%d",progress_coordinates->filler.x1,progress_coordinates->filler.y1,filler_x,progress_coordinates->filler.y2);
if(filler_x > progress_coordinates->filler.x1){
GDS_DrawBox(display,progress_coordinates->filler.x1,progress_coordinates->filler.y1,filler_x,progress_coordinates->filler.y2,GDS_COLOR_WHITE,true);
}
else {
ESP_LOGI(TAG,"%s",ota_status.status_text);
taskYIELD();
// Clear the inner box
GDS_DrawBox(display,progress_coordinates->filler.x1,progress_coordinates->filler.y1,progress_coordinates->filler.x2,progress_coordinates->filler.y2,GDS_COLOR_BLACK,true);
}
ESP_LOGD(TAG,"Updating Display");
GDS_Update(display);
}
const char * ota_get_status(){
if(!ota_status.bInitialized)
{
memset(ota_status.status_text, 0x00,sizeof(ota_status.status_text));
ota_status.bInitialized = true;
}
return ota_status.status_text;
}
uint8_t ota_get_pct_complete(){
return ota_status.ota_total_len==0?0:
(uint8_t)((float)ota_status.ota_actual_len/(float)ota_status.ota_total_len*100.0f);
void sendMessaging(messaging_types type,const char * fmt, ...){
va_list args;
cJSON * msg = cJSON_CreateObject();
size_t str_len=0;
char * msg_str=NULL;
va_start(args, fmt);
str_len = vsnprintf(NULL,0,fmt,args)+1;
if(str_len>0){
msg_str = malloc(str_len);
vsnprintf(msg_str,str_len,fmt,args);
if(type == MESSAGING_WARNING){
ESP_LOGW(TAG,"%s",msg_str);
}
else if (type == MESSAGING_ERROR){
ESP_LOGE(TAG,"%s",msg_str);
}
else
ESP_LOGI(TAG,"%s",msg_str);
}
else {
ESP_LOGW(TAG, "Sending empty string message");
}
va_end(args);
if(type!=MESSAGING_INFO){
GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, msg_str);
}
cJSON_AddStringToObject(msg,"ota_dsc",str_or_unknown(msg_str));
free(msg_str);
cJSON_AddNumberToObject(msg,"ota_pct", ota_get_pct_complete() );
char * json_msg = cJSON_PrintUnformatted(msg);
messaging_post_message(type, MESSAGING_CLASS_OTA, json_msg);
free(json_msg);
cJSON_free(msg);
_printMemStats();
}
//esp_err_t decode_alloc_ota_message(single_message_t * message, char * ota_dsc, uint8_t * ota_pct ){
// if(!message || !message->message) return ESP_ERR_INVALID_ARG;
// cJSON * json = cJSON_Parse(message->message);
// if(!json) return ESP_FAIL;
// if(ota_dsc) {
// ota_dsc = strdup(cJSON_GetObjectItem(json, "ota_dsc")?cJSON_GetStringValue(cJSON_GetObjectItem(json, "ota_dsc")):"");
// }
// if(ota_pct){
// *ota_pct = cJSON_GetObjectItem(json, "ota_pct")?cJSON_GetObjectItem(json, "ota_pct")->valueint:0;
// }
// cJSON_free(json);
// return ESP_OK;
//}
static void __attribute__((noreturn)) task_fatal_error(void)
{
@@ -110,7 +216,7 @@ static void __attribute__((noreturn)) task_fatal_error(void)
;
}
}
#define FREE_RESET(p) if(p!=NULL) { free(p); p=NULL; }
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
// --------------
@@ -138,10 +244,11 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt)
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
if(ota_status.bOTAStarted) triggerStatusJsonRefresh(true,"Installing...");
ota_status.ota_total_len=0;
ota_status.ota_actual_len=0;
if(ota_status.bOTAStarted) sendMessaging(MESSAGING_INFO,"Connecting to URL...");
ota_status.total_image_len=0;
ota_status.actual_image_len=0;
ota_status.lastpct=0;
ota_status.remain_image_len=0;
ota_status.newpct=0;
gettimeofday(&ota_status.OTA_start, NULL);
@@ -152,13 +259,11 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt)
case HTTP_EVENT_ON_HEADER:
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s",evt->header_key, evt->header_value);
if (strcasecmp(evt->header_key, "location") == 0) {
FREE_RESET(ota_status.redirected_url);
ota_status.redirected_url=strdup(evt->header_value);
ESP_LOGW(TAG,"OTA will redirect to url: %s",ota_status.redirected_url);
ESP_LOGW(TAG,"OTA will redirect to url: %s",evt->header_value);
}
if (strcasecmp(evt->header_key, "content-length") == 0) {
ota_status.ota_total_len = atol(evt->header_value);
ESP_LOGW(TAG, "Content length found: %s, parsed to %d", evt->header_value, ota_status.ota_total_len);
ota_status.total_image_len = atol(evt->header_value);
ESP_LOGW(TAG, "Content length found: %s, parsed to %d", evt->header_value, ota_status.total_image_len);
}
break;
case HTTP_EVENT_ON_DATA:
@@ -176,29 +281,49 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt)
return ESP_OK;
}
esp_err_t init_config(char * url){
memset(&ota_config, 0x00, sizeof(ota_config));
ota_status.bInitialized = true;
triggerStatusJsonRefresh(true,"Initializing...");
if(url==NULL || strlen(url)==0){
ESP_LOGE(TAG,"HTTP OTA called without a url");
esp_err_t init_config(ota_thread_parms_t * p_ota_thread_parms){
memset(&http_client_config, 0x00, sizeof(http_client_config));
sendMessaging(MESSAGING_INFO,"Initializing...");
loc_displayer_progressbar(0);
ota_status.ota_type= OTA_TYPE_INVALID;
if(p_ota_thread_parms->url !=NULL && strlen(p_ota_thread_parms->url)>0 ){
ota_status.ota_type= OTA_TYPE_HTTP;
}
else if(p_ota_thread_parms->bin!=NULL && p_ota_thread_parms->length > 0) {
ota_status.ota_type= OTA_TYPE_BUFFER;
}
if( ota_status.ota_type== OTA_TYPE_INVALID ){
ESP_LOGE(TAG,"HTTP OTA called without a url or a binary buffer");
return ESP_ERR_INVALID_ARG;
}
ota_status.buffer_size = BUFFSIZE;
ota_status.ota_write_data = heap_caps_malloc(ota_status.buffer_size+1 , (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT));
if(ota_status.ota_write_data== NULL){
ESP_LOGE(TAG,"Error allocating the ota buffer");
return ESP_ERR_NO_MEM;
}
switch (ota_status.ota_type) {
case OTA_TYPE_HTTP:
http_client_config.cert_pem =get_certificate();
http_client_config.event_handler = _http_event_handler;
http_client_config.disable_auto_redirect=true;
http_client_config.skip_cert_common_name_check = false;
http_client_config.url = strdup(p_ota_thread_parms->url);
http_client_config.max_redirection_count = 3;
// buffer size below is for http read chunks
http_client_config.buffer_size = 1024 ;
break;
case OTA_TYPE_BUFFER:
ota_status.bin_data = p_ota_thread_parms->bin;
ota_status.total_image_len = p_ota_thread_parms->length;
break;
default:
return ESP_FAIL;
break;
}
ota_status.current_url= url;
ota_config.cert_pem =get_certificate();
ota_config.event_handler = _http_event_handler;
ota_config.buffer_size = BUFFSIZE;
//ota_config.disable_auto_redirect=true;
ota_config.disable_auto_redirect=false;
ota_config.skip_cert_common_name_check = false;
ota_config.url = strdup(url);
ota_config.max_redirection_count = 3;
ota_write_data = heap_caps_malloc(ota_config.buffer_size+1 , MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
//ota_write_data = malloc(ota_config.buffer_size+1);
if(ota_write_data== NULL){
ESP_LOGE(TAG,"Error allocating the ota buffer");
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
esp_partition_t * _get_ota_partition(esp_partition_subtype_t subtype){
@@ -225,7 +350,7 @@ esp_partition_t * _get_ota_partition(esp_partition_subtype_t subtype){
esp_err_t _erase_last_boot_app_partition(esp_partition_t *ota_partition)
esp_err_t _erase_last_boot_app_partition(const esp_partition_t *ota_partition)
{
uint16_t num_passes=0;
uint16_t remain_size=0;
@@ -257,18 +382,20 @@ esp_err_t _erase_last_boot_app_partition(esp_partition_t *ota_partition)
ESP_LOGD(TAG,"Pass %d of %d, with chunks of %d bytes, from %d to %d", i+1, num_passes,single_pass_size,i*single_pass_size,i*single_pass_size+single_pass_size);
err=esp_partition_erase_range(ota_partition, i*single_pass_size, single_pass_size);
if(err!=ESP_OK) return err;
// triggerStatusJsonRefresh(i%10==0?true:false,"Erasing flash (%u/%u)",i,num_passes);
if(i%2) {
triggerStatusJsonRefresh(false,"Erasing flash (%u/%u)",i,num_passes);
loc_displayer_progressbar((int)(((float)i/(float)num_passes)*100.0f));
sendMessaging(MESSAGING_INFO,"Erasing flash (%u/%u)",i,num_passes);
}
vTaskDelay(200/ portTICK_PERIOD_MS); // wait here for a short amount of time. This will help with reducing WDT errors
vTaskDelay(100/ portTICK_PERIOD_MS); // wait here for a short amount of time. This will help with reducing WDT errors
}
if(remain_size>0){
err=esp_partition_erase_range(ota_partition, ota_partition->size-remain_size, remain_size);
if(err!=ESP_OK) return err;
}
triggerStatusJsonRefresh(true,"Erasing flash complete.");
taskYIELD();
sendMessaging(MESSAGING_INFO,"Erasing flash complete.");
loc_displayer_progressbar(100);
vTaskDelay(200/ portTICK_PERIOD_MS);
return ESP_OK;
}
@@ -287,28 +414,32 @@ static bool process_again(int status_code)
static esp_err_t _http_handle_response_code(esp_http_client_handle_t http_client, int status_code)
{
esp_err_t err=ESP_OK;
if (status_code == HttpStatus_MovedPermanently || status_code == HttpStatus_Found) {
if (status_code == HttpStatus_MovedPermanently || status_code == HttpStatus_Found ) {
ESP_LOGW(TAG, "Handling HTTP redirection. ");
err = esp_http_client_set_redirection(http_client);
if (err != ESP_OK) {
ESP_LOGE(TAG, "URL redirection Failed. %s", esp_err_to_name(err));
return err;
}
ESP_LOGW(TAG, "Done Handling HTTP redirection. ");
} else if (status_code == HttpStatus_Unauthorized) {
ESP_LOGW(TAG, "Handling Unauthorized. ");
esp_http_client_add_auth(http_client);
}
ESP_LOGD(TAG, "Redirection done, checking if we need to read the data. ");
if (process_again(status_code)) {
char * local_buff = heap_caps_malloc(ota_config.buffer_size+1, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
//char * local_buff = malloc(ota_config.buffer_size+1);
if(local_buff==NULL){
ESP_LOGE(TAG,"Failed to allocate internal memory buffer for http processing");
return ESP_ERR_NO_MEM;
}
//ESP_LOGD(TAG, "We have to read some more data. Allocating buffer size %u",ota_config.buffer_size+1);
//char * local_buff = heap_caps_malloc(ota_status.buffer_size+1, (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT));
// if(local_buff==NULL){
// ESP_LOGE(TAG,"Failed to allocate internal memory buffer for http processing");
// return ESP_ERR_NO_MEM;
// }
while (1) {
ESP_LOGD(TAG, "Reading data chunk. ");
int data_read = esp_http_client_read(http_client, local_buff, ota_config.buffer_size);
int data_read = esp_http_client_read(http_client, ota_status.ota_write_data, ota_status.buffer_size);
if (data_read < 0) {
ESP_LOGE(TAG, "Error: SSL data read error");
err= ESP_FAIL;
@@ -319,7 +450,7 @@ static esp_err_t _http_handle_response_code(esp_http_client_handle_t http_client
break;
}
}
FREE_RESET(local_buff);
//FREE_RESET(local_buff);
}
return err;
@@ -329,44 +460,50 @@ static esp_err_t _http_connect(esp_http_client_handle_t http_client)
esp_err_t err = ESP_FAIL;
int status_code, header_ret;
do {
ESP_LOGD(TAG, "connecting the http client. ");
ESP_LOGI(TAG, "connecting the http client. ");
err = esp_http_client_open(http_client, 0);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
sendMessaging(MESSAGING_ERROR,"Failed to open HTTP connection: %s", esp_err_to_name(err));
return err;
}
ESP_LOGD(TAG, "Fetching headers");
ESP_LOGI(TAG, "Fetching headers");
header_ret = esp_http_client_fetch_headers(http_client);
if (header_ret < 0) {
// Error found
sendMessaging(MESSAGING_ERROR,"Header fetch failed");
return header_ret;
}
ESP_LOGD(TAG, "HTTP Header fetch completed, found content length of %d",header_ret);
ESP_LOGI(TAG, "HTTP Header fetch completed, found content length of %d",header_ret);
status_code = esp_http_client_get_status_code(http_client);
ESP_LOGD(TAG, "HTTP status code was %d",status_code);
err = _http_handle_response_code(http_client, status_code);
if (err != ESP_OK) {
sendMessaging(MESSAGING_ERROR,"HTTP connect error: %s", esp_err_to_name(err));
return err;
}
} while (process_again(status_code));
if(status_code >=400 && status_code <=900){
sendMessaging(MESSAGING_ERROR,"Error: HTTP Status %d",status_code);
err=ESP_FAIL;
}
return err;
}
void ota_task_cleanup(const char * message, ...){
ota_status.bOTAThreadStarted=false;
loc_displayer_progressbar(0);
if(message!=NULL){
va_list args;
va_start(args, message);
triggerStatusJsonRefresh(true,message, args);
sendMessaging(MESSAGING_ERROR,message, args);
va_end(args);
ESP_LOGE(TAG, "%s",ota_status.status_text);
}
FREE_RESET(ota_status.redirected_url);
FREE_RESET(ota_status.current_url);
FREE_RESET(ota_write_data);
FREE_RESET(ota_status.ota_write_data);
FREE_RESET(ota_status.bin_data);
if(ota_http_client!=NULL) {
esp_http_client_cleanup(ota_http_client);
ota_http_client=NULL;
@@ -374,24 +511,120 @@ void ota_task_cleanup(const char * message, ...){
ota_status.bOTAStarted = false;
task_fatal_error();
}
esp_err_t ota_buffer_all(){
int data_read=0;
esp_err_t err=ESP_OK;
if (ota_status.ota_type == OTA_TYPE_HTTP){
GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Downloading file");
ota_http_client = esp_http_client_init(&http_client_config);
if (ota_http_client == NULL) {
sendMessaging(MESSAGING_ERROR,"Error: Failed to initialize HTTP connection.");
return ESP_FAIL;
}
_printMemStats();
// Open the http connection and follow any redirection
err = _http_connect(ota_http_client);
if (err != ESP_OK) {
return err;
}
if(ota_status.total_image_len<=0){
sendMessaging(MESSAGING_ERROR,"Error: Invalid image length");
return ESP_FAIL;
}
ota_status.bin_data= malloc(ota_status.total_image_len);
if(ota_status.bin_data==NULL){
sendMessaging(MESSAGING_ERROR,"Error: buffer alloc error");
return ESP_FAIL;
}
data_read = esp_http_client_read(ota_http_client, ota_status.bin_data, ota_status.total_image_len);
if(data_read != ota_status.total_image_len){
sendMessaging(MESSAGING_ERROR,"Error: Binary incomplete");
return ESP_FAIL;
}
}
else {
gettimeofday(&ota_status.OTA_start, NULL);
}
ota_status.remain_image_len=ota_status.total_image_len;
return err;
}
int ota_buffer_read(){
int data_read=0;
if(ota_status.remain_image_len >ota_status.buffer_size){
data_read = ota_status.buffer_size;
} else {
data_read = ota_status.remain_image_len;
}
memcpy(ota_status.ota_write_data, &ota_status.bin_data[ota_status.actual_image_len], data_read);
ota_status.actual_image_len += data_read;
ota_status.remain_image_len -= data_read;
return data_read;
}
esp_err_t ota_header_check(){
esp_app_desc_t new_app_info;
esp_app_desc_t running_app_info;
ota_status.configured = esp_ota_get_boot_partition();
ota_status.running = esp_ota_get_running_partition();
ota_status.last_invalid_app= esp_ota_get_last_invalid_partition();
ota_status.ota_partition = _get_ota_partition(ESP_PARTITION_SUBTYPE_APP_OTA_0);
ESP_LOGI(TAG, "Running partition [%s] type %d subtype %d (offset 0x%08x)", ota_status.running->label, ota_status.running->type, ota_status.running->subtype, ota_status.running->address);
if (ota_status.total_image_len > ota_status.ota_partition->size){
ota_task_cleanup("Error: Image size too large to fit in partition.");
return ESP_FAIL;
}
if(ota_status.ota_partition == NULL){
ESP_LOGE(TAG,"Unable to locate OTA application partition. ");
ota_task_cleanup("Error: OTA partition not found");
return ESP_FAIL;
}
if (ota_status.configured != ota_status.running) {
ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x", ota_status.configured->address, ota_status.running->address);
ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)");
}
ESP_LOGI(TAG, "Next ota update partition is: [%s] subtype %d at offset 0x%x",
ota_status.update_partition->label, ota_status.update_partition->subtype, ota_status.update_partition->address);
if (ota_status.total_image_len >= IMAGE_HEADER_SIZE) {
// check current version with downloading
memcpy(&new_app_info, &ota_status.bin_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version);
if (esp_ota_get_partition_description(ota_status.running, &running_app_info) == ESP_OK) {
ESP_LOGI(TAG, "Running recovery version: %s", running_app_info.version);
}
esp_app_desc_t invalid_app_info;
if (esp_ota_get_partition_description(ota_status.last_invalid_app, &invalid_app_info) == ESP_OK) {
ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version);
}
if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) {
ESP_LOGW(TAG, "Current running version is the same as a new.");
}
return ESP_OK;
}
else{
ota_task_cleanup("Error: Binary file too small");
}
return ESP_FAIL;
}
void ota_task(void *pvParameter)
{
esp_err_t err = ESP_OK;
size_t buffer_size = BUFFSIZE;
int data_read = 0;
GDS_TextSetFont(display,2,GDS_GetHeight(display)>32?&Font_droid_sans_fallback_15x17:&Font_droid_sans_fallback_11x13,-2);
GDS_ClearExt(display, true);
GDS_TextLine(display, 1, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Firmware update");
GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Initializing");
loc_displayer_progressbar(0);
ESP_LOGD(TAG, "HTTP ota Thread started");
const esp_partition_t *configured = esp_ota_get_boot_partition();
const esp_partition_t *running = esp_ota_get_running_partition();
const esp_partition_t * update_partition = esp_ota_get_next_update_partition(NULL);
ESP_LOGI(TAG, "esp_ota_get_next_update_partition returned : partition [%s] subtype %d at offset 0x%x",
update_partition->label, update_partition->subtype, update_partition->address);
if (configured != running) {
ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x", configured->address, running->address);
ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)");
}
ESP_LOGI(TAG, "Running partition [%s] type %d subtype %d (offset 0x%08x)", running->label, running->type, running->subtype, running->address);
_printMemStats();
ota_status.update_partition = esp_ota_get_next_update_partition(NULL);
ESP_LOGI(TAG,"Initializing OTA configuration");
err = init_config(pvParameter);
@@ -400,139 +633,96 @@ void ota_task(void *pvParameter)
return;
}
_printMemStats();
ota_status.bOTAStarted = true;
sendMessaging(MESSAGING_INFO,"Starting OTA...");
err=ota_buffer_all();
if(err!=ESP_OK){
ota_task_cleanup(NULL);
return;
}
if(ota_header_check()!=ESP_OK){
ota_task_cleanup(NULL);
return;
}
/* Locate and erase ota application partition */
ESP_LOGW(TAG,"**************** Expecting WATCHDOG errors below during flash erase. This is OK and not to worry about **************** ");
triggerStatusJsonRefresh(true,"Erasing OTA partition");
esp_partition_t *ota_partition = _get_ota_partition(ESP_PARTITION_SUBTYPE_APP_OTA_0);
if(ota_partition == NULL){
ESP_LOGE(TAG,"Unable to locate OTA application partition. ");
ota_task_cleanup("Error: OTA application partition not found. (%s)",esp_err_to_name(err));
return;
}
GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Formatting partition");
sendMessaging(MESSAGING_INFO,"Formatting OTA partition");
_printMemStats();
err=_erase_last_boot_app_partition(ota_partition);
err=_erase_last_boot_app_partition(ota_status.ota_partition);
if(err!=ESP_OK){
ota_task_cleanup("Error: Unable to erase last APP partition. (%s)",esp_err_to_name(err));
return;
}
loc_displayer_progressbar(0);
_printMemStats();
ota_status.bOTAStarted = true;
triggerStatusJsonRefresh(true,"Starting OTA...");
ota_http_client = esp_http_client_init(&ota_config);
if (ota_http_client == NULL) {
ota_task_cleanup("Error: Failed to initialize HTTP connection.");
return;
}
_printMemStats();
// Open the http connection and follow any redirection
err = _http_connect(ota_http_client);
if (err != ESP_OK) {
ota_task_cleanup("Error: HTTP Start read failed. (%s)",esp_err_to_name(err));
return;
}
_printMemStats();
// Call OTA Begin with a small partition size - this minimizes the time spent in erasing partition,
// which was already done above
esp_ota_handle_t update_handle = 0 ;
int binary_file_length = 0;
err = esp_ota_begin(ota_status.ota_partition, 512, &update_handle);
if (err != ESP_OK) {
ota_task_cleanup("esp_ota_begin failed (%s)", esp_err_to_name(err));
return;
}
ESP_LOGD(TAG, "esp_ota_begin succeeded");
GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Writing image...");
while (ota_status.remain_image_len>0) {
/*deal with all receive packet*/
bool image_header_was_checked = false;
while (1) {
int data_read = esp_http_client_read(ota_http_client, ota_write_data, buffer_size);
if (data_read < 0) {
data_read = ota_buffer_read();
if (data_read <= 0) {
ota_task_cleanup("Error: Data read error");
return;
} else if (data_read > 0) {
if (image_header_was_checked == false) {
esp_app_desc_t new_app_info;
if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) {
// check current version with downloading
memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version);
esp_app_desc_t running_app_info;
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
ESP_LOGI(TAG, "Running recovery version: %s", running_app_info.version);
}
const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition();
esp_app_desc_t invalid_app_info;
if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) {
ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version);
}
// check current version with last invalid partition
// if (last_invalid_app != NULL) {
// if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) {
// ESP_LOGW(TAG, "New version is the same as invalid version.");
// ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
// ESP_LOGW(TAG, "The firmware has been rolled back to the previous version.");
// ota_task_cleanup("esp_ota_begin failed (%s)", esp_err_to_name(err));
// }
// }
if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) {
ESP_LOGW(TAG, "Current running version is the same as a new.");
}
image_header_was_checked = true;
// Call OTA Begin with a small partition size - this drives the erase operation which was already done;
err = esp_ota_begin(ota_partition, 512, &update_handle);
if (err != ESP_OK) {
ota_task_cleanup("esp_ota_begin failed (%s)", esp_err_to_name(err));
return;
}
ESP_LOGD(TAG, "esp_ota_begin succeeded");
} else {
ota_task_cleanup("Error: Binary file too large for the current partition");
return;
}
}
err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read);
err = esp_ota_write( update_handle, (const void *)ota_status.ota_write_data, data_read);
if (err != ESP_OK) {
ota_task_cleanup("Error: OTA Partition write failure. (%s)",esp_err_to_name(err));
return;
}
binary_file_length += data_read;
ESP_LOGD(TAG, "Written image length %d", binary_file_length);
ota_status.ota_actual_len=binary_file_length;
ESP_LOGD(TAG, "Written image length %d", ota_status.actual_image_len);
if(ota_get_pct_complete()%5 == 0) ota_status.newpct = ota_get_pct_complete();
if(ota_status.lastpct!=ota_status.newpct ) {
loc_displayer_progressbar(ota_status.newpct);
gettimeofday(&tv, NULL);
uint32_t elapsed_ms= (tv.tv_sec-ota_status.OTA_start.tv_sec )*1000+(tv.tv_usec-ota_status.OTA_start.tv_usec)/1000;
ESP_LOGI(TAG,"OTA progress : %d/%d (%d pct), %d KB/s", ota_status.ota_actual_len, ota_status.ota_total_len, ota_status.newpct, elapsed_ms>0?ota_status.ota_actual_len*1000/elapsed_ms/1024:0);
triggerStatusJsonRefresh(true,"Downloading & writing update.");
ESP_LOGI(TAG,"OTA progress : %d/%d (%d pct), %d KB/s", ota_status.actual_image_len, ota_status.total_image_len, ota_status.newpct, elapsed_ms>0?ota_status.actual_image_len*1000/elapsed_ms/1024:0);
sendMessaging(MESSAGING_INFO,"Writing binary file.");
ota_status.lastpct=ota_status.newpct;
}
taskYIELD();
} else if (data_read == 0) {
ESP_LOGI(TAG, "Connection closed");
ESP_LOGI(TAG, "End of OTA data stream");
break;
}
}
ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length);
if (ota_status.ota_total_len != binary_file_length) {
ESP_LOGI(TAG, "Total Write binary data length: %d", ota_status.actual_image_len);
if (ota_status.total_image_len != ota_status.actual_image_len) {
ota_task_cleanup("Error: Error in receiving complete file");
return;
}
_printMemStats();
loc_displayer_progressbar(100);
err = esp_ota_end(update_handle);
if (err != ESP_OK) {
ota_task_cleanup("Error: %s",esp_err_to_name(err));
return;
}
_printMemStats();
err = esp_ota_set_boot_partition(ota_partition);
err = esp_ota_set_boot_partition(ota_status.ota_partition);
if (err == ESP_OK) {
ESP_LOGI(TAG,"OTA Process completed successfully!");
triggerStatusJsonRefresh(true,"Success!");
sendMessaging(MESSAGING_INFO,"Success!");
GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Success!");
vTaskDelay(1500/ portTICK_PERIOD_MS); // wait here to give the UI a chance to refresh
GDS_Clear(display,GDS_COLOR_BLACK);
esp_restart();
} else {
ota_task_cleanup("Error: Unable to update boot partition [%s]",esp_err_to_name(err));
@@ -542,7 +732,7 @@ void ota_task(void *pvParameter)
return;
}
esp_err_t process_recovery_ota(const char * bin_url){
esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t length){
int ret = 0;
uint16_t stack_size, task_priority;
if(ota_status.bOTAThreadStarted){
@@ -551,23 +741,21 @@ esp_err_t process_recovery_ota(const char * bin_url){
}
memset(&ota_status, 0x00, sizeof(ota_status));
ota_status.bOTAThreadStarted=true;
char * urlPtr=strdup(bin_url);
// the first thing we need to do here is to erase the firmware url
// to avoid a boot loop
#ifdef CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1
#define OTA_CORE 0
#warning "OTA will run on core 0"
#else
#pragma message "OTA will run on core 1"
#define OTA_CORE 1
#endif
ESP_LOGI(TAG, "Starting ota on core %u for : %s", OTA_CORE,urlPtr);
if(bin_url){
ota_thread_parms.url =strdup(bin_url);
ESP_LOGI(TAG, "Starting ota on core %u for : %s", OTA_CORE,ota_thread_parms.url);
}
else {
ota_thread_parms.bin = bin_buffer;
ota_thread_parms.length = length;
ESP_LOGI(TAG, "Starting ota on core %u for file upload", OTA_CORE);
}
char * num_buffer=config_alloc_get(NVS_TYPE_STR, "ota_stack");
if(num_buffer!=NULL) {
stack_size= atol(num_buffer);
free(num_buffer);
num_buffer=NULL;
FREE_AND_NULL(num_buffer);
}
else {
ESP_LOGW(TAG,"OTA stack size config not found");
@@ -576,16 +764,15 @@ esp_err_t process_recovery_ota(const char * bin_url){
num_buffer=config_alloc_get(NVS_TYPE_STR, "ota_prio");
if(num_buffer!=NULL) {
task_priority= atol(num_buffer);
free(num_buffer);
num_buffer=NULL;
FREE_AND_NULL(num_buffer);
}
else {
ESP_LOGW(TAG,"OTA task priority not found");
task_priority= OTA_TASK_PRIOTITY;
}
ESP_LOGD(TAG,"OTA task stack size %d, priority %d (%d %s ESP_TASK_MAIN_PRIO)",stack_size , task_priority, abs(task_priority-ESP_TASK_MAIN_PRIO), task_priority-ESP_TASK_MAIN_PRIO>0?"above":"below");
ret=xTaskCreatePinnedToCore(&ota_task, "ota_task", stack_size , (void *)urlPtr, task_priority, NULL, OTA_CORE);
//ret=xTaskCreate(&ota_task, "ota_task", 1024*20, (void *)urlPtr, ESP_TASK_MAIN_PRIO+2, NULL);
ret=xTaskCreatePinnedToCore(&ota_task, "ota_task", stack_size , (void *)&ota_thread_parms, task_priority, NULL, OTA_CORE);
if (ret != pdPASS) {
ESP_LOGI(TAG, "create thread %s failed", "ota_task");
return ESP_FAIL;
@@ -593,10 +780,14 @@ esp_err_t process_recovery_ota(const char * bin_url){
return ESP_OK;
}
esp_err_t start_ota(const char * bin_url)
esp_err_t start_ota(const char * bin_url, char * bin_buffer, uint32_t length)
{
if(is_recovery_running){
return process_recovery_ota(bin_url);
#if RECOVERY_APPLICATION
return process_recovery_ota(bin_url,bin_buffer,length);
#else
if(!bin_url){
ESP_LOGE(TAG,"missing URL parameter. Unable to start OTA");
return ESP_ERR_INVALID_ARG;
}
ESP_LOGW(TAG, "Called to update the firmware from url: %s",bin_url);
if(config_set_value(NVS_TYPE_STR, "fwurl", bin_url) != ESP_OK){
@@ -610,4 +801,6 @@ esp_err_t start_ota(const char * bin_url)
ESP_LOGW(TAG, "Rebooting to recovery to complete the installation");
return guided_factory();
return ESP_OK;
#endif
}

View File

@@ -30,4 +30,5 @@ esp_err_t start_ota(const char * bin_url);
const char * ota_get_status();
uint8_t ota_get_pct_complete();
esp_err_t start_ota(const char * bin_url, char * bin_buffer, uint32_t length);

View File

@@ -10,6 +10,4 @@
COMPONENT_SRCDIRS := .
COMPONENT_SRCDIRS += ./libtelnet
COMPONENT_ADD_INCLUDEDIRS := .
COMPONENT_ADD_INCLUDEDIRS += ./libtelnet
COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/main/
COMPONENT_PRIV_INCLUDEDIRS += ./libtelnet

View File

@@ -38,6 +38,8 @@
#include "config.h"
#include "nvs_utilities.h"
#include "platform_esp32.h"
#include "messaging.h"
#include "trace.h"
/************************************
@@ -47,15 +49,16 @@
#define TELNET_STACK_SIZE 8048
#define TELNET_RX_BUF 1024
const static char tag[] = "telnet";
const static char TAG[] = "telnet";
static int uart_fd=0;
RingbufHandle_t buf_handle;
SemaphoreHandle_t xSemaphore = NULL;
//static SemaphoreHandle_t xSemaphore = NULL;
static size_t send_chunk=300;
static size_t log_buf_size=2000; //32-bit aligned size
static bool bIsEnabled=false;
static int partnerSocket=0;
static telnet_t *tnHandle;
extern bool bypass_wifi_manager;
/************************************
* Forward declarations
@@ -68,22 +71,34 @@ static int stdout_fstat(int fd, struct stat * st);
static ssize_t stdout_write(int fd, const void * data, size_t size);
static char *eventToString(telnet_event_type_t type);
static void handle_telnet_conn();
static void process_logs( UBaseType_t bytes);
static void process_logs( UBaseType_t bytes, bool is_write_op);
static bool bMirrorToUART=false;
struct telnetUserData {
int sockfd;
telnet_t *tnHandle;
char * rxbuf;
};
bool is_serial_suppressed(){
return bIsEnabled?!bMirrorToUART:false ;
}
void init_telnet(){
char *val= get_nvs_value_alloc(NVS_TYPE_STR, "telnet_enable");
if (!val || strlen(val) == 0 || !strcasestr("YX",val) ) {
ESP_LOGI(tag,"Telnet support disabled");
if (!val || strlen(val) == 0 || !strcasestr("YXD",val) ) {
ESP_LOGI(TAG,"Telnet support disabled");
if(val) free(val);
return;
}
// if wifi manager is bypassed, there will possibly be no wifi available
//
bMirrorToUART = (strcasestr("D",val)!=NULL);
if(!bMirrorToUART && bypass_wifi_manager){
// This isn't supposed to happen, as telnet won't start if wifi manager isn't
// started. So this is a safeguard only.
ESP_LOGW(TAG,"Wifi manager is not active. Forcing console on Serial output.");
}
FREE_AND_NULL(val);
val=get_nvs_value_alloc(NVS_TYPE_STR, "telnet_block");
if(val){
send_chunk=atol(val);
@@ -97,18 +112,21 @@ void init_telnet(){
log_buf_size=log_buf_size>0?log_buf_size:4000;
}
// Create the semaphore to guard a shared resource.
vSemaphoreCreateBinary( xSemaphore );
//vSemaphoreCreateBinary( xSemaphore );
// Redirect the output to our telnet handler as soon as possible
StaticRingbuffer_t *buffer_struct = (StaticRingbuffer_t *)heap_caps_malloc(sizeof(StaticRingbuffer_t), MALLOC_CAP_SPIRAM);
uint8_t *buffer_storage = (uint8_t *)heap_caps_malloc(sizeof(uint8_t)*log_buf_size, MALLOC_CAP_SPIRAM);
StaticRingbuffer_t *buffer_struct = (StaticRingbuffer_t *)malloc(sizeof(StaticRingbuffer_t) );
// All non-split ring buffer must have their memory alignment set to 32 bits.
uint8_t *buffer_storage = (uint8_t *)heap_caps_malloc(sizeof(uint8_t)*log_buf_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT );
buf_handle = xRingbufferCreateStatic(log_buf_size, RINGBUF_TYPE_BYTEBUF, buffer_storage, buffer_struct);
if (buf_handle == NULL) {
ESP_LOGE(tag,"Failed to create ring buffer for telnet!");
ESP_LOGE(TAG,"Failed to create ring buffer for telnet!");
messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Failed to allocate memory for telnet buffer");
return;
}
ESP_LOGI(tag, "***Redirecting log output to telnet");
ESP_LOGI(TAG, "***Redirecting log output to telnet");
const esp_vfs_t vfs = {
.flags = ESP_VFS_FLAG_DEFAULT,
.write = &stdout_write,
@@ -116,8 +134,12 @@ void init_telnet(){
.fstat = &stdout_fstat,
.close = &stdout_close,
.read = &stdout_read,
};
uart_fd=open("/dev/uart/0", O_RDWR);
if(bMirrorToUART){
uart_fd=open("/dev/uart/0", O_RDWR);
}
ESP_ERROR_CHECK(esp_vfs_register("/dev/pkspstdout", &vfs, NULL));
freopen("/dev/pkspstdout", "w", stdout);
freopen("/dev/pkspstdout", "w", stderr);
@@ -125,11 +147,11 @@ void init_telnet(){
}
void start_telnet(void * pvParameter){
static bool isStarted=false;
StaticTask_t *xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
StackType_t *xStack = malloc(TELNET_STACK_SIZE);
StaticTask_t *xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT));
StackType_t *xStack = heap_caps_malloc(TELNET_STACK_SIZE,(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT));
if(!isStarted && bIsEnabled) {
xTaskCreateStatic( (TaskFunction_t) &telnet_task, "telnet", TELNET_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, xTaskBuffer);
xTaskCreateStatic( (TaskFunction_t) &telnet_task, "telnet", TELNET_STACK_SIZE, NULL, ESP_TASK_MAIN_PRIO , xStack, xTaskBuffer);
isStarted=true;
}
}
@@ -142,13 +164,15 @@ static void telnet_task(void *data) {
int rc = bind(serverSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
if (rc < 0) {
ESP_LOGE(tag, "bind: %d (%s)", errno, strerror(errno));
ESP_LOGE(TAG, "bind: %d (%s)", errno, strerror(errno));
close(serverSocket);
return;
}
rc = listen(serverSocket, 5);
if (rc < 0) {
ESP_LOGE(tag, "listen: %d (%s)", errno, strerror(errno));
ESP_LOGE(TAG, "listen: %d (%s)", errno, strerror(errno));
close(serverSocket);
return;
}
@@ -156,16 +180,17 @@ static void telnet_task(void *data) {
socklen_t len = sizeof(serverAddr);
rc = accept(serverSocket, (struct sockaddr *)&serverAddr, &len);
if (rc < 0 ){
ESP_LOGE(tag, "accept: %d (%s)", errno, strerror(errno));
ESP_LOGE(TAG, "accept: %d (%s)", errno, strerror(errno));
return;
}
else {
partnerSocket = rc;
ESP_LOGD(tag, "We have a new client connection!");
ESP_LOGD(TAG, "We have a new client connection!");
handle_telnet_conn();
ESP_LOGD(tag, "Telnet connection terminated");
ESP_LOGD(TAG, "Telnet connection terminated");
}
}
close(serverSocket);
vTaskDelete(NULL);
}
@@ -228,7 +253,9 @@ void process_received_data(const char * buffer, size_t size){
command[size]='\0';
if(command[0]!='\r' && command[0]!='\n'){
// echo the command buffer out to uart and run
write(uart_fd, command, size);
if(bMirrorToUART){
write(uart_fd, command, size);
}
run_command((char *)command);
}
free(command);
@@ -265,23 +292,23 @@ static void handle_telnet_events(
} // myhandle_telnet_events
static void process_logs(UBaseType_t count){
static void process_logs( UBaseType_t bytes, bool is_write_op){
//Receive an item from no-split ring buffer
size_t item_size;
UBaseType_t uxItemsWaiting;
UBaseType_t uxBytesToSend=count;
UBaseType_t uxItemsWaiting;
UBaseType_t uxBytesToSend=bytes;
vRingbufferGetInfo(buf_handle, NULL, NULL, NULL, NULL, &uxItemsWaiting);
if(count == 0){
// this sends the entire buffer to the remote client
uxBytesToSend = uxItemsWaiting;
}
if( partnerSocket ==0 && (uxItemsWaiting*100 / log_buf_size) <75){
// We still have some room in the ringbuffer and there's no telnet
// connection yet, so bail out for now.
//printf("%s() Log buffer used %u of %u bytes used\n", __FUNCTION__, uxItemsWaiting, log_buf_size);
vRingbufferGetInfo(buf_handle, NULL, NULL, NULL, NULL, &uxItemsWaiting);
bool is_space_available = ((log_buf_size-uxItemsWaiting)>=bytes && log_buf_size>uxItemsWaiting);
if( is_space_available && (is_write_op || partnerSocket == 0) ){
// there's still some room left in the buffer, and we're either
// processing a write operation or telnet isn't connected yet.
return;
}
if(is_write_op && !is_space_available && uxBytesToSend==0){
// flush at least the size of a full chunk
uxBytesToSend = send_chunk;
}
while(uxBytesToSend>0){
char *item = (char *)xRingbufferReceiveUpTo(buf_handle, &item_size, pdMS_TO_TICKS(50), uxBytesToSend);
@@ -322,7 +349,7 @@ static void handle_telnet_conn() {
pTelnetUserData->sockfd = partnerSocket;
// flush all the log buffer on connect
process_logs(0);
process_logs(log_buf_size, false);
while(1) {
//ESP_LOGD(tag, "waiting for data");
@@ -339,7 +366,7 @@ static void handle_telnet_conn() {
partnerSocket = 0;
return;
}
process_logs(send_chunk);
process_logs(send_chunk, false);
taskYIELD();
}
@@ -348,37 +375,24 @@ static void handle_telnet_conn() {
// ******************* stdout/stderr Redirection to ringbuffer
static ssize_t stdout_write(int fd, const void * data, size_t size) {
if (xSemaphoreTake(xSemaphore, (TickType_t) 10) == pdTRUE) {
// #1 Write to ringbuffer
if (buf_handle == NULL) {
printf("%s() ABORT. file handle _log_remote_fp is NULL\n",
__FUNCTION__);
} else {
//Send an item
UBaseType_t res = xRingbufferSend(buf_handle, data, size,
pdMS_TO_TICKS(100));
if (res != pdTRUE) {
// flush some entries
process_logs(size);
res = xRingbufferSend(buf_handle, data, size,
pdMS_TO_TICKS(100));
if (res != pdTRUE) {
printf("%s() ABORT. Unable to store log entry in buffer\n",
__FUNCTION__);
}
}
}
xSemaphoreGive(xSemaphore);
// #1 Write to ringbuffer
if (buf_handle == NULL) {
printf("%s() ABORT. file handle _log_remote_fp is NULL\n",
__FUNCTION__);
} else {
// We could not obtain the semaphore and can therefore not access
// the shared resource safely.
// flush the buffer if needed
process_logs(size, true);
//Send an item
UBaseType_t res = xRingbufferSend(buf_handle, data, size, pdMS_TO_TICKS(10));
assert(res == pdTRUE);
}
return write(uart_fd, data, size);
return bMirrorToUART?write(uart_fd, data, size):size;
}
static ssize_t stdout_read(int fd, void* data, size_t size) {
return read(fd, data, size);
//return read(fd, data, size);
return 0;
}
static int stdout_open(const char * path, int flags, int mode) {

View File

@@ -1,3 +1,4 @@
void init_telnet();
void start_telnet(void * pvParameter);
extern bool is_serial_suppressed();

View File

@@ -31,9 +31,4 @@ extern bool wait_for_wifi();
extern void console_start();
extern pthread_cond_t wifi_connect_suspend_cond;
extern pthread_t wifi_connect_suspend_mutex;
typedef enum {
INFO,
WARNING,
ERROR
} message_severity_t;
extern void set_status_message(message_severity_t severity, const char * message);

View File

@@ -28,5 +28,20 @@
#define STR(macro) QUOTE(macro)
#endif
#define ESP_LOG_DEBUG_EVENT(tag,e) ESP_LOGD(tag,"evt: " e)
#ifndef STR_OR_ALT
#define STR_OR_ALT(str,alt) (str?str:alt)
#endif
extern const char unknown_string_placeholder[];
extern const char * str_or_unknown(const char * str);
#ifndef FREE_AND_NULL
#define FREE_AND_NULL(x) if(x) { free(x); x=NULL; }
#endif
#ifndef CASE_TO_STR
#define CASE_TO_STR(x) case x: return STR(x); break;
#endif
#define START_FREE_MEM_CHECK(a) size_t a=heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
#define CHECK_RESET_FREE_MEM_CHECK(a,b) ESP_LOGV(__FUNCTION__ ,b "Mem used: %i",a-heap_caps_get_free_size(MALLOC_CAP_INTERNAL)); a=heap_caps_get_free_size(MALLOC_CAP_INTERNAL)

View File

@@ -0,0 +1,93 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ESP_HTTP_SERVER_H_
#define __ESP_HTTP_SERVER_H_
#include <stdio.h>
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <http_parser.h>
#include <sdkconfig.h>
#include <esp_err.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Starts the web server
*
* Create an instance of HTTP server and allocate memory/resources for it
* depending upon the specified configuration.
*
* Example usage:
* @code{c}
*
* //Function for starting the webserver
* httpd_handle_t start_webserver(void)
* {
* // Generate default configuration
* httpd_config_t config = HTTPD_DEFAULT_CONFIG();
*
* // Empty handle to http_server
* httpd_handle_t server = NULL;
*
* // Start the httpd server
* if (httpd_start(&server, &config) == ESP_OK) {
* // Register URI handlers
* httpd_register_uri_handler(server, &uri_get);
* httpd_register_uri_handler(server, &uri_post);
* }
* // If server failed to start, handle will be NULL
* return server;
* }
*
* @endcode
*
* @param[in] config Configuration for new instance of the server
* @param[out] handle Handle to newly created instance of the server. NULL on error
* @return
* - ESP_OK : Instance created successfully
* - ESP_ERR_INVALID_ARG : Null argument(s)
* - ESP_ERR_HTTPD_ALLOC_MEM : Failed to allocate memory for instance
* - ESP_ERR_HTTPD_TASK : Failed to launch server task
*/
esp_err_t __httpd_start(httpd_handle_t *handle, const httpd_config_t *config);
static inline int __httpd_os_thread_create_static(TaskHandle_t *thread,
const char *name, uint16_t stacksize, int prio,
void (*thread_routine)(void *arg), void *arg,
BaseType_t core_id)
{
StaticTask_t *xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT));
StackType_t *xStack = heap_caps_malloc(stacksize,(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT));
//
*thread = xTaskCreateStaticPinnedToCore(thread_routine, name, stacksize, arg, prio, xStack,xTaskBuffer,core_id);
if (*thread) {
return ESP_OK;
}
return ESP_FAIL;
}
#ifdef __cplusplus
}
#endif
#endif /* ! _ESP_HTTP_SERVER_H_ */

View File

@@ -0,0 +1,373 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <errno.h>
#include <esp_log.h>
#include <esp_err.h>
#include <assert.h>
#include <esp_http_server.h>
#include <_esp_http_server.h>
#include "esp_httpd_priv.h"
#include "ctrl_sock.h"
static const char *TAG = "_httpd";
struct httpd_ctrl_data {
enum httpd_ctrl_msg {
HTTPD_CTRL_SHUTDOWN,
HTTPD_CTRL_WORK,
} hc_msg;
httpd_work_fn_t hc_work;
void *hc_work_arg;
};
static esp_err_t _httpd_server_init(struct httpd_data *hd)
{
int fd = socket(PF_INET6, SOCK_STREAM, 0);
if (fd < 0) {
ESP_LOGE(TAG, LOG_FMT("error in socket (%d)"), errno);
return ESP_FAIL;
}
struct in6_addr inaddr_any = IN6ADDR_ANY_INIT;
struct sockaddr_in6 serv_addr = {
.sin6_family = PF_INET6,
.sin6_addr = inaddr_any,
.sin6_port = htons(hd->config.server_port)
};
/* Enable SO_REUSEADDR to allow binding to the same
* address and port when restarting the server */
int enable = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
/* This will fail if CONFIG_LWIP_SO_REUSE is not enabled. But
* it does not affect the normal working of the HTTP Server */
ESP_LOGW(TAG, LOG_FMT("error enabling SO_REUSEADDR (%d)"), errno);
}
int ret = bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (ret < 0) {
ESP_LOGE(TAG, LOG_FMT("error in bind (%d)"), errno);
close(fd);
return ESP_FAIL;
}
ret = listen(fd, hd->config.backlog_conn);
if (ret < 0) {
ESP_LOGE(TAG, LOG_FMT("error in listen (%d)"), errno);
close(fd);
return ESP_FAIL;
}
int ctrl_fd = cs_create_ctrl_sock(hd->config.ctrl_port);
if (ctrl_fd < 0) {
ESP_LOGE(TAG, LOG_FMT("error in creating ctrl socket (%d)"), errno);
close(fd);
return ESP_FAIL;
}
int msg_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (msg_fd < 0) {
ESP_LOGE(TAG, LOG_FMT("error in creating msg socket (%d)"), errno);
close(fd);
close(ctrl_fd);
return ESP_FAIL;
}
hd->listen_fd = fd;
hd->ctrl_fd = ctrl_fd;
hd->msg_fd = msg_fd;
return ESP_OK;
}
static void _httpd_process_ctrl_msg(struct httpd_data *hd)
{
struct httpd_ctrl_data msg;
int ret = recv(hd->ctrl_fd, &msg, sizeof(msg), 0);
if (ret <= 0) {
ESP_LOGW(TAG, LOG_FMT("error in recv (%d)"), errno);
return;
}
if (ret != sizeof(msg)) {
ESP_LOGW(TAG, LOG_FMT("incomplete msg"));
return;
}
switch (msg.hc_msg) {
case HTTPD_CTRL_WORK:
if (msg.hc_work) {
ESP_LOGD(TAG, LOG_FMT("work"));
(*msg.hc_work)(msg.hc_work_arg);
}
break;
case HTTPD_CTRL_SHUTDOWN:
ESP_LOGD(TAG, LOG_FMT("shutdown"));
hd->hd_td.status = THREAD_STOPPING;
break;
default:
break;
}
}
static esp_err_t _httpd_accept_conn(struct httpd_data *hd, int listen_fd)
{
/* If no space is available for new session, close the least recently used one */
if (hd->config.lru_purge_enable == true) {
if (!httpd_is_sess_available(hd)) {
/* Queue asynchronous closure of the least recently used session */
return httpd_sess_close_lru(hd);
/* Returning from this allowes the main server thread to process
* the queued asynchronous control message for closing LRU session.
* Since connection request hasn't been addressed yet using accept()
* therefore _httpd_accept_conn() will be called again, but this time
* with space available for one session
*/
}
}
struct sockaddr_in addr_from;
socklen_t addr_from_len = sizeof(addr_from);
int new_fd = accept(listen_fd, (struct sockaddr *)&addr_from, &addr_from_len);
if (new_fd < 0) {
ESP_LOGW(TAG, LOG_FMT("error in accept (%d)"), errno);
return ESP_FAIL;
}
ESP_LOGD(TAG, LOG_FMT("newfd = %d"), new_fd);
struct timeval tv;
/* Set recv timeout of this fd as per config */
tv.tv_sec = hd->config.recv_wait_timeout;
tv.tv_usec = 0;
setsockopt(new_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
/* Set send timeout of this fd as per config */
tv.tv_sec = hd->config.send_wait_timeout;
tv.tv_usec = 0;
setsockopt(new_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
if (ESP_OK != httpd_sess_new(hd, new_fd)) {
ESP_LOGW(TAG, LOG_FMT("session creation failed"));
close(new_fd);
return ESP_FAIL;
}
ESP_LOGD(TAG, LOG_FMT("complete"));
return ESP_OK;
}
/* Manage in-coming connection or data requests */
static esp_err_t _httpd_server(struct httpd_data *hd)
{
fd_set read_set;
FD_ZERO(&read_set);
if (hd->config.lru_purge_enable || httpd_is_sess_available(hd)) {
/* Only listen for new connections if server has capacity to
* handle more (or when LRU purge is enabled, in which case
* older connections will be closed) */
FD_SET(hd->listen_fd, &read_set);
}
FD_SET(hd->ctrl_fd, &read_set);
int tmp_max_fd;
httpd_sess_set_descriptors(hd, &read_set, &tmp_max_fd);
int maxfd = MAX(hd->listen_fd, tmp_max_fd);
tmp_max_fd = maxfd;
maxfd = MAX(hd->ctrl_fd, tmp_max_fd);
ESP_LOGD(TAG, LOG_FMT("doing select maxfd+1 = %d"), maxfd + 1);
int active_cnt = select(maxfd + 1, &read_set, NULL, NULL, NULL);
if (active_cnt < 0) {
ESP_LOGE(TAG, LOG_FMT("error in select (%d)"), errno);
httpd_sess_delete_invalid(hd);
return ESP_OK;
}
/* Case0: Do we have a control message? */
if (FD_ISSET(hd->ctrl_fd, &read_set)) {
ESP_LOGD(TAG, LOG_FMT("processing ctrl message"));
_httpd_process_ctrl_msg(hd);
if (hd->hd_td.status == THREAD_STOPPING) {
ESP_LOGD(TAG, LOG_FMT("stopping thread"));
return ESP_FAIL;
}
}
/* Case1: Do we have any activity on the current data
* sessions? */
int fd = -1;
while ((fd = httpd_sess_iterate(hd, fd)) != -1) {
if (FD_ISSET(fd, &read_set) || (httpd_sess_pending(hd, fd))) {
ESP_LOGD(TAG, LOG_FMT("processing socket %d"), fd);
if (httpd_sess_process(hd, fd) != ESP_OK) {
ESP_LOGD(TAG, LOG_FMT("closing socket %d"), fd);
close(fd);
/* Delete session and update fd to that
* preceding the one being deleted */
fd = httpd_sess_delete(hd, fd);
}
}
}
/* Case2: Do we have any incoming connection requests to
* process? */
if (FD_ISSET(hd->listen_fd, &read_set)) {
ESP_LOGD(TAG, LOG_FMT("processing listen socket %d"), hd->listen_fd);
if (_httpd_accept_conn(hd, hd->listen_fd) != ESP_OK) {
ESP_LOGW(TAG, LOG_FMT("error accepting new connection"));
}
}
return ESP_OK;
}
static void _httpd_close_all_sessions(struct httpd_data *hd)
{
int fd = -1;
while ((fd = httpd_sess_iterate(hd, fd)) != -1) {
ESP_LOGD(TAG, LOG_FMT("cleaning up socket %d"), fd);
httpd_sess_delete(hd, fd);
close(fd);
}
}
/* The main HTTPD thread */
static void _httpd_thread(void *arg)
{
int ret;
struct httpd_data *hd = (struct httpd_data *) arg;
hd->hd_td.status = THREAD_RUNNING;
ESP_LOGD(TAG, LOG_FMT("web server started"));
while (1) {
ret = _httpd_server(hd);
if (ret != ESP_OK) {
break;
}
}
ESP_LOGD(TAG, LOG_FMT("web server exiting"));
close(hd->msg_fd);
cs_free_ctrl_sock(hd->ctrl_fd);
_httpd_close_all_sessions(hd);
close(hd->listen_fd);
hd->hd_td.status = THREAD_STOPPED;
httpd_os_thread_delete();
}
static struct httpd_data *__httpd_create(const httpd_config_t *config)
{
/* Allocate memory for httpd instance data */
struct httpd_data *hd = calloc(1, sizeof(struct httpd_data));
if (!hd) {
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP server instance"));
return NULL;
}
hd->hd_calls = calloc(config->max_uri_handlers, sizeof(httpd_uri_t *));
if (!hd->hd_calls) {
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP URI handlers"));
free(hd);
return NULL;
}
hd->hd_sd = calloc(config->max_open_sockets, sizeof(struct sock_db));
if (!hd->hd_sd) {
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP session data"));
free(hd->hd_calls);
free(hd);
return NULL;
}
struct httpd_req_aux *ra = &hd->hd_req_aux;
ra->resp_hdrs = calloc(config->max_resp_headers, sizeof(struct resp_hdr));
if (!ra->resp_hdrs) {
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP response headers"));
free(hd->hd_sd);
free(hd->hd_calls);
free(hd);
return NULL;
}
hd->err_handler_fns = calloc(HTTPD_ERR_CODE_MAX, sizeof(httpd_err_handler_func_t));
if (!hd->err_handler_fns) {
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP error handlers"));
free(ra->resp_hdrs);
free(hd->hd_sd);
free(hd->hd_calls);
free(hd);
return NULL;
}
/* Save the configuration for this instance */
hd->config = *config;
return hd;
}
static void _httpd_delete(struct httpd_data *hd)
{
struct httpd_req_aux *ra = &hd->hd_req_aux;
/* Free memory of httpd instance data */
free(hd->err_handler_fns);
free(ra->resp_hdrs);
free(hd->hd_sd);
/* Free registered URI handlers */
httpd_unregister_all_uri_handlers(hd);
free(hd->hd_calls);
free(hd);
}
esp_err_t __httpd_start(httpd_handle_t *handle, const httpd_config_t *config)
{
if (handle == NULL || config == NULL) {
return ESP_ERR_INVALID_ARG;
}
/* Sanity check about whether LWIP is configured for providing the
* maximum number of open sockets sufficient for the server. Though,
* this check doesn't guarantee that many sockets will actually be
* available at runtime as other processes may use up some sockets.
* Note that server also uses 3 sockets for its internal use :
* 1) listening for new TCP connections
* 2) for sending control messages over UDP
* 3) for receiving control messages over UDP
* So the total number of required sockets is max_open_sockets + 3
*/
if (CONFIG_LWIP_MAX_SOCKETS < config->max_open_sockets + 3) {
ESP_LOGE(TAG, "Configuration option max_open_sockets is too large (max allowed %d)\n\t"
"Either decrease this or configure LWIP_MAX_SOCKETS to a larger value",
CONFIG_LWIP_MAX_SOCKETS - 3);
return ESP_ERR_INVALID_ARG;
}
struct httpd_data *hd = __httpd_create(config);
if (hd == NULL) {
/* Failed to allocate memory */
return ESP_ERR_HTTPD_ALLOC_MEM;
}
if (_httpd_server_init(hd) != ESP_OK) {
_httpd_delete(hd);
return ESP_FAIL;
}
httpd_sess_init(hd);
if (__httpd_os_thread_create_static(&hd->hd_td.handle, "httpd",
hd->config.stack_size,
hd->config.task_priority,
_httpd_thread, hd,
hd->config.core_id) != ESP_OK) {
/* Failed to launch task */
_httpd_delete(hd);
return ESP_ERR_HTTPD_TASK;
}
*handle = (httpd_handle_t *)hd;
return ESP_OK;
}

View File

@@ -10,7 +10,28 @@ if (!String.prototype.format) {
});
};
}
var nvs_type_t = {
NVS_TYPE_U8 : 0x01, /*!< Type uint8_t */
NVS_TYPE_I8 : 0x11, /*!< Type int8_t */
NVS_TYPE_U16 : 0x02, /*!< Type uint16_t */
NVS_TYPE_I16 : 0x12, /*!< Type int16_t */
NVS_TYPE_U32 : 0x04, /*!< Type uint32_t */
NVS_TYPE_I32 : 0x14, /*!< Type int32_t */
NVS_TYPE_U64 : 0x08, /*!< Type uint64_t */
NVS_TYPE_I64 : 0x18, /*!< Type int64_t */
NVS_TYPE_STR : 0x21, /*!< Type string */
NVS_TYPE_BLOB : 0x42, /*!< Type blob */
NVS_TYPE_ANY : 0xff /*!< Must be last */
} ;
var task_state_t = {
0 : "eRunning", /*!< A task is querying the state of itself, so must be running. */
1 : "eReady", /*!< The task being queried is in a read or pending ready list. */
2 : "eBlocked", /*!< The task being queried is in the Blocked state. */
3 : "eSuspended", /*!< The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
4 : "eDeleted"
}
var releaseURL = 'https://api.github.com/repos/sle118/squeezelite-esp32/releases';
var recovery = false;
var enableAPTimer = true;
@@ -19,7 +40,6 @@ var commandHeader = 'squeezelite -b 500:2000 -d all=info ';
var pname, ver, otapct, otadsc;
var blockAjax = false;
var blockFlashButton = false;
var lastMsg = '';
var apList = null;
var selectedSSID = "";
@@ -189,20 +209,29 @@ $(document).ready(function(){
$("input#autoexec-cb").on("click", function() {
var data = { 'timestamp': Date.now() };
autoexec = (this.checked)?1:0;
data['autoexec'] = autoexec;
showMessage('please wait for the ESP32 to reboot', 'WARNING');
data['config'] = {};
data['config'] = {
autoexec : {
value : autoexec,
type : 33
}
}
showMessage('please wait for the ESP32 to reboot', 'MESSAGING_WARNING');
$.ajax({
url: '/config.json',
dataType: 'text',
method: 'POST',
cache: false,
headers: { "X-Custom-autoexec": autoexec },
// headers: { "X-Custom-autoexec": autoexec },
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data),
data: JSON.stringify(data),
error: function (xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
if (thrownError != '') showMessage(thrownError, 'ERROR');
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
},
complete: function(response) {
//var returnedResponse = JSON.parse(response.responseText);
@@ -219,7 +248,7 @@ $(document).ready(function(){
error: function (xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
if (thrownError != '') showMessage(thrownError, 'ERROR');
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
},
complete: function(response) {
console.log('reboot call completed');
@@ -232,20 +261,26 @@ $(document).ready(function(){
$("input#save-autoexec1").on("click", function() {
var data = { 'timestamp': Date.now() };
autoexec1 = $("#autoexec1").val();
data['autoexec1'] = autoexec1;
data['config'] = {};
data['config'] = {
autoexec1 : {
value : autoexec1,
type : 33
}
}
$.ajax({
url: '/config.json',
dataType: 'text',
method: 'POST',
cache: false,
headers: { "X-Custom-autoexec1": autoexec1 },
// headers: { "X-Custom-autoexec1": autoexec1 },
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data),
error: function (xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
if (thrownError != '') showMessage(thrownError, 'ERROR');
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
}
});
console.log('sent config JSON with headers:', autoexec1);
@@ -254,15 +289,19 @@ $(document).ready(function(){
$("input#save-gpio").on("click", function() {
var data = { 'timestamp': Date.now() };
var config = {};
var headers = {};
$("input.gpio").each(function() {
var id = $(this)[0].id;
var pin = $(this).val();
if (pin != '') {
headers["X-Custom-"+id] = pin;
data[id] = pin;
config[id] = {};
config[id].value = pin;
config[id].type = nvs_type_t.NVS_TYPE_STR;
}
});
data['config'] = config;
$.ajax({
url: '/config.json',
dataType: 'text',
@@ -274,7 +313,7 @@ $(document).ready(function(){
error: function (xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
if (thrownError != '') showMessage(thrownError, 'ERROR');
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
}
});
console.log('sent config JSON with headers:', JSON.stringify(headers));
@@ -284,23 +323,40 @@ $(document).ready(function(){
$("#save-nvs").on("click", function() {
var headers = {};
var data = { 'timestamp': Date.now() };
var config = {};
$("input.nvs").each(function() {
var key = $(this)[0].id;
var val = $(this).val();
var nvs_type = parseInt($(this)[0].attributes.nvs_type.nodeValue,10);
if (key != '') {
headers["X-Custom-"+key] = val;
data[key] = {};
data[key].value = val;
data[key].type = 33;
config[key] = {};
if(nvs_type == nvs_type_t.NVS_TYPE_U8
|| nvs_type == nvs_type_t.NVS_TYPE_I8
|| nvs_type == nvs_type_t.NVS_TYPE_U16
|| nvs_type == nvs_type_t.NVS_TYPE_I16
|| nvs_type == nvs_type_t.NVS_TYPE_U32
|| nvs_type == nvs_type_t.NVS_TYPE_I32
|| nvs_type == nvs_type_t.NVS_TYPE_U64
|| nvs_type == nvs_type_t.NVS_TYPE_I64) {
config[key].value = parseInt(val);
}
else {
config[key].value = val;
}
config[key].type = nvs_type;
}
});
var key = $("#nvs-new-key").val();
var val = $("#nvs-new-value").val();
if (key != '') {
headers["X-Custom-"+key] = val;
data[key] = {};
data[key].value = val;
// headers["X-Custom-" +key] = val;
config[key] = {};
config[key].value = val;
config[key].type = 33;
}
data['config'] = config;
$.ajax({
url: '/config.json',
dataType: 'text',
@@ -308,35 +364,64 @@ $(document).ready(function(){
cache: false,
headers: headers,
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data),
data : JSON.stringify(data),
error: function (xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
if (thrownError != '') showMessage(thrownError, 'ERROR');
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
}
});
console.log('sent config JSON with headers:', JSON.stringify(headers));
console.log('sent config JSON with data:', JSON.stringify(data));
});
$("#fwUpload").on("click", function() {
var upload_path = "/flash.json";
var fileInput = document.getElementById("flashfilename").files;
if (fileInput.length == 0) {
alert("No file selected!");
} else {
var file = fileInput[0];
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4) {
if (xhttp.status == 200) {
showMessage(xhttp.responseText, 'MESSAGING_INFO')
} else if (xhttp.status == 0) {
showMessage("Upload connection was closed abruptly!", 'MESSAGING_ERROR');
} else {
showMessage(xhttp.status + " Error!\n" + xhttp.responseText, 'MESSAGING_ERROR');
}
}
};
xhttp.open("POST", upload_path, true);
xhttp.send(file);
}
enableStatusTimer = true;
});
$("#flash").on("click", function() {
var data = { 'timestamp': Date.now() };
if (blockFlashButton) return;
blockFlashButton = true;
var url = $("#fwurl").val();
data['fwurl'] = url;
data['config'] = {
fwurl : {
value : url,
type : 33
}
};
$.ajax({
url: '/config.json',
dataType: 'text',
method: 'POST',
cache: false,
headers: { "X-Custom-fwurl": url },
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data),
data: JSON.stringify(data),
error: function (xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
if (thrownError != '') showMessage(thrownError, 'ERROR');
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
}
});
enableStatusTimer = true;
@@ -509,13 +594,17 @@ function performConnect(conntype){
dataType: 'text',
method: 'POST',
cache: false,
headers: { 'X-Custom-ssid': selectedSSID, 'X-Custom-pwd': pwd, 'X-Custom-host_name': dhcpname },
// headers: { 'X-Custom-ssid': selectedSSID, 'X-Custom-pwd': pwd, 'X-Custom-host_name': dhcpname },
contentType: 'application/json; charset=utf-8',
data: { 'timestamp': Date.now()},
data: JSON.stringify({ 'timestamp': Date.now(),
'ssid' : selectedSSID,
'pwd' : pwd,
'host_name' : dhcpname
}),
error: function (xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
if (thrownError != '') showMessage(thrownError, 'ERROR');
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
}
});
@@ -564,11 +653,76 @@ function refreshAPHTML(data){
$( "#wifi-list" ).html(h)
}
function getMessages() {
$.getJSON("/messages.json?1", function(data) {
data.forEach(function(msg) {
var msg_age = msg["current_time"] - msg["sent_time"];
var msg_time = new Date();
msg_time.setTime( msg_time.getTime() - msg_age );
switch (msg["class"]) {
case "MESSAGING_CLASS_OTA":
//message: "{"ota_dsc":"Erasing flash complete","ota_pct":0}"
var ota_data = JSON.parse(msg["message"]);
if (ota_data.hasOwnProperty('ota_pct') && ota_data['ota_pct'] != 0){
otapct = ota_data['ota_pct'];
$('.progress-bar').css('width', otapct+'%').attr('aria-valuenow', otapct);
$('.progress-bar').html(otapct+'%');
}
if (ota_data.hasOwnProperty('ota_dsc') && ota_data['ota_dsc'] != ''){
otadsc = ota_data['ota_dsc'];
$("span#flash-status").html(otadsc);
if (otadsc.match(/Error:/) || otapct > 95) {
blockFlashButton = false;
enableStatusTimer = true;
}
}
break;
case "MESSAGING_CLASS_STATS":
// for task states, check structure : task_state_t
var stats_data = JSON.parse(msg["message"]);
console.log(msg_time.toLocaleString() + " - Number of tasks on the ESP32: " + stats_data["ntasks"]);
var stats_tasks = stats_data["tasks"];
console.log(msg_time.toLocaleString() + '\tname' + '\tcpu' + '\tstate'+ '\tminstk'+ '\tbprio'+ '\tcprio'+ '\tnum' );
stats_tasks.forEach(function(task) {
console.log(msg_time.toLocaleString() + '\t' + task["nme"] + '\t'+ task["cpu"] + '\t'+ task_state_t[task["st"]]+ '\t'+ task["minstk"]+ '\t'+ task["bprio"]+ '\t'+ task["cprio"]+ '\t'+ task["num"]);
});
break;
case "MESSAGING_CLASS_SYSTEM":
showMessage(msg["message"], msg["type"],msg_age);
$("#syslogTable").append(
"<tr class='"+msg["type"]+"'>"+
"<td>"+msg_time.toLocaleString()+"</td>"+
"<td>"+msg["message"]+"</td>"+
"</tr>"
);
break;
default:
break;
}
});
})
.fail(function(xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
});
/*
Minstk is minimum stack space left
Bprio is base priority
cprio is current priority
nme is name
st is task state. I provided a "typedef" that you can use to convert to text
cpu is cpu percent used
*/
}
function checkStatus(){
RepeatCheckStatusInterval();
if (!enableStatusTimer) return;
if (blockAjax) return;
blockAjax = true;
getMessages();
$.getJSON( "/status.json", function( data ) {
if (data.hasOwnProperty('ssid') && data['ssid'] != ""){
if (data["ssid"] === selectedSSID){
@@ -659,20 +813,24 @@ function checkStatus(){
$("#otadiv").show();
$('a[href^="#tab-audio"]').hide();
$('a[href^="#tab-gpio"]').show();
$('#uploaddiv').show();
$("footer.footer").removeClass('sl');
$("footer.footer").addClass('recovery');
$("#boot-button").html('Reboot');
$("#boot-form").attr('action', '/reboot_ota.json');
enableStatusTimer = true;
} else {
recovery = false;
$("#otadiv").hide();
$('a[href^="#tab-audio"]').show();
$('a[href^="#tab-gpio"]').hide();
$('#uploaddiv').hide();
$("footer.footer").removeClass('recovery');
$("footer.footer").addClass('sl');
$("#boot-button").html('Recovery');
$("#boot-form").attr('action', '/recovery.json');
enableStatusTimer = false;
}
}
@@ -683,29 +841,10 @@ function checkStatus(){
ver = data['version'];
$("span#foot-fw").html("fw: <strong>"+ver+"</strong>, mode: <strong>"+pname+"</strong>");
}
if (data.hasOwnProperty('ota_pct') && data['ota_pct'] != 0){
otapct = data['ota_pct'];
$('.progress-bar').css('width', otapct+'%').attr('aria-valuenow', otapct);
$('.progress-bar').html(otapct+'%');
}
if (data.hasOwnProperty('ota_dsc') && data['ota_dsc'] != ''){
otadsc = data['ota_dsc'];
$("span#flash-status").html(otadsc);
if (otadsc.match(/Error:/) || otapct > 95) {
blockFlashButton = false;
enableStatusTimer = true;
}
} else {
else {
$("span#flash-status").html('');
}
if (data.hasOwnProperty('message') && data['message'] != ''){
var msg = data['message'].text;
var severity = data['message'].severity;
if (msg != lastMsg) {
showMessage(msg, severity);
lastMsg = msg;
}
}
if (data.hasOwnProperty('Voltage')) {
var voltage = data['Voltage'];
var layer;
@@ -735,7 +874,7 @@ function checkStatus(){
.fail(function(xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
if (thrownError != '') showMessage(thrownError, 'ERROR');
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
blockAjax = false;
});
}
@@ -770,7 +909,7 @@ function getConfig() {
"<tr>"+
"<td>"+key+"</td>"+
"<td class='value'>"+
"<input type='text' class='form-control nvs' id='"+key+"'>"+
"<input type='text' class='form-control nvs' id='"+key+"' nvs_type="+data[key].type+" >"+
"</td>"+
"</tr>"
);
@@ -783,7 +922,7 @@ function getConfig() {
"<input type='text' class='form-control' id='nvs-new-key' placeholder='new key'>"+
"</td>"+
"<td>"+
"<input type='text' class='form-control' id='nvs-new-value' placeholder='new value'>"+
"<input type='text' class='form-control' id='nvs-new-value' placeholder='new value' nvs_type=33 >"+ // todo: provide a way to choose field type
"</td>"+
"</tr>"
);
@@ -791,19 +930,23 @@ function getConfig() {
.fail(function(xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
if (thrownError != '') showMessage(thrownError, 'ERROR');
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
blockAjax = false;
});
}
function showMessage(message, severity) {
if (severity == 'INFO') {
function showMessage(message, severity, age=0) {
if (severity == 'MESSAGING_INFO') {
$('#message').css('background', '#6af');
} else if (severity == 'WARNING') {
} else if (severity == 'MESSAGING_WARNING') {
$('#message').css('background', '#ff0');
} else if (severity == 'MESSAGING_ERROR' ) {
$('#message').css('background', '#f00');
} else {
$('#message').css('background', '#f00');
}
$('#message').html(message);
$("#content").fadeTo("slow", 0.3, function() {
$("#message").show(500).delay(5000).hide(500, function() {
@@ -815,3 +958,6 @@ function showMessage(message, severity) {
function inRange(x, min, max) {
return ((x-min)*(x-max) <= 0);
}

View File

@@ -7,14 +7,8 @@
# please read the SDK documents if you need to do this.
#
COMPONENT_EMBED_FILES := style.css code.js index.html bootstrap.min.css.gz jquery.min.js.gz popper.min.js.gz bootstrap.min.js.gz
#CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG
CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO \
-I$(COMPONENT_PATH)/../tools
COMPONENT_ADD_INCLUDEDIRS := .
COMPONENT_ADD_INCLUDEDIRS += $(COMPONENT_PATH)/../tools
COMPONENT_ADD_INCLUDEDIRS += $(COMPONENT_PATH)/../squeezelite-ota
COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/main/
COMPONENT_ADD_INCLUDEDIRS := .
COMPONENT_EXTRA_INCLUDES += $(IDF_PATH)/components/esp_http_server/src $(IDF_PATH)/components/esp_http_server/src/port/esp32 $(IDF_PATH)/components/esp_http_server/src/util $(IDF_PATH)/components/esp_http_server/src/
CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO

View File

@@ -73,11 +73,7 @@ void dns_server_stop(){
}
void dns_server(void *pvParameters) {
struct sockaddr_in sa, ra;
/* Set redirection DNS hijack to the access point IP */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,148 @@
/*
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 "esp_http_server.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"
#include "esp_vfs.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ESP_LOGE_LOC(t,str, ...) ESP_LOGE(t, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define ESP_LOGI_LOC(t,str, ...) ESP_LOGI(t, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define ESP_LOGD_LOC(t,str, ...) ESP_LOGD(t, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define ESP_LOGW_LOC(t,str, ...) ESP_LOGW(t, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define ESP_LOGV_LOC(t,str, ...) ESP_LOGV(t, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__)
esp_err_t root_get_handler(httpd_req_t *req);
esp_err_t resource_filehandler(httpd_req_t *req);
esp_err_t resource_filehandler(httpd_req_t *req);
esp_err_t resource_filehandler(httpd_req_t *req);
esp_err_t resource_filehandler(httpd_req_t *req);
esp_err_t resource_filehandler(httpd_req_t *req);
esp_err_t resource_filehandler(httpd_req_t *req);
esp_err_t ap_get_handler(httpd_req_t *req);
esp_err_t config_get_handler(httpd_req_t *req);
esp_err_t config_post_handler(httpd_req_t *req);
esp_err_t connect_post_handler(httpd_req_t *req);
esp_err_t connect_delete_handler(httpd_req_t *req);
esp_err_t reboot_ota_post_handler(httpd_req_t *req);
esp_err_t reboot_post_handler(httpd_req_t *req);
esp_err_t recovery_post_handler(httpd_req_t *req);
#if RECOVERY_APPLICATION
esp_err_t flash_post_handler(httpd_req_t *req);
#endif
esp_err_t status_get_handler(httpd_req_t *req);
esp_err_t messages_get_handler(httpd_req_t *req);
esp_err_t ap_scan_handler(httpd_req_t *req);
esp_err_t redirect_ev_handler(httpd_req_t *req);
esp_err_t redirect_200_ev_handler(httpd_req_t *req);
esp_err_t err_handler(httpd_req_t *req, httpd_err_code_t error);
#define SCRATCH_BUFSIZE (10240)
#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128)
typedef struct rest_server_context {
char base_path[ESP_VFS_PATH_MAX + 1];
char scratch[SCRATCH_BUFSIZE];
} rest_server_context_t;
/**
* @brief RTOS task for the HTTP server. Do not start manually.
* @see void http_server_start()
*/
void CODE_RAM_LOCATION http_server(void *pvParameters);
/* @brief helper function that processes one HTTP request at a time */
void CODE_RAM_LOCATION http_server_netconn_serve(struct netconn *conn);
/* @brief create the task for the http server */
esp_err_t CODE_RAM_LOCATION 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* CODE_RAM_LOCATION http_server_get_header(char *request, char *header_name, int *len);
void CODE_RAM_LOCATION strreplace(char *src, char *str, char *rep);
/* @brief lock the json config object */
bool http_server_lock_json_object(TickType_t xTicksToWait);
/* @brief unlock the json config object */
void http_server_unlock_json_object();
#define PROTECTED_JSON_CALL(a) if(http_server_lock_json_object( portMAX_DELAY )){ \ a; http_server_unlocklock_json_object(); } else{ ESP_LOGE(TAG, "could not get access to json mutex in wifi_scan"); }
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -4,19 +4,12 @@
<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" />
<link rel="stylesheet" href="/bootstrap.css">
<link rel="stylesheet" href="res/test/bootstrap.min.css"> <!-- TODO delete -->
<link rel="stylesheet" href="/style.css">
<script src="/jquery.js"></script>
<script src="/popper.js"></script>
<script src="/bootstrap.js"></script>
<script src="res/test/jquery.min.js"></script> <!-- TODO delete -->
<script src="res/test/popper.min.js"></script> <!-- TODO delete -->
<script src="res/test/bootstrap.min.js"></script> <!-- TODO delete -->
<script src="/code.js"></script>
<link rel="stylesheet" href="/res/bootstrap.css">
<link rel="stylesheet" href="/res/style.css">
<script src="/res/jquery.js"></script>
<script src="/res/popper.js"></script>
<script src="/res/bootstrap.js"></script>
<script src="/res/code.js"></script>
<title>esp32-wifi-manager</title>
</head>
@@ -74,7 +67,7 @@
<a class="nav-link" data-toggle="tab" href="#tab-firmware">Firmware</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#tab-gpio">GPIO</a>
<a class="nav-link" data-toggle="tab" href="#tab-syslog">Syslog</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#tab-nvs">NVS editor</a>
@@ -197,7 +190,7 @@
</div>
</div>
</div>
</div>
</div> <!-- wifi -->
<div class="tab-pane fade" id="tab-audio">
<div id="audioout">
@@ -247,72 +240,7 @@
<label class="custom-control-label" for="autoexec-cb"></label>
</div>
<br />
</div>
<div class="tab-pane fade" id="tab-gpio">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Signal</th>
<th scope="col">I2S pin</th>
<th scope="col">SPDIF pin</th>
</tr>
</thead>
<tbody id="gpioTable">
<tr>
<td>Bit clock</td>
<td>
<input type="text" class="form-control gpio" id="gpio-i2s-bc" maxlength="2" size="2">
</td>
<td>
<input type="text" class="form-control gpio" id="gpio-spdif-bc" maxlength="2" size="2">
</td>
</tr>
<tr>
<td>Word select</td>
<td>
<input type="text" class="form-control gpio" id="gpio-i2s-ws" maxlength="2" size="2">
</td>
<td>
<input type="text" class="form-control gpio" id="gpio-spdif-ws" maxlength="2" size="2">
</td>
</tr>
<tr>
<td>Data</td>
<td>
<input type="text" class="form-control gpio" id="gpio-i2s-data" maxlength="2" size="2">
</td>
<td>
<input type="text" class="form-control gpio" id="gpio-spdif-data" maxlength="2" size="2">
</td>
</tr>
</tbody>
</table>
<div class="buttons">
<input id="save-gpio" type="button" class="btn btn-success" value="Save" />
</div>
</div>
<div class="tab-pane fade" id="tab-nvs">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Key</th>
<th scope="col">Value</th>
</tr>
</thead>
<tbody id="nvsTable">
</tbody>
</table>
<div class="buttons">
<div id="boot-div">
<form id="reboot-form" action="/reboot.json" method="post" target="dummyframe">
<button id="reboot-button" type="submit" class="btn btn-primary">Reboot</button>
</form>
</div>
<input id="save-nvs" type="button" class="btn btn-success" value="Save" />
</div>
</div>
</div> <!-- audio -->
<div class="tab-pane fade" id="tab-firmware">
<div id="boot-div">
@@ -343,21 +271,17 @@
</table>
<h2>Firmware URL:</h2>
<textarea id="fwurl" maxlength="350"></textarea>
<!--
<br />OR<br />
<div class="input-group mb-3" id="upload">
<div class="custom-file">
<input type="file" class="custom-file-input" id="inputGroupFile01">
<label class="custom-file-label" for="inputGroupFile01"></label>
</div>
<div class="input-group-append">
<span class="input-group-text" id="fwUpload">Upload</span>
</div>
</div>
-->
<div class="buttons">
<input type="button" id="flash" class="btn btn-danger" value="Flash!" /><span id="flash-status"></span>
<input type="button" id="flash" class="btn btn-danger" value="Flash!" /><span id="flash-status"></span>
</div>
<p>OR</p>
<div class="form-group">
<input type="file" class="form-control-file" id="flashfilename" aria-describedby="fileHelp">
<div class="buttons">
<button type="button" class="btn btn-danger" id="fwUpload">Upload!</button>
</div>
</div>
<div id="otadiv">
<div class="progress" id="progress">
<div class="progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" style="width:0%">
@@ -365,11 +289,48 @@
</div>
</div>
</div>
</div>
</div> <!-- firmware -->
<div class="tab-pane fade" id="tab-syslog">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Timestamp</th>
<th scope="col">Message</th>
</tr>
</thead>
<tbody id="syslogTable">
</tbody>
</table>
<div class="buttons">
<input id="clear-syslog" type="button" class="btn btn-danger btn-sm" value="Clear" />
</div>
</div> <!-- syslog -->
<div class="tab-pane fade" id="tab-nvs">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Key</th>
<th scope="col">Value</th>
</tr>
</thead>
<tbody id="nvsTable">
</tbody>
</table>
<div class="buttons">
<div id="boot-div">
<form id="reboot-form" action="/reboot.json" method="post" target="dummyframe">
<button id="reboot-button" type="submit" class="btn btn-primary">Reboot</button>
</form>
</div>
<input id="save-nvs" type="button" class="btn btn-success" value="Save" />
</div>
</div> <!-- nvs -->
<div class="tab-pane fade" id="tab-credits">
<div class="jumbotron">
<p><strong><a href="https://github.com/sle118/squeezelite-esp32">squeezelite-esp32</a></strong>, &copy; 2019, philippe44, sle118, daduke<br />Licensed under the GPL</p>
<p><strong><a href="https://github.com/sle118/squeezelite-esp32">squeezelite-esp32</a></strong>, &copy; 2020, philippe44, sle118, daduke<br />Licensed under the GPL</p>
<p>
This app would not be possible without the following libraries:
</p>
@@ -388,7 +349,7 @@
<input type="checkbox" class="custom-control-input" id="show-nvs" checked="checked">
<label class="custom-control-label" for="show-nvs"></label>
</div>
</div>
</div> <!-- credits -->
</div>
<footer class="footer"><span id="foot-fw"></span><span id="foot-wifi"></span></footer>
<iframe width="0" height="0" border="0" name="dummyframe" id="dummyframe"></iframe>

View File

@@ -212,12 +212,6 @@ input[type='text'], input[type='password'], textarea {
padding: 4px;
}
input.gpio {
width: 2em;
color: #000;
height: 1.8em;
}
.custom-switch {
margin-left: 8px;
}
@@ -273,6 +267,18 @@ textarea#autoexec1, textarea#fwurl, div#upload {
width: 80%;
}
table tr.MESSAGING_INFO {
background: #123;
}
table tr.MESSAGING_WARNING {
background: #330;
}
table tr.MESSAGING_ERROR {
background: #300;
}
input, textarea {
border-radius: 3px;
border: 1px solid transparent;

View File

@@ -37,7 +37,6 @@ Contains the freeRTOS task and all necessary support
#include <stdbool.h>
#include "dns_server.h"
#include "http_server.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
@@ -61,6 +60,9 @@ Contains the freeRTOS task and all necessary support
#include "platform_config.h"
#include "trace.h"
#include "cmd_system.h"
#include "messaging.h"
#include "http_server_handlers.h"
#include "monitor.h"
#include "globdefs.h"
@@ -69,7 +71,7 @@ Contains the freeRTOS task and all necessary support
#endif
#define STR_OR_BLANK(p) p==NULL?"":p
#define FREE_AND_NULL(p) if(p!=NULL){ free(p); p=NULL;}
/* objects used to manipulate the main queue of events */
QueueHandle_t wifi_manager_queue;
SemaphoreHandle_t wifi_manager_json_mutex = NULL;
@@ -83,7 +85,7 @@ char *ip_info_json = NULL;
char * release_url=NULL;
cJSON * ip_info_cjson=NULL;
wifi_config_t* wifi_manager_config_sta = NULL;
static update_reason_code_t last_update_reason_code=0;
static int32_t total_connected_time=0;
static int64_t last_connected=0;
@@ -202,9 +204,6 @@ bool isGroupBitSet(uint8_t bit){
EventBits_t uxBits= xEventGroupGetBits(wifi_manager_event_group);
return (uxBits & bit);
}
void wifi_manager_refresh_ota_json(){
wifi_manager_send_message(EVENT_REFRESH_OTA, NULL);
}
void wifi_manager_scan_async(){
wifi_manager_send_message(ORDER_START_WIFI_SCAN, NULL);
@@ -357,6 +356,7 @@ esp_err_t wifi_manager_save_sta_config(){
esp_err = nvs_commit(handle);
if (esp_err != ESP_OK) {
ESP_LOGE(TAG, "Unable to commit changes. Error %s", esp_err_to_name(esp_err));
messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Unable to save wifi credentials. %s",esp_err_to_name(esp_err));
return esp_err;
}
nvs_close(handle);
@@ -449,8 +449,6 @@ cJSON * wifi_manager_get_basic_info(cJSON **old){
cJSON_AddItemToObject(root, "version", cJSON_CreateString(desc->version));
if(release_url !=NULL) cJSON_AddItemToObject(root, "release_url", cJSON_CreateString(release_url));
cJSON_AddNumberToObject(root,"recovery", is_recovery_running?1:0);
cJSON_AddItemToObject(root, "ota_dsc", cJSON_CreateString(ota_get_status()));
cJSON_AddNumberToObject(root,"ota_pct", ota_get_pct_complete() );
cJSON_AddItemToObject(root, "Jack", cJSON_CreateString(jack_inserted_svc() ? "1" : "0"));
cJSON_AddNumberToObject(root,"Voltage", battery_value_svc());
cJSON_AddNumberToObject(root,"disconnect_count", num_disconnect );
@@ -479,12 +477,6 @@ void wifi_manager_generate_ip_info_json(update_reason_code_t update_reason_code)
wifi_config_t *config = wifi_manager_get_wifi_sta_config();
ip_info_cjson = wifi_manager_get_basic_info(&ip_info_cjson);
if(update_reason_code == UPDATE_OTA) {
update_reason_code = last_update_reason_code;
}
else {
last_update_reason_code = update_reason_code;
}
cJSON_AddNumberToObject(ip_info_cjson, "urc", update_reason_code);
if(config){
cJSON_AddItemToObject(ip_info_cjson, "ssid", cJSON_CreateString((char *)config->sta.ssid));
@@ -505,7 +497,7 @@ char * get_mac_string(uint8_t mac[6]){
char * macStr=malloc(LOCAL_MAC_SIZE);
memset(macStr, 0x00, LOCAL_MAC_SIZE);
snprintf(macStr, LOCAL_MAC_SIZE-1,MACSTR, MAC2STR(mac));
snprintf(macStr, LOCAL_MAC_SIZE,MACSTR, MAC2STR(mac));
return macStr;
}
@@ -855,20 +847,6 @@ void wifi_manager_connect_async(){
wifi_manager_send_message(ORDER_CONNECT_STA, (void*)CONNECTION_REQUEST_USER);
}
void set_status_message(message_severity_t severity, const char * message){
if(ip_info_cjson==NULL){
ip_info_cjson = wifi_manager_get_new_json(&ip_info_cjson);
}
if(ip_info_cjson==NULL){
ESP_LOGE(TAG, "Error setting status message. Unable to allocate cJSON.");
return;
}
cJSON * item=cJSON_GetObjectItem(ip_info_cjson, "message");
item = wifi_manager_get_new_json(&item);
cJSON_AddItemToObject(item, "severity", cJSON_CreateString(severity==INFO?"INFO":severity==WARNING?"WARNING":severity==ERROR?"ERROR":"" ));
cJSON_AddItemToObject(item, "text", cJSON_CreateString(message));
}
char* wifi_manager_alloc_get_ip_info_json(){
return cJSON_PrintUnformatted(ip_info_cjson);
@@ -1138,12 +1116,6 @@ void wifi_manager( void * pvParameters ){
ESP_LOGD(TAG, "Done Invoking SCAN DONE callback");
}
break;
case EVENT_REFRESH_OTA:
if(wifi_manager_lock_json_buffer( portMAX_DELAY )){
wifi_manager_generate_ip_info_json( UPDATE_OTA );
wifi_manager_unlock_json_buffer();
}
break;
case ORDER_START_WIFI_SCAN:
ESP_LOGD(TAG, "MESSAGE: ORDER_START_WIFI_SCAN");
@@ -1153,6 +1125,7 @@ void wifi_manager( void * pvParameters ){
if(esp_wifi_scan_start(&scan_config, false)!=ESP_OK){
ESP_LOGW(TAG, "Unable to start scan; wifi is trying to connect");
// set_status_message(WARNING, "Wifi Connecting. Cannot start scan.");
messaging_post_message(MESSAGING_WARNING,MESSAGING_CLASS_SYSTEM,"Wifi connecting. Cannot start scan.");
}
else {
xEventGroupSetBits(wifi_manager_event_group, WIFI_MANAGER_SCAN_BIT);
@@ -1358,6 +1331,8 @@ void wifi_manager( void * pvParameters ){
else{
/* lost connection ? */
ESP_LOGE(TAG, "WiFi Connection lost.");
messaging_post_message(MESSAGING_WARNING,MESSAGING_CLASS_SYSTEM,"WiFi Connection lost");
if(wifi_manager_lock_json_buffer( portMAX_DELAY )){
wifi_manager_generate_ip_info_json( UPDATE_LOST_CONNECTION );
wifi_manager_unlock_json_buffer();
@@ -1439,7 +1414,7 @@ void wifi_manager( void * pvParameters ){
if(cb_ptr_arr[msg.code]) (*cb_ptr_arr[msg.code])(NULL);
break;
case UPDATE_CONNECTION_OK:
/* refresh JSON with the new ota data */
/* refresh JSON */
if(wifi_manager_lock_json_buffer( portMAX_DELAY )){
/* generate the connection info with success */
wifi_manager_generate_ip_info_json( UPDATE_CONNECTION_OK );

View File

@@ -42,6 +42,17 @@ extern "C" {
#include "squeezelite-ota.h"
#include "cJSON.h"
#ifndef RECOVERY_APPLICATION
#error "RECOVERY_APPLICATION not defined. Defaulting to squeezelite"
#endif
#if RECOVERY_APPLICATION==1
#elif RECOVERY_APPLICATION==0
#else
#error "unknown configuration"
#endif
/**
* @brief Defines the maximum size of a SSID name. 32 is IEEE standard.
@@ -187,12 +198,11 @@ typedef enum message_code_t {
EVENT_STA_DISCONNECTED = 12,
EVENT_SCAN_DONE = 13,
EVENT_STA_GOT_IP = 14,
EVENT_REFRESH_OTA = 15,
ORDER_RESTART_OTA = 16,
ORDER_RESTART_RECOVERY = 17,
ORDER_RESTART_OTA_URL = 18,
ORDER_RESTART = 19,
MESSAGE_CODE_COUNT = 20 /* important for the callback array */
ORDER_RESTART_OTA = 15,
ORDER_RESTART_RECOVERY = 16,
ORDER_RESTART_OTA_URL = 17,
ORDER_RESTART = 18,
MESSAGE_CODE_COUNT = 19 /* important for the callback array */
}message_code_t;
@@ -215,8 +225,7 @@ typedef enum update_reason_code_t {
UPDATE_CONNECTION_OK = 0,
UPDATE_FAILED_ATTEMPT = 1,
UPDATE_USER_DISCONNECT = 2,
UPDATE_LOST_CONNECTION = 3,
UPDATE_OTA=4
UPDATE_LOST_CONNECTION = 3
}update_reason_code_t;
typedef enum connection_request_made_by_code_t{

View File

@@ -0,0 +1,165 @@
/*
* Squeezelite for esp32
*
* (c) Sebastien 2019
* Philippe G. 2019, philippe_44@outlook.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "http_server_handlers.h"
#include "esp_log.h"
#include "esp_http_server.h"
#include "_esp_http_server.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "config.h"
#include "messaging.h"
static const char TAG[] = "http_server";
static httpd_handle_t _server = NULL;
rest_server_context_t *rest_context = NULL;
RingbufHandle_t messaging=NULL;
void register_common_handlers(httpd_handle_t server){
httpd_uri_t res_get = { .uri = "/res/*", .method = HTTP_GET, .handler = resource_filehandler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &res_get);
}
void register_regular_handlers(httpd_handle_t server){
httpd_uri_t root_get = { .uri = "/", .method = HTTP_GET, .handler = root_get_handler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &root_get);
httpd_uri_t ap_get = { .uri = "/ap.json", .method = HTTP_GET, .handler = ap_get_handler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &ap_get);
httpd_uri_t scan_get = { .uri = "/scan.json", .method = HTTP_GET, .handler = ap_scan_handler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &scan_get);
httpd_uri_t config_get = { .uri = "/config.json", .method = HTTP_GET, .handler = config_get_handler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &config_get);
httpd_uri_t status_get = { .uri = "/status.json", .method = HTTP_GET, .handler = status_get_handler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &status_get);
httpd_uri_t messages_get = { .uri = "/messages.json", .method = HTTP_GET, .handler = messages_get_handler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &messages_get);
httpd_uri_t config_post = { .uri = "/config.json", .method = HTTP_POST, .handler = config_post_handler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &config_post);
httpd_uri_t connect_post = { .uri = "/connect.json", .method = HTTP_POST, .handler = connect_post_handler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &connect_post);
httpd_uri_t reboot_ota_post = { .uri = "/reboot_ota.json", .method = HTTP_POST, .handler = reboot_ota_post_handler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &reboot_ota_post);
httpd_uri_t reboot_post = { .uri = "/reboot.json", .method = HTTP_POST, .handler = reboot_post_handler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &reboot_post);
httpd_uri_t recovery_post = { .uri = "/recovery.json", .method = HTTP_POST, .handler = recovery_post_handler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &recovery_post);
httpd_uri_t connect_delete = { .uri = "/connect.json", .method = HTTP_DELETE, .handler = connect_delete_handler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &connect_delete);
#if RECOVERY_APPLICATION
httpd_uri_t flash_post = { .uri = "/flash.json", .method = HTTP_POST, .handler = flash_post_handler, .user_ctx = rest_context };
httpd_register_uri_handler(server, &flash_post);
#endif
// from https://github.com/tripflex/wifi-captive-portal/blob/master/src/mgos_wifi_captive_portal.c
// https://unix.stackexchange.com/questions/432190/why-isnt-androids-captive-portal-detection-triggering-a-browser-window
// Known HTTP GET requests to check for Captive Portal
///kindle-wifi/wifiredirect.html Kindle when requested with com.android.captiveportallogin
///kindle-wifi/wifistub.html Kindle before requesting with captive portal login window (maybe for detection?)
httpd_uri_t connect_redirect_1 = { .uri = "/mobile/status.php", .method = HTTP_GET, .handler = redirect_200_ev_handler, .user_ctx = rest_context };// Android 8.0 (Samsung s9+)
httpd_register_uri_handler(server, &connect_redirect_1);
httpd_uri_t connect_redirect_2 = { .uri = "/generate_204", .method = HTTP_GET, .handler = redirect_200_ev_handler, .user_ctx = rest_context };// Android
httpd_register_uri_handler(server, &connect_redirect_2);
httpd_uri_t connect_redirect_3 = { .uri = "/gen_204", .method = HTTP_GET, .handler = redirect_ev_handler, .user_ctx = rest_context };// Android 9.0
httpd_register_uri_handler(server, &connect_redirect_3);
// httpd_uri_t connect_redirect_4 = { .uri = "/ncsi.txt", .method = HTTP_GET, .handler = redirect_ev_handler, .user_ctx = rest_context };// Windows
// httpd_register_uri_handler(server, &connect_redirect_4);
httpd_uri_t connect_redirect_5 = { .uri = "/hotspot-detect.html", .method = HTTP_GET, .handler = redirect_ev_handler, .user_ctx = rest_context }; // iOS 8/9
httpd_register_uri_handler(server, &connect_redirect_5);
httpd_uri_t connect_redirect_6 = { .uri = "/library/test/success.html", .method = HTTP_GET, .handler = redirect_ev_handler, .user_ctx = rest_context };// iOS 8/9
httpd_register_uri_handler(server, &connect_redirect_6);
httpd_uri_t connect_redirect_7 = { .uri = "/hotspotdetect.html", .method = HTTP_GET, .handler = redirect_ev_handler, .user_ctx = rest_context }; // iOS
httpd_register_uri_handler(server, &connect_redirect_7);
httpd_uri_t connect_redirect_8 = { .uri = "/success.txt", .method = HTTP_GET, .handler = redirect_ev_handler, .user_ctx = rest_context }; // OSX
httpd_register_uri_handler(server, &connect_redirect_8);
ESP_LOGD(TAG,"Registering default error handler for 404");
httpd_register_err_handler(server, HTTPD_404_NOT_FOUND,&err_handler);
}
esp_err_t http_server_start()
{
ESP_LOGI(TAG, "Initializing HTTP Server");
messaging = messaging_register_subscriber(10, "http_server");
rest_context = calloc(1, sizeof(rest_server_context_t));
if(rest_context==NULL){
ESP_LOGE(TAG,"No memory for http context");
return ESP_FAIL;
}
strlcpy(rest_context->base_path, "/res/", sizeof(rest_context->base_path));
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.max_uri_handlers = 25;
config.max_open_sockets = 5;
config.uri_match_fn = httpd_uri_match_wildcard;
//todo: use the endpoint below to configure session token?
// config.open_fn
ESP_LOGI(TAG, "Starting HTTP Server");
esp_err_t err= __httpd_start(&_server, &config);
if(err != ESP_OK){
ESP_LOGE_LOC(TAG,"Start server failed");
}
else {
register_common_handlers(_server);
register_regular_handlers(_server);
}
return err;
}
/* Function to free context */
void adder_free_func(void *ctx)
{
ESP_LOGI(TAG, "/adder Free Context function called");
free(ctx);
}
void stop_webserver(httpd_handle_t server)
{
// Stop the httpd server
httpd_stop(server);
}

113
eclipse_make_wrapper.py Normal file
View File

@@ -0,0 +1,113 @@
#!/usr/bin/env python
#
# Wrapper to run make and preprocess any paths in the output from MSYS Unix-style paths
# to Windows paths, for Eclipse
from __future__ import print_function, division
import sys
import subprocess
import os.path
import os
import re
import glob
from test import test_cmd_line
#UNIX_PATH_RE = re.compile(r'(([a-zA-Z]{1}[:]{1}){0,1}[/\\][^\s\'\"\t\[\(]+)+')
UNIX_PATH_RE = re.compile(r'(([a-zA-Z]{1}[:]{1}){0,1}[/\\][^\s\'\"\t\[\(]+(?![^\\/\n]*$)[/\\]?)')
INCLUDE_PATH_RE = re.compile(r'-I[\s"]{0,}(.+?)["]{0,}(?=\s-\S)')
INCLUDE_PATH_ADJ_RE = re.compile(r'^([/]opt[/]esp-idf[/]){1}(.*)')
INCLUDE_PATH_ADJ2_RE = re.compile(r'^([/]c[/]){1}(.*)')
paths = {}
names = []
idf_path= os.environ.get('IDF_PATH').replace("/", "\\")
cwd_path= os.environ.get('CWD')
pwd_path= os.environ.get('PWD')
def check_path(path):
try:
return paths[path]
except KeyError:
pass
paths[path] = path
winpath =path
if not os.path.exists(winpath):
# cache as failed, replace with success if it works
if re.match(INCLUDE_PATH_ADJ2_RE, path) is not None:
winpath = INCLUDE_PATH_ADJ2_RE.sub(r'c:/\2',path) #replace /c/
try:
winpath = subprocess.check_output(["cygpath", "-w", winpath]).strip()
except subprocess.CalledProcessError:
return path # something went wrong running cygpath, assume this is not a path!
if not os.path.exists(winpath):
if not os.path.exists(winpath):
winpath=idf_path + '\\' + re.sub(r'^[/\\]opt[/\\](esp-idf[/\\]){0,}', '', path, 1)
try:
winpath = subprocess.check_output(["cygpath", "-w", winpath]).strip()
except subprocess.CalledProcessError:
return path # something went wrong running cygpath, assume this is not a path!
if not os.path.exists(winpath):
return path # not actually a valid path
winpath = winpath.replace("/", "\\") # make consistent with forward-slashes used elsewhere
paths[path] = winpath
#print("In path: {0}, out path: {1}".format(path,winpath) )
return winpath
def fix_paths(filename):
if re.match(r'.*[\\](.*$)',filename) is not None:
filename = re.findall(r'.*[\\](.*$)',filename)[0].replace("\\", "/")
return filename.rstrip()
def print_paths(path_list, file_name, source_file):
new_path_list = list(set(path_list))
new_path_list.sort()
last_n = ''
cmd_line='xtensa-esp32-elf-gcc '
for n in new_path_list:
if re.match(INCLUDE_PATH_ADJ_RE, n) is not None:
n = INCLUDE_PATH_ADJ_RE.sub(idf_path+r"\2",n )
if re.match(INCLUDE_PATH_ADJ2_RE, n) is not None:
n = INCLUDE_PATH_ADJ2_RE.sub(r'c:/\2',n)
if last_n != n:
cmd_line = cmd_line + ' -I ' + n.rstrip()
last_n = n
if source_file:
cmd_line = cmd_line + ' -c ' + fix_paths(source_file)
cmd_line = cmd_line + ' -o ' + fix_paths(file_name)
print(cmd_line)
def extract_includes():
for filename in [y for x in os.walk('build') for y in glob.glob(os.path.join(x[0], '*.d'))]:
lines = []
source=''
with open(filename) as file_in:
for line in file_in:
if re.match(r'\S*(?=/[^/]*\.[h][p]?)',line) is not None:
lines.extend(re.findall(r'\S*(?=/[^/]*\.[h][p]?)/',line))
if re.match(r'\S*(?=\.[cC][pP]{0,})[^\\\s]*',line) is not None:
source = re.findall(r'\S*(?=\.[cC][pP]{0,})[^\\\s]*',line)[0]
print_paths(lines,filename,source )
def main():
cwd_path=check_path(os.getcwd())
os.environ['CWD']= cwd_path
os.environ['PWD']= cwd_path
idf_path= os.environ.get('IDF_PATH').replace("/", "\\")
cwd_path= os.environ.get('CWD')
pwd_path= os.environ.get('PWD')
print('Running custom script make in {}, IDF_PATH={}, CWD={}, PWD={}'.format(cwd_path,idf_path,cwd_path,pwd_path))
make = subprocess.Popen(["make"] + sys.argv[1:] + ["BATCH_BUILD=1"], stdout=subprocess.PIPE)
for line in iter(make.stdout.readline, ''):
line = re.sub(UNIX_PATH_RE, lambda m: check_path(m.group(0)), line)
names.extend(INCLUDE_PATH_RE.findall(line))
print(line.rstrip())
sys.exit(make.wait())
if __name__ == "__main__":
main()

View File

@@ -3,4 +3,3 @@ idf_component_register(SRC_DIRS .
INCLUDE_DIRS .
EMBED_FILES ../server_certs/github.pem
)

View File

@@ -6,10 +6,7 @@
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the SDK documents if you need to do this.
#
#CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG
CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO -DMODEL_NAME=SqueezeESP32
COMPONENT_ADD_INCLUDEDIRS += $(COMPONENT_PATH)/../tools \
$(COMPONENT_PATH)/../config
COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/components/tools/
LDFLAGS += -s
COMPONENT_EMBED_TXTFILES := ${PROJECT_PATH}/server_certs/github.pem
COMPONENT_EMBED_TXTFILES := ${PROJECT_PATH}/server_certs/github.pem
COMPONENT_ADD_INCLUDEDIRS := .

View File

@@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#include "platform_esp32.h"
#include "led.h"
#include <stdio.h>
@@ -40,7 +40,6 @@
#include "lwip/err.h"
#include "lwip/netdb.h"
#include "nvs_utilities.h"
#include "http_server.h"
#include "trace.h"
#include "wifi_manager.h"
#include "squeezelite-ota.h"
@@ -48,11 +47,12 @@
#include "platform_config.h"
#include "audio_controls.h"
#include "telnet.h"
#include "messaging.h"
static const char certs_namespace[] = "certificates";
static const char certs_key[] = "blob";
static const char certs_version[] = "version";
const char unknown_string_placeholder[] = "unknown";
EventGroupHandle_t wifi_event_group;
bool bypass_wifi_manager=false;
@@ -70,7 +70,9 @@ extern const uint8_t server_cert_pem_end[] asm("_binary_github_pem_end");
// as an exception _init function don't need include
extern void services_init(void);
extern void display_init(char *welcome);
bool is_recovery_running;
const char * str_or_unknown(const char * str) { return (str?str:unknown_string_placeholder); }
/* 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){
@@ -87,12 +89,14 @@ void cb_connection_got_ip(void *pvParameter){
}
ip.addr = ipInfo.ip.addr;
ESP_LOGI(TAG, "I have a connection!");
messaging_post_message(MESSAGING_INFO,MESSAGING_CLASS_SYSTEM,"Wifi connected");
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
bWifiConnected=true;
led_unpush(LED_GREEN);
}
void cb_connection_sta_disconnected(void *pvParameter){
led_blink_pushed(LED_GREEN, 250, 250);
messaging_post_message(MESSAGING_WARNING,MESSAGING_CLASS_SYSTEM,"Wifi disconnected");
bWifiConnected=false;
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
}
@@ -168,7 +172,7 @@ esp_err_t update_certificates(){
if ( (esp_err= nvs_get_str(handle, certs_version, NULL, &len)) == ESP_OK) {
str=(char *)malloc(len);
if ( (esp_err = nvs_get_str(handle, certs_version, str, &len)) == ESP_OK) {
printf("String associated with key '%s' is %s \n", certs_version, str);
ESP_LOGI(TAG,"String associated with key '%s' is %s", certs_version, str);
}
}
if(str!=NULL){
@@ -374,17 +378,15 @@ void app_main()
wifi_event_group = xEventGroupCreate();
ESP_LOGD(TAG,"Clearing CONNECTED_BIT from wifi group");
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
ESP_LOGI(TAG,"Registering default values");
register_default_nvs();
ESP_LOGD(TAG,"Configuring services");
ESP_LOGI(TAG,"Configuring services");
services_init();
ESP_LOGD(TAG,"Initializing display");
ESP_LOGI(TAG,"Initializing display");
display_init("SqueezeESP32");
if(!is_recovery_running){
ESP_LOGI(TAG,"Checking if certificates need to be updated");
update_certificates();
@@ -418,10 +420,12 @@ void app_main()
led_blink(LED_GREEN, 250, 250);
if(bypass_wifi_manager){
ESP_LOGW(TAG,"\n\nwifi manager is disabled. Please use wifi commands to connect to your wifi access point.\n\n");
ESP_LOGW(TAG,"*******************************************************************************************");
ESP_LOGW(TAG,"* wifi manager is disabled. Please use wifi commands to connect to your wifi access point.");
ESP_LOGW(TAG,"*******************************************************************************************");
}
else {
ESP_LOGW(TAG,"\n\nwifi manager is ENABLED. Starting...\n\n");
ESP_LOGI(TAG,"Starting Wifi Manager");
wifi_manager_start();
wifi_manager_set_callback(EVENT_STA_GOT_IP, &cb_connection_got_ip);
wifi_manager_set_callback(EVENT_STA_DISCONNECTED, &cb_connection_sta_disconnected);
@@ -445,4 +449,5 @@ void app_main()
}
free(fwurl);
}
messaging_post_message(MESSAGING_INFO,MESSAGING_CLASS_SYSTEM,"System started");
}

View File

@@ -480,7 +480,7 @@ CONFIG_ESP_EVENT_POST_FROM_ISR=y
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
CONFIG_HTTPD_MAX_REQ_HDR_LEN=512
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_MAX_URI_LEN=512
CONFIG_HTTPD_ERR_RESP_NO_DELAY=y
CONFIG_HTTPD_PURGE_BUF_LEN=32