Pārlūkot izejas kodu

Merge pull request #1336 from nodemcu/dev

1.5.1 master drop
Arnim Läuger 8 gadi atpakaļ
vecāks
revīzija
4911d2db8b
100 mainītis faili ar 5643 papildinājumiem un 2204 dzēšanām
  1. 0 117
      .cproject
  2. 33 0
      .github/ISSUE_TEMPLATE.md
  3. 11 0
      .github/PULL_REQUEST_TEMPLATE.md
  4. 9 0
      .gitignore
  5. 0 27
      .project
  6. 0 11
      .settings/language.settings.xml
  7. 0 67
      .settings/org.eclipse.cdt.codan.core.prefs
  8. 0 6
      .settings/org.eclipse.cdt.core.prefs
  9. 3 11
      .travis.yml
  10. 18 1
      CONTRIBUTING.md
  11. 38 10
      Makefile
  12. 59 610
      README.md
  13. 9 8
      app/Makefile
  14. 5 0
      app/coap/coap.h
  15. 2 0
      app/crypto/Makefile
  16. 33 0
      app/crypto/digests.c
  17. 11 0
      app/crypto/digests.h
  18. 131 0
      app/crypto/mech.c
  19. 30 0
      app/crypto/mech.h
  20. 14 0
      app/crypto/sdk-aes.h
  21. 1 0
      app/driver/Makefile
  22. 3 2
      app/driver/key.c
  23. 109 88
      app/driver/pwm.c
  24. 320 0
      app/driver/rotary.c
  25. 33 0
      app/driver/sigma_delta.c
  26. 40 35
      app/driver/spi.c
  27. 45 12
      app/driver/uart.c
  28. 5 4
      app/http/Makefile
  29. 545 0
      app/http/httpclient.c
  30. 97 0
      app/http/httpclient.h
  31. 3 0
      app/include/arch/cc.h
  32. 1 1
      app/include/driver/pwm.h
  33. 6 0
      app/include/driver/readline.h
  34. 26 0
      app/include/driver/rotary.h
  35. 11 0
      app/include/driver/sigma_delta.h
  36. 3 3
      app/include/driver/spi.h
  37. 0 13
      app/include/driver/spi_master.h
  38. 1 0
      app/include/driver/spi_register.h
  39. 1 1
      app/include/driver/uart.h
  40. 1 1
      app/include/lwip/app/espconn.h
  41. 1 1
      app/include/lwip/app/espconn_tcp.h
  42. 0 1
      app/include/lwipopts.h
  43. 1 1
      app/include/module.h
  44. 16 0
      app/include/nodemcu_mdns.h
  45. 43 1
      app/include/rom.h
  46. 1 0
      app/include/rtc/rtctime_internal.h
  47. 34 0
      app/include/task/task.h
  48. 2 2
      app/include/ucg_config.h
  49. 42 18
      app/include/user_config.h
  50. 26 12
      app/include/user_modules.h
  51. 4 4
      app/include/user_version.h
  52. 0 281
      app/json/jsonparse.c
  53. 0 296
      app/json/jsontree.c
  54. 0 1
      app/libc/Makefile
  55. 4 3
      app/libc/c_stdio.c
  56. 2 2
      app/libc/c_stdio.h
  57. 2 0
      app/libc/c_stdlib.h
  58. 113 0
      app/libc/c_string.c
  59. 6 0
      app/libc/c_string.h
  60. 2 1
      app/lua/Makefile
  61. 3 0
      app/lua/lauxlib.c
  62. 1 1
      app/lua/lauxlib.h
  63. 3 1
      app/lua/lua.c
  64. 1 0
      app/lua/lua.h
  65. 1 5
      app/lua/luaconf.h
  66. 36 3
      app/lwip/app/espconn.c
  67. 22 14
      app/lwip/app/espconn_tcp.c
  68. 1 1
      app/lwip/app/espconn_udp.c
  69. 24 13
      app/lwip/core/mdns.c
  70. 9 5
      app/lwip/core/tcp.c
  71. 1 0
      app/modules/.gitignore
  72. 3 1
      app/modules/Makefile
  73. 40 1
      app/modules/adc.c
  74. 153 0
      app/modules/am2320.c
  75. 115 0
      app/modules/apa102.c
  76. 442 0
      app/modules/bme280.c
  77. 3 3
      app/modules/bmp085.c
  78. 1 0
      app/modules/cjson.c
  79. 5 2
      app/modules/coap.c
  80. 236 4
      app/modules/crypto.c
  81. 159 0
      app/modules/encoder.c
  82. 9 4
      app/modules/enduser_setup.c
  83. 35 30
      app/modules/file.c
  84. 139 147
      app/modules/gpio.c
  85. 228 0
      app/modules/http.c
  86. 5 0
      app/modules/linit.c
  87. 97 0
      app/modules/mdns.c
  88. 410 203
      app/modules/mqtt.c
  89. 217 8
      app/modules/net.c
  90. 117 26
      app/modules/node.c
  91. 147 0
      app/modules/perf.c
  92. 9 2
      app/modules/pwm.c
  93. 1 0
      app/modules/rc.c
  94. 405 0
      app/modules/rotary.c
  95. 1 1
      app/modules/rtctime.c
  96. 87 0
      app/modules/sigma_delta.c
  97. 34 16
      app/modules/sntp.c
  98. 1 5
      app/modules/spi.c
  99. 425 0
      app/modules/struct.c
  100. 61 56
      app/modules/tmr.c

+ 0 - 117
.cproject

@@ -1,117 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
-	<storageModule moduleId="org.eclipse.cdt.core.settings">
-		<cconfiguration id="cdt.managedbuild.toolchain.gnu.cross.base.992255352">
-			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.cross.base.992255352" moduleId="org.eclipse.cdt.core.settings" name="Default">
-				<externalSettings/>
-				<extensions>
-					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
-				</extensions>
-			</storageModule>
-			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
-				<configuration artifactName="${ProjName}" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.cross.base.992255352" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
-					<folderInfo id="cdt.managedbuild.toolchain.gnu.cross.base.992255352.1209976491" name="/" resourcePath="">
-						<toolChain id="cdt.managedbuild.toolchain.gnu.cross.base.1901907892" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.base">
-							<option id="cdt.managedbuild.option.gnu.cross.prefix.1771691886" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix"/>
-							<option id="cdt.managedbuild.option.gnu.cross.path.1831805518" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path"/>
-							<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.2055058514" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
-							<builder enableAutoBuild="false" enabledIncrementalBuild="true" id="cdt.managedbuild.builder.gnu.cross.415628675" incrementalBuildTarget="all" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelBuildOn="false" superClass="cdt.managedbuild.builder.gnu.cross">
-								<outputEntries>
-									<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="outputPath" name=""/>
-								</outputEntries>
-							</builder>
-							<tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.972920545" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
-								<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1985687892" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
-							</tool>
-							<tool id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.36308842" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler">
-								<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1696212951" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
-							</tool>
-							<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.1453974065" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
-							<tool id="cdt.managedbuild.tool.gnu.cross.cpp.linker.2057246668" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
-								<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.2049169207" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
-									<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
-									<additionalInput kind="additionalinput" paths="$(LIBS)"/>
-								</inputType>
-							</tool>
-							<tool id="cdt.managedbuild.tool.gnu.cross.archiver.2111505538" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
-							<tool id="cdt.managedbuild.tool.gnu.cross.assembler.353143895" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler">
-								<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1235194497" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
-							</tool>
-						</toolChain>
-					</folderInfo>
-					<folderInfo id="cdt.managedbuild.toolchain.gnu.cross.base.992255352.588851457" name="/" resourcePath="bin">
-						<toolChain id="cdt.managedbuild.toolchain.gnu.cross.base.255773949" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.base" unusedChildren="">
-							<option id="cdt.managedbuild.option.gnu.cross.prefix.1771691886.1324829965" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix.1771691886"/>
-							<option id="cdt.managedbuild.option.gnu.cross.path.1831805518.1198903031" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path.1831805518"/>
-							<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
-							<tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.1642402045" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler.972920545">
-								<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.584005709" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
-							</tool>
-							<tool id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.1648531134" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler.36308842">
-								<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.598540090" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
-							</tool>
-							<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.910780357" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker.1453974065"/>
-							<tool id="cdt.managedbuild.tool.gnu.cross.cpp.linker.586323152" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker.2057246668"/>
-							<tool id="cdt.managedbuild.tool.gnu.cross.archiver.1467668351" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver.2111505538"/>
-							<tool id="cdt.managedbuild.tool.gnu.cross.assembler.36312719" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler.353143895">
-								<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1895164072" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
-							</tool>
-						</toolChain>
-					</folderInfo>
-				</configuration>
-			</storageModule>
-			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
-		</cconfiguration>
-	</storageModule>
-	<storageModule moduleId="cdtBuildSystem" version="4.0.0">
-		<project id="nodemcu-firmware.null.292794708" name="nodemcu-firmware"/>
-	</storageModule>
-	<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
-	<storageModule moduleId="refreshScope" versionNumber="2">
-		<configuration configurationName="Default">
-			<resource resourceType="PROJECT" workspacePath="/nodemcu-firmware"/>
-		</configuration>
-	</storageModule>
-	<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
-	<storageModule moduleId="scannerConfiguration">
-		<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
-		<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.992255352;cdt.managedbuild.toolchain.gnu.cross.base.992255352.1209976491;cdt.managedbuild.tool.gnu.cross.c.compiler.972920545;cdt.managedbuild.tool.gnu.c.compiler.input.1985687892">
-			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
-		</scannerConfigBuildInfo>
-		<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.992255352;cdt.managedbuild.toolchain.gnu.cross.base.992255352.1209976491;cdt.managedbuild.tool.gnu.cross.cpp.compiler.36308842;cdt.managedbuild.tool.gnu.cpp.compiler.input.1696212951">
-			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
-		</scannerConfigBuildInfo>
-	</storageModule>
-	<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
-		<buildTargets>
-			<target name="all" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
-				<buildCommand>make</buildCommand>
-				<buildArguments/>
-				<buildTarget>all</buildTarget>
-				<stopOnError>true</stopOnError>
-				<useDefaultCommand>true</useDefaultCommand>
-				<runAllBuilders>true</runAllBuilders>
-			</target>
-			<target name="clean" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
-				<buildCommand>make</buildCommand>
-				<buildTarget>clean</buildTarget>
-				<stopOnError>true</stopOnError>
-				<useDefaultCommand>true</useDefaultCommand>
-				<runAllBuilders>true</runAllBuilders>
-			</target>
-			<target name="flash" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
-				<buildCommand>make</buildCommand>
-				<buildArguments/>
-				<buildTarget>flash</buildTarget>
-				<stopOnError>true</stopOnError>
-				<useDefaultCommand>true</useDefaultCommand>
-				<runAllBuilders>true</runAllBuilders>
-			</target>
-		</buildTargets>
-	</storageModule>
-</cproject>

+ 33 - 0
.github/ISSUE_TEMPLATE.md

@@ -0,0 +1,33 @@
+Make sure you read and understand http://nodemcu.readthedocs.io/en/dev/en/support/.
+Use one of the two templates below and delete the rest.
+
+8<------------------------ BUG REPORT -----------------------------------------
+### Expected behavior
+
+### Actual behavior
+
+### Test code
+Provide a minimal and isolated test which will reproduce the problem.
+```Lua
+-- add code here
+```
+### NodeMCU version
+Which branch are you on? If you know the Git revision then add it here as well.
+
+### Hardware
+Describe which ESP8266 device you use and document any special hardware setup 
+required to reproduce the problem.
+
+8<------------------------ END BUG REPORT -------------------------------------
+
+
+8<------------------------ FEATURE REQUEST ------------------------------------
+### Missing feature
+
+### Justification
+Tell us why you would like to see this feature added.
+
+### Workarounds
+Are there any workarounds you currently have in place because the feature is missing?
+
+8<------------------------ END FEATURE REQUEST --------------------------------

+ 11 - 0
.github/PULL_REQUEST_TEMPLATE.md

@@ -0,0 +1,11 @@
+Fixes #\<GitHub-issue-number\>.
+
+Make sure all boxes are checked (add x inside the brackets) when you submit your contribution, remove this sentence before doing so.
+
+- [ ] This PR is compliant with the [contributing guidelines](https://github.com/nodemcu/nodemcu-firmware/blob/dev/CONTRIBUTING.md) (if not, please describe why).
+- [ ] I have thoroughly tested my contribution.
+- [ ] The code changes are reflected in the documentation at `docs/en/*`.
+
+\<Description of and rational behind this PR\>
+
+Committers supporting this PR: leave blank

+ 9 - 0
.gitignore

@@ -0,0 +1,9 @@
+sdk/
+cache/
+user_config.h
+server-ca.crt
+
+#ignore Eclipse project files
+.cproject
+.project
+.settings/

+ 0 - 27
.project

@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>nodemcu-firmware</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
-			<triggers>clean,full,incremental,</triggers>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
-			<triggers>full,incremental,</triggers>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.cdt.core.cnature</nature>
-		<nature>org.eclipse.cdt.core.ccnature</nature>
-		<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
-		<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
-	</natures>
-</projectDescription>

+ 0 - 11
.settings/language.settings.xml

@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project>
-	<configuration id="cdt.managedbuild.toolchain.gnu.cross.base.992255352" name="Default">
-		<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
-			<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
-			<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
-			<provider copy-of="extension" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser"/>
-			<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
-		</extension>
-	</configuration>
-</project>

+ 0 - 67
.settings/org.eclipse.cdt.codan.core.prefs

@@ -1,67 +0,0 @@
-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},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}}
-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},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}}
-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}}
-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}}
-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}}
-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},no_break_comment\=>"no break",last_case_param\=>false,empty_case_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},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}}
-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},skip\=>true}
-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}}
-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}}
-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}}
-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}}
-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}}
-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}}
-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}}
-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},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}}
-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}}
-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}}
-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}}
-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}}
-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}}
-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},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},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},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}}
-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},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},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},macro\=>true,exceptions\=>("@(\#)","$Id")}
-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}}

+ 0 - 6
.settings/org.eclipse.cdt.core.prefs

@@ -1,6 +0,0 @@
-eclipse.preferences.version=1
-environment/project/cdt.managedbuild.toolchain.gnu.cross.base.992255352/PATH/delimiter=;
-environment/project/cdt.managedbuild.toolchain.gnu.cross.base.992255352/PATH/operation=append
-environment/project/cdt.managedbuild.toolchain.gnu.cross.base.992255352/PATH/value=C\:\\Espressif\\xtensa-lx106-elf\\bin;C\:\\MinGW\\bin;C\:\\MinGW\\msys\\1.0\\bin;C\:\\Python27
-environment/project/cdt.managedbuild.toolchain.gnu.cross.base.992255352/append=true
-environment/project/cdt.managedbuild.toolchain.gnu.cross.base.992255352/appendContributed=true

+ 3 - 11
.travis.yml

@@ -5,6 +5,9 @@ addons:
     packages:
     - python-serial
     - srecord
+cache:
+  directories:
+    - cache
 install:
 - tar -zxvf tools/esp-open-sdk.tar.gz
 - export PATH=$PATH:$PWD/esp-open-sdk/xtensa-lx106-elf/bin
@@ -22,14 +25,3 @@ script:
 - srec_cat -output ${file_name_integer} -binary 0x00000.bin -binary -fill 0xff 0x00000 0x10000 0x10000.bin -binary -offset 0x10000
 # http://docs.travis-ci.com/user/environment-variables/#Convenience-Variables
 - if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash "$TRAVIS_BUILD_DIR"/tools/pr-build.sh; fi
-deploy:
-  provider: releases
-  api_key:
-    secure: Swecz5lWvsuSbchSbVQ1rmCPN9nQIN5p/HlZNIEdEgAgnoLcJxRV4P8poVTB37jiA8Pck+8x2nWXpg74Rqik0i3KlPNvDfg5o4rIazWLNs4bc1Tbcpt44XAzFKKLYnDnWQUGcqjk7BcAXuNAF2X/fPBCVhFbHVg3Z7cDb32RsNw=
-  file:
-    - "$TRAVIS_BUILD_DIR/bin/${file_name_float}"
-    - "$TRAVIS_BUILD_DIR/bin/${file_name_integer}"
-  skip_cleanup: true
-  on:
-    tags: true
-    repo: nodemcu/nodemcu-firmware

+ 18 - 1
CONTRIBUTING.md

@@ -9,6 +9,7 @@ It is appreciated but optional if you raise an issue _before_ you start changing
 ### Table Of Contents
 
 * [Development environment setup](#development-environment-setup)
+* [Writing Documentation](#writing-documentation)
 * [Working with Git and GitHub](#working-with-git-and-github)
   * [General flow](#general-flow)
   * [Keeping your fork in sync](#keeping-your-fork-in-sync)
@@ -19,6 +20,22 @@ Use the platform and tools you feel most comfortable with. There are  no constra
 - [Full-fledged Linux enviroment](http://www.esp8266.com/wiki/doku.php?id=toolchain#how_to_setup_a_vm_to_host_your_toolchain), either physical or virtual.
 - [Docker image](https://hub.docker.com/r/marcelstoer/nodemcu-build/) which allows to run the build inside the container as if you were running a build script on your local machine.
 
+## Writing Documentation
+The NodeMCU documentation is maintained within the same repository as the code. The primary reason is to keep the two in sync more easily. It's thus trivial for the NodeMCU team to verify that a PR includes the necessary documentation. Furthermore, the documentation is merged automatically with the code if it moves from branch X to Y.
+
+The documentation consists of a collection of Markdown files (see note on Markdown syntax at end of chapter) stored in the [`/docs`](docs) directory. With every commit a human readable and browsable version is automatically built with [Read the Docs](https://readthedocs.io/) (RTD). The public NodeMCU documentation can be found at [nodemcu.readthedocs.io](http://nodemcu.readthedocs.io/).
+
+There are essentially only two things to keep in mind if you're contributing a PR:
+
+- If you add functions to or change functions of an *existing module* you should modify the module's `.md` file in [`/docs/en/modules`](docs/en/modules). Adhere to the existing documentation structure and keep functions in alphabetical order.
+- If you add a *new module* you should, in addition to the above, also add a reference for the new `.md` file to [`mkdocs.yml`](./mkdocs.yml) (lines 32+). Note that modules are ordered alphabetically here as well.
+
+If you also want to verify that all is well with your Markdown files you can install Python-based [MkDocs](http://www.mkdocs.org/), which is used by RTD to build the static HTML files, and run `mkdocs serve` in the root of your NodeMCU firmware directory.
+
+A note on Markdown *syntax*. As Mkdocs is Python-based it's no surprise it uses a [Python Markdown implementation](https://pythonhosted.org/Markdown/). The good news is that it sticks pretty closely to John Gruber's Markdown and also [supports tables and fenced code blocks](http://www.mkdocs.org/user-guide/writing-your-docs/#markdown-extensions) just like GitHub does.
+
+If you're interested in some NodeMCU history you're welcome to read [issue #774](https://github.com/nodemcu/nodemcu-firmware/issues/774)
+
 ## Working with Git and GitHub
 
 *Pull requests for new features and major fixes should be opened against the `dev` branch.*
@@ -33,7 +50,7 @@ Avoid intermediate merge commits. [Rebase](https://www.atlassian.com/git/tutoria
 1. `git checkout <branch-name>`
 1. Make changes to the code base and commit them using e.g. `git commit -a -m 'Look ma, I did it'`
 1. When you're done:
- 1. [Squash your commits](http://stackoverflow.com/a/5201642/131929) into one. There are several ways of doing this.
+ 1. [Squash your commits](http://www.andrewconnell.com/blog/squash-multiple-git-commits-into-one) into one. There are [several ways](http://stackoverflow.com/a/5201642/131929) of doing this.
  1. Bring your fork up-to-date with the NodeMCU upstream repo ([see below](#keeping-your-fork-in-sync)). Then rebase your branch on `dev` running `git rebase dev`.
 1. `git push`
 1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/) (PR) on GitHub. 

+ 38 - 10
Makefile

@@ -1,16 +1,16 @@
 #  copyright (c) 2010 Espressif System
 #
 .NOTPARALLEL:
-ifndef PDIR
-
-endif
 
 # SDK version NodeMCU is locked to
-SDK_VER:=1.4.0
+SDK_VER:=1.5.1
+SDK_FILE_VER:=$(SDK_VER)_16_01_08
+SDK_FILE_ID:=1046
+SDK_FILE_SHA1:=374f689a5f9e47690d7b4cd2fc1a1094f3fd5a4f
 # Ensure we search "our" SDK before the tool-chain's SDK (if any)
-TOP_DIR:=$(dir $(lastword $(MAKEFILE_LIST)))
-SDK_DIR:=$(TOP_DIR)sdk/esp_iot_sdk_v$(SDK_VER)
-CCFLAGS:= -I$(TOP_DIR)sdk-overrides/include -I$(SDK_DIR)/include
+TOP_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST))))
+SDK_DIR:=$(TOP_DIR)/sdk/esp_iot_sdk_v$(SDK_VER)
+CCFLAGS:= -I$(TOP_DIR)/sdk-overrides/include -I$(SDK_DIR)/include
 LDFLAGS:= -L$(SDK_DIR)/lib -L$(SDK_DIR)/ld $(LDFLAGS)
 
 #############################################################
@@ -131,8 +131,8 @@ CCFLAGS += 			\
 	-mtext-section-literals
 #	-Wall			
 
-CFLAGS = $(CCFLAGS) $(DEFINES) $(EXTRA_CCFLAGS) $(INCLUDES)
-DFLAGS = $(CCFLAGS) $(DDEFINES) $(EXTRA_CCFLAGS) $(INCLUDES)
+CFLAGS = $(CCFLAGS) $(DEFINES) $(EXTRA_CCFLAGS) $(STD_CFLAGS) $(INCLUDES)
+DFLAGS = $(CCFLAGS) $(DDEFINES) $(EXTRA_CCFLAGS) $(STD_CFLAGS) $(INCLUDES)
 
 
 #############################################################
@@ -171,11 +171,27 @@ $(BINODIR)/%.bin: $(IMAGEODIR)/%.out
 # Should be done in top-level makefile only
 #
 
-all:	.subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS)
+all:	sdk_extracted pre_build .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS)
+
+.PHONY: sdk_extracted
+
+sdk_extracted: $(TOP_DIR)/sdk/.extracted-$(SDK_VER)
+
+$(TOP_DIR)/sdk/.extracted-$(SDK_VER): $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip
+	mkdir -p "$(dir $@)"
+	(cd "$(dir $@)" && rm -fr esp_iot_sdk_v$(SDK_VER) && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_VER)*.zip esp_iot_sdk_v$(SDK_VER)/lib/* esp_iot_sdk_v$(SDK_VER)/ld/eagle.rom.addr.v6.ld esp_iot_sdk_v$(SDK_VER)/include/* )
+	rm -f $(SDK_DIR)/lib/liblwip.a
+	touch $@
+
+$(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip:
+	mkdir -p "$(dir $@)"
+	wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused http://bbs.espressif.com/download/file.php?id=$(SDK_FILE_ID) -O $@ || { rm -f "$@"; exit 1; }
+	(echo "$(SDK_FILE_SHA1)  $@" | sha1sum -c -) || { rm -f "$@"; exit 1; }
 
 clean:
 	$(foreach d, $(SUBDIRS), $(MAKE) -C $(d) clean;)
 	$(RM) -r $(ODIR)/$(TARGET)/$(FLAVOR)
+	$(RM) -r "$(TOP_DIR)/sdk"
 
 clobber: $(SPECIAL_CLOBBER)
 	$(foreach d, $(SUBDIRS), $(MAKE) -C $(d) clobber;)
@@ -202,6 +218,18 @@ endif
 endif
 endif
 
+.PHONY: pre_build
+
+ifneq ($(wildcard $(TOP_DIR)/server-ca.crt),)
+pre_build:
+	python $(TOP_DIR)/tools/make_server_cert.py $(TOP_DIR)/server-ca.crt > $(TOP_DIR)/app/modules/server-ca.crt.h
+DEFINES += -DHAVE_SSL_SERVER_CRT=\"server-ca.crt.h\"
+else
+pre_build:
+	@-rm -f $(TOP_DIR)/app/modules/server-ca.crt.h
+endif
+
+
 $(OBJODIR)/%.o: %.c
 	@mkdir -p $(OBJODIR);
 	$(CC) $(if $(findstring $<,$(DSRCS)),$(DFLAGS),$(CFLAGS)) $(COPTS_$(*F)) -o $@ -c $<

+ 59 - 610
README.md

@@ -1,667 +1,116 @@
-# **NodeMCU 1.4.0** #
+# **NodeMCU 1.5.1** #
 
-[![Join the chat at https://gitter.im/nodemcu/nodemcu-firmware](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/nodemcu/nodemcu-firmware?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![Join the chat at https://gitter.im/nodemcu/nodemcu-firmware](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/nodemcu/nodemcu-firmware?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 [![Build Status](https://travis-ci.org/nodemcu/nodemcu-firmware.svg)](https://travis-ci.org/nodemcu/nodemcu-firmware)
+[![Documentation Status](https://readthedocs.com/projects/nodemcu/badge/?version=dev)](http://nodemcu.readthedocs.io/)
+[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/nodemcu/nodemcu-firmware/blob/master/LICENSE)
 
-###A lua based firmware for wifi-soc esp8266
-  - Build on [ESP8266 NONOS SDK 1.4.0](http://bbs.espressif.com/viewtopic.php?f=46&t=1124)
-  - Lua core based on [eLua project](http://www.eluaproject.net/)
-  - cjson based on [lua-cjson](https://github.com/mpx/lua-cjson)
-  - File system based on [spiffs](https://github.com/pellepl/spiffs)
-  - Open source development kit for NodeMCU [nodemcu-devkit-v0.9](https://github.com/nodemcu/nodemcu-devkit) [nodemcu-devkit-v1.0](https://github.com/nodemcu/nodemcu-devkit-v1.0)
+### A Lua based firmware for ESP8266 WiFi SOC
 
-# Summary
-
-- Easy to program wireless node and/or Access Point
-- Based on Lua 5.1.4 (without *debug, os* module.)
-- Event-driven programming model preferred
-- Built-in modules: node, json, file, timer, pwm, i2c, spi, onewire, net, mqtt, coap, gpio, wifi, adc, uart, bit, u8g, ucg, ws2801, ws2812, crypto, dht, rtc, sntp, bmp085, tls2561, hx711 and system api.
-- Both Integer (less memory usage) and Float version firmware provided.
-
-## Useful links
-
-| Resource | Location |
-| -------------- | -------------- |
-| Developer Wiki       | https://github.com/nodemcu/nodemcu-firmware/wiki |
-| API docs             | [NodeMCU api](https://github.com/nodemcu/nodemcu-firmware/wiki/nodemcu_api_en) |
-| Home                 | [nodemcu.com](http://www.nodemcu.com) |
-| BBS                  | [Chinese BBS](http://bbs.nodemcu.com) |
-| Docs                 | [NodeMCU docs](http://www.nodemcu.com/docs/) |
-| Tencent QQ group     | 309957875 |
-| Windows flash tool   | [nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher) |
-| Linux flash tool     | [Esptool](https://github.com/themadinventor/esptool) |
-| ESPlorer GUI         | https://github.com/4refr0nt/ESPlorer |
-| NodeMCU Studio GUI   | https://github.com/nodemcu/nodemcu-studio-csharp |
-
-# Programming Examples
+NodeMCU is an [eLua](http://www.eluaproject.net/) based firmware for the [ESP8266 WiFi SOC from Espressif](http://espressif.com/en/products/esp8266/). The firmware is based on the [Espressif NON-OS SDK 1.5.1](http://bbs.espressif.com/viewtopic.php?f=46&p=5315) and uses a file system based on [spiffs](https://github.com/pellepl/spiffs). The code repository consists of 98.1% C-code that glues the thin Lua veneer to the SDK.
 
-Because Lua is a high level language and several modules are built into the firmware, you can very easily program your ESP8266. Here are some examples!
-
-## Connect to your AP
-
-```lua
-    ip = wifi.sta.getip()
-    print(ip)
-    --nil
-    wifi.setmode(wifi.STATION)
-    wifi.sta.config("SSID", "password")
-    ip = wifi.sta.getip()
-    print(ip)
-    --192.168.18.110
-```
+The NodeMCU *firmware* is a companion project to the popular [NodeMCU dev kits](https://github.com/nodemcu/nodemcu-devkit-v1.0), ready-made open source development boards with ESP8266-12E chips.
 
-## Manipulate hardware like an Arduino
-
-```lua
-    pin = 1
-    gpio.mode(pin, gpio.OUTPUT)
-    gpio.write(pin, gpio.HIGH)
-    print(gpio.read(pin))
-```
+# Summary
 
-## Write a network application in Node.js style
+- Easy to program wireless node and/or access point
+- Based on Lua 5.1.4 (without *debug, os* modules)
+- Asynchronous event-driven programming model
+- 40+ built-in modules
+- Firmware available with or without floating point support (integer-only uses less memory)
+- Up-to-date documentation at [https://nodemcu.readthedocs.io](https://nodemcu.readthedocs.io)
 
-```lua
-    -- A simple http client
-    conn=net.createConnection(net.TCP, 0)
-    conn:on("receive", function(conn, payload) print(payload) end)
-    conn:connect(80, "115.239.210.27")
-    conn:send("GET / HTTP/1.1\r\nHost: www.baidu.com\r\n"
-        .. "Connection: keep-alive\r\nAccept: */*\r\n\r\n")
-```
+# Programming Model
 
-## Or a simple HTTP server
+The NodeMCU programming model is similar to that of [Node.js](https://en.wikipedia.org/wiki/Node.js), only in Lua. It is asynchronous and event-driven. Many functions, therefore, have parameters for callback functions. To give you an idea what a NodeMCU program looks like study the short snippets below. For more extensive examples have a look at the [`/lua_examples`](lua_examples) folder in the repository on GitHub.
 
 ```lua
-    -- A simple http server
-    srv=net.createServer(net.TCP)
-    srv:listen(80, function(conn)
-      conn:on("receive", function(conn,payload)
-        print(payload)
-        conn:send("<h1> Hello, NodeMCU.</h1>")
-      end)
-      conn:on("sent", function(conn) conn:close() end)
-    end)
-```
-
-## Connect to MQTT broker
-
-```lua
--- init mqtt client with keepalive timer 120sec
-m = mqtt.Client("clientid", 120, "user", "password")
-
--- setup Last Will and Testament (optional)
--- Broker will publish a message with qos = 0, retain = 0, data = "offline"
--- to topic "/lwt" if client doesn't send keepalive packet
-m:lwt("/lwt", "offline", 0, 0)
-
-m:on("connect", function(con) print("connected") end)
-m:on("offline", function(con) print("offline") end)
-
--- on publish message receive event
-m:on("message", function(conn, topic, data)
-  print(topic .. ":")
-  if data ~= nil then
-    print(data)
-  end
+-- a simple HTTP server
+srv = net.createServer(net.TCP)
+srv:listen(80, function(conn)
+	conn:on("receive", function(conn, payload)
+		print(payload)
+		conn:send("<h1> Hello, NodeMCU.</h1>")
+	end)
+	conn:on("sent", function(conn) conn:close() end)
 end)
-
--- m:connect(host, port, secure, auto_reconnect, function(client) end)
--- for secure: m:connect("192.168.11.118", 1880, 1, 0)
--- for auto-reconnect: m:connect("192.168.11.118", 1880, 0, 1)
-m:connect("192.168.11.118", 1880, 0, 0, function(conn) print("connected") end)
-
--- subscribe to topic with qos = 0
-m:subscribe("/topic", 0, function(conn) print("subscribe success") end)
--- or subscribe multiple topics (topic/0, qos = 0; topic/1, qos = 1; topic2, qos = 2)
--- m:subscribe({["topic/0"]=0,["topic/1"]=1,topic2=2}, function(conn) print("subscribe success") end)
--- publish a message with data = hello, QoS = 0, retain = 0
-m:publish("/topic", "hello", 0, 0, function(conn) print("sent") end)
-
-m:close();  -- if auto-reconnect == 1, it will disable auto-reconnect and then disconnect from host.
--- you can call m:connect again
-
-```
-
-## UDP client and server
-
-```lua
--- a udp server
-s=net.createServer(net.UDP)
-s:on("receive", function(s, c) print(c) end)
-s:listen(5683)
-
--- a udp client
-cu=net.createConnection(net.UDP)
-cu:on("receive", function(cu, c) print(c) end)
-cu:connect(5683, "192.168.18.101")
-cu:send("hello")
-```
-
-## Do something shiny with an RGB LED
-
-```lua
-  function led(r, g, b)
-    pwm.setduty(1, r)
-    pwm.setduty(2, g)
-    pwm.setduty(3, b)
-  end
-  pwm.setup(1, 500, 512)
-  pwm.setup(2, 500, 512)
-  pwm.setup(3, 500, 512)
-  pwm.start(1)
-  pwm.start(2)
-  pwm.start(3)
-  led(512, 0, 0) -- red
-  led(0, 0, 512) -- blue
-```
-
-## And blink it
-
-```lua
-  lighton=0
-  tmr.alarm(1, 1000, 1, function()
-    if lighton==0 then
-      lighton=1
-      led(512, 512, 512)
-    else
-      lighton=0
-      led(0, 0, 0)
-    end
-  end)
 ```
-
-## If you want to run something when the system boots
-
 ```lua
-  --init.lua will be executed
-  file.open("init.lua", "w")
-  file.writeline([[print("Hello, do this at the beginning.")]])
-  file.close()
-  node.restart()  -- this will restart the module.
+-- connect to WiFi access point
+wifi.setmode(wifi.STATION)
+wifi.sta.config("SSID", "password")
 ```
 
-## Add a simple telnet server to the Lua interpreter
+# Documentation
 
-```lua
-    -- a simple telnet server
-    s=net.createServer(net.TCP, 180)
-    s:listen(2323, function(c)
-       function s_output(str)
-          if(c~=nil)
-             then c:send(str)
-          end
-       end
-       node.output(s_output, 0)   -- re-direct output to function s_ouput.
-       c:on("receive", function(c, l)
-          node.input(l)           -- works like pcall(loadstring(l)) but support multiples separate lines
-       end)
-       c:on("disconnection", function(c)
-          node.output(nil)        -- un-register the redirect output function, output goes to serial
-       end)
-       print("Welcome to NodeMCU world.")
-    end)
-```
-
-# Building the firmware
+The entire [NodeMCU documentation](https://nodemcu.readthedocs.io) is maintained right in this repository at [/docs](docs). The fact that the API documentation is mainted in the same repository as the code that *provides* the API ensures consistency between the two. With every commit the documentation is rebuilt by Read the Docs and thus transformed from terse Markdown into a nicely browsable HTML site at [https://nodemcu.readthedocs.io](https://nodemcu.readthedocs.io). 
 
-There are several options for building the NodeMCU firmware.
+- How to [build the firmware](https://nodemcu.readthedocs.io/en/dev/en/build/)
+- How to [flash the firmware](https://nodemcu.readthedocs.io/en/dev/en/flash/)
+- How to [upload code and NodeMCU IDEs](https://nodemcu.readthedocs.io/en/dev/en/upload/)
+- API documentation for every module
 
-## Online firmware custom build
+# Releases
 
-Please try Marcel's [NodeMCU custom build](http://nodemcu-build.com) cloud service and you can choose only the modules you need, and download the firmware once built.
+Due to the ever-growing number of modules available within NodeMCU, pre-built binaries are no longer made available. Use the automated [custom firmware build service](http://nodemcu-build.com/) to get the specific firmware configuration you need, or consult the [documentation](http://nodemcu.readthedocs.io/en/dev/en/build/) for other options to build your own firmware.
 
-NodeMCU custom builds can build from all active branches (with the latest fixes).
+This projects uses two main branches, `master` and `dev`. `dev` is actively worked on and it's also where PRs should be created against. `master` thus can be considered "stable" even though there are no automated regression tests. The goal is to merge back to `master` roughly every 2 months. Depending on the current "heat" (issues, PRs) we accept changes to `dev` for 5-6 weeks and then hold back for 2-3 weeks before the next snap is completed.
 
-## Docker containerised build
+A new tag is created every time `dev` is merged back to `master`. They are listed in the [releases section here on GitHub](https://github.com/nodemcu/nodemcu-firmware/releases). Tag names follow the \<SDK-version\>-master_yyyymmdd pattern.
 
-See [https://hub.docker.com/r/marcelstoer/nodemcu-build/](https://hub.docker.com/r/marcelstoer/nodemcu-build/)
+# Support
 
-This Docker image includes the build toolchain and SDK. You just run the Docker image with your checked-out NodeMCU firmware repository (this one).
+See [https://nodemcu.readthedocs.io/en/dev/en/support/](https://nodemcu.readthedocs.io/en/dev/en/support/).
 
-You will need to see BUILD OPTIONS below, to configure the firmware before building.
+# License
 
-## Build it yourself
+[MIT](https://github.com/nodemcu/nodemcu-firmware/blob/master/LICENSE) © [zeroday](https://github.com/NodeMCU)/[nodemcu.com](http://nodemcu.com/index_en.html)
 
-See BUILD OPTIONS below, to configure the firmware before building.
+# Build Options
 
-### Minimum requirements:
+The following sections explain some of the options you have if you want to [build your own NodeMCU firmware](http://nodemcu.readthedocs.io/en/dev/en/build/).
 
-  - unrar
-  - GNU autoconf, automake, libtool
-  - GNU gcc, g++, make
-  - GNU flex, bison, gawk, sed
-  - python, python-serial, libexpat-dev
-  - srecord
-  - The esp-open-sdk from https://github.com/pfalcon/esp-open-sdk
+### Select Modules
 
-### Build instructions:
+Disable modules you won't be using to reduce firmware size and free up some RAM. The ESP8266 is quite limited in available RAM and running out of memory can cause a system panic. The default configuration is designed to run on all ESP modules including the 512 KB modules like ESP-01 and only includes general purpose interface modules which require at most two GPIO pins.
 
-Assuming NodeMCU firmware is checked-out to `/opt/nodemcu-firmware`:
-
-```sh
-git clone --recursive https://github.com/pfalcon/esp-open-sdk.git /opt/esp-open-sdk
-cd /opt/esp-open-sdk
-make STANDALONE=y
-PATH=/opt/esp-open-sdk/xtensa-lx106-elf/bin:$PATH
-cd /opt/nodemcu-firmware
-make
-```
-
-# BUILD OPTIONS
-
-Disable modules you won't be using, to reduce firmware size on flash and
-free more RAM. The ESP8266 is quite limited in available RAM, and running
-out can cause a system panic.
-
-## Edit `app/include/user_modules.h`
-
-Comment-out the #define statement for unused modules. Example:
+Edit `app/include/user_modules.h` and comment-out the `#define` statement for modules you don't need. Example:
 
 ```c
-#ifdef LUA_USE_MODULES
-#define LUA_USE_MODULES_NODE
-#define LUA_USE_MODULES_FILE
-#define LUA_USE_MODULES_GPIO
-#define LUA_USE_MODULES_WIFI
-#define LUA_USE_MODULES_NET
-#define LUA_USE_MODULES_PWM
-#define LUA_USE_MODULES_I2C
-#define LUA_USE_MODULES_SPI
-#define LUA_USE_MODULES_TMR
-#define LUA_USE_MODULES_ADC
-#define LUA_USE_MODULES_UART
-#define LUA_USE_MODULES_OW
-#define LUA_USE_MODULES_BIT
+...
 #define LUA_USE_MODULES_MQTT
 // #define LUA_USE_MODULES_COAP
 // #define LUA_USE_MODULES_U8G
-// #define LUA_USE_MODULES_WS2801
-// #define LUA_USE_MODULES_WS2812
-// #define LUA_USE_MODULES_CJSON
-#define LUA_USE_MODULES_CRYPTO
-#define LUA_USE_MODULES_RC
-#define LUA_USE_MODULES_DHT
-#define LUA_USE_MODULES_RTCMEM
-#define LUA_USE_MODULES_RTCTIME
-#define LUA_USE_MODULES_RTCFIFO
-#define LUA_USE_MODULES_SNTP
-// #define LUA_USE_MODULES_BMP085
-#define LUA_USE_MODULES_TSL2561
-// #define LUA_USE_MODULES_HX711
-
-#endif /* LUA_USE_MODULES */
+...
 ```
 
-## Tagging your build
+### Tag Your Build
 
 Identify your firmware builds by editing `app/include/user_version.h`
 
 ```c
-#define NODE_VERSION    "NodeMCU 1.4.0+myname"
+#define NODE_VERSION    "NodeMCU 1.5.1+myname"
 #ifndef BUILD_DATE
-#define BUILD_DATE        "YYYYMMDD"
+#define BUILD_DATE      "YYYYMMDD"
 #endif
 ```
 
-## Setting the boot time serial interface rate
+### Set UART Bit Rate
 
-The initial baud rate at boot time is 9600 bps, but you can change this by
-editing `app/include/user_config.h` and change BIT_RATE_DEFAULT, e.g.:
+The initial baud rate at boot time is 115200bps. You can change this by
+editing `BIT_RATE_DEFAULT` in `app/include/user_config.h`:
 
 ```c
 #define BIT_RATE_DEFAULT BIT_RATE_115200
 ```
 
-## Debugging
-
-To enable runtime debug messages to serial console, edit `app/include/user_config.h`
-
-```c
-#define DEVELOP_VERSION
-```
-
-`DEVELOP_VERSION` changes the startup baud rate to 74880.
-
-# Flash the firmware
-
-## Flash tools for Windows
-
-You can use the [nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher) to burn the firmware.
-
-## Flash tools for Linux
-
-Esptool is a python utility which can read and write the flash in an ESP8266 device. See https://github.com/themadinventor/esptool
-
-## Preparing the hardware for firmware upgrade
-
-To enable ESP8266 firmware flashing, the GPIO0 pin must be pulled low before
-the device is reset. Conversely, for a normal boot, GPIO0 must be pulled high
-or floating.
-
-If you have a [NodeMCU Development Kit](http://www.nodemcu.com/index_en.html) then
-you don't need to do anything, as the USB connection can pull GPIO0
-low by asserting DTR, and reset your board by asserting RTS.
-
-If you have an ESP-01 or other device without inbuilt USB, you will need to
-enable flashing yourself by pulling GPIO0 low or pressing a "flash" switch.
-
-## Files to burn to the flash
-
-If you got your firmware from [NodeMCU custom builds](http://frightanic.com/nodemcu-custom-build) then you can flash that file directly to address 0x00000.
-
-Otherwise, if you built your own firmware from source code:
-  - bin/0x00000.bin to 0x00000
-  - bin/0x10000.bin to 0x10000
-
-Also, in some special circumstances, you may need to flash `blank.bin` or `esp_init_data_default.bin` to various addresses on the flash (depending on flash size and type).
-
-If upgrading from `spiffs` version 0.3.2 to 0.3.3 or later, or after flashing any new firmware (particularly one with a much different size), you may need to run `file.format()` to re-format your flash filesystem.
-You will know if you need to do this because your flash files disappeared, or they exist but seem empty, or data cannot be written to new files.
+Note that, by default, the firmware runs an auto-baudrate detection algorithm so that typing a few characters at boot time will cause
+the firmware to lock onto that baud rate (between 1200 and 230400). 
 
-# Connecting to your NodeMCU device
+### Debugging
 
-NodeMCU serial interface uses 9600 baud at boot time. To increase the speed after booting, issue `uart.setup(0,115200,8,0,1,1)` (ESPlorer will do this automatically when changing the speed in the dropdown list).
+To enable runtime debug messages to serial console edit `app/include/user_config.h`
 
-If the device panics and resets at any time, errors will be written to the serial interface at 115200 bps.
-
-# User Interface tools
-
-## Esplorer
-
-Victor Brutskiy's [ESPlorer](https://github.com/4refr0nt/ESPlorer) is written in Java, is open source and runs on most platforms such as Linux, Windows, Mac OS, etc.
-
-#### Features
-
-  - Edit Lua scripts and run on the ESP8266 and save to its flash
-  - Serial console log
-  - Also supports original AT firmware (reading and setting WiFi modes, etc)
-
-## NodeMCU Studio
-
-[NodeMCU Studio](https://github.com/nodemcu/nodemcu-studio-csharp) is written in C# and supports Windows. This software is open source and can write lua files to filesystem.
-
-# OPTIONAL MODULES
-
-####Use DS18B20 module extends your esp8266
-```lua
-    -- read temperature with DS18B20
-    node.compile("ds18b20.lua")   --  run this only once to compile and save to "ds18b20.lc"
-    t=require("ds18b20")
-    t.setup(9)
-    addrs=t.addrs()
-    -- Total DS18B20 numbers, assume it is 2
-    print(table.getn(addrs))
-    -- The first DS18B20
-    print(t.read(addrs[1],t.C))
-    print(t.read(addrs[1],t.F))
-    print(t.read(addrs[1],t.K))
-    -- The second DS18B20
-    print(t.read(addrs[2],t.C))
-    print(t.read(addrs[2],t.F))
-    print(t.read(addrs[2],t.K))
-    -- Just read
-    print(t.read())
-    -- Just read as centigrade
-    print(t.read(nil,t.C))
-    -- Don't forget to release it after use
-    t = nil
-	ds18b20 = nil
-    package.loaded["ds18b20"]=nil
-```
-
-####Operate a display with u8glib
-u8glib is a graphics library with support for many different displays. The nodemcu firmware supports a subset of these.
-Both I2C and SPI:
-* sh1106_128x64
-* ssd1306 - 128x64 and 64x48 variants
-* ssd1309_128x64
-* ssd1327_96x96_gr
-* uc1611 - dogm240 and dogxl240 variants
-
-SPI only:
-* ld7032_60x32
-* pcd8544_84x48
-* pcf8812_96x65
-* ssd1322_nhd31oled - bw and gr variants
-* ssd1325_nhd27oled - bw and gr variants
-* ssd1351_128x128 - gh and hicolor variants
-* st7565_64128n - variants 64128n, dogm128/132, lm6059/lm6063, c12832/c12864
-* uc1601_c128032
-* uc1608 - 240x128 and 240x64 variants
-* uc1610_dogxl160 - bw and gr variants
-* uc1611 - dogm240 and dogxl240 variants
-* uc1701 - dogs102 and mini12864 variants
-
-U8glib v1.18.1
-
-#####I2C connection
-Hook up SDA and SCL to any free GPIOs. Eg. [u8g_graphics_test.lua](lua_examples/u8glib/u8g_graphics_test.lua) expects SDA=5 (GPIO14) and SCL=6 (GPIO12). They are used to set up nodemcu's I2C driver before accessing the display:
-```lua
-sda = 5
-scl = 6
-i2c.setup(0, sda, scl, i2c.SLOW)
-```
-
-#####SPI connection
-The HSPI module is used, so certain pins are fixed:
-* HSPI CLK  = GPIO14
-* HSPI MOSI = GPIO13
-* HSPI MISO = GPIO12 (not used)
-
-All other pins can be assigned to any available GPIO:
-* CS
-* D/C
-* RES (optional for some displays)
-
-Also refer to the initialization sequence eg in [u8g_graphics_test.lua](lua_examples/u8glib/u8g_graphics_test.lua):
-```lua
-spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, 8, 8)
-```
-
-
-#####Library usage
-The Lua bindings for this library closely follow u8glib's object oriented C++ API. Based on the u8g class, you create an object for your display type.
-
-SSD1306 via I2C:
-```lua
-sla = 0x3c
-disp = u8g.ssd1306_128x64_i2c(sla)
-```
-SSD1306 via SPI:
-```lua
-cs  = 8 -- GPIO15, pull-down 10k to GND
-dc  = 4 -- GPIO2
-res = 0 -- GPIO16, RES is optional YMMV
-disp = u8g.ssd1306_128x64_hw_spi(cs, dc, res)
-```
-
-This object provides all of u8glib's methods to control the display.
-Again, refer to [u8g_graphics_test.lua](lua_examples/u8glib/u8g_graphics_test.lua) to get an impression how this is achieved with Lua code. Visit the [u8glib homepage](https://github.com/olikraus/u8glib) for technical details.
-
-#####Displays
-I2C and HW SPI based displays with support in u8glib can be enabled. To get access to the respective constructors, add the desired entries to the I2C or SPI display tables in [app/include/u8g_config.h](app/include/u8g_config.h):
-```c
-#define U8G_DISPLAY_TABLE_I2C                   \
-    U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_i2c) \
-
-#define U8G_DISPLAY_TABLE_SPI                      \
-    U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_hw_spi) \
-    U8G_DISPLAY_TABLE_ENTRY(pcd8544_84x48_hw_spi)  \
-    U8G_DISPLAY_TABLE_ENTRY(pcf8812_96x65_hw_spi)  \
-```
-An exhaustive list of available displays can be found in the [u8g module wiki entry](https://github.com/nodemcu/nodemcu-firmware/wiki/nodemcu_api_en#u8g-module).
-
-
-#####Fonts
-u8glib comes with a wide range of fonts for small displays. Since they need to be compiled into the firmware image, you'd need to include them in [app/include/u8g_config.h](app/include/u8g_config.h) and recompile. Simply add the desired fonts to the font table:
 ```c
-#define U8G_FONT_TABLE \
-    U8G_FONT_TABLE_ENTRY(font_6x10)  \
-    U8G_FONT_TABLE_ENTRY(font_chikita)
-```
-They'll be available as `u8g.<font_name>` in Lua.
-
-#####Bitmaps
-Bitmaps and XBMs are supplied as strings to `drawBitmap()` and `drawXBM()`. This off-loads all data handling from the u8g module to generic methods for binary files. See [u8g_bitmaps.lua](lua_examples/u8glib/u8g_bitmaps.lua).
-In contrast to the source code based inclusion of XBMs into u8glib, it's required to provide precompiled binary files. This can be performed online with [Online-Utility's Image Converter](http://www.online-utility.org/image_converter.jsp): Convert from XBM to MONO format and upload the binary result with [nodemcu-uploader.py](https://github.com/kmpm/nodemcu-uploader).
-
-#####Unimplemented functions
-- [ ] Cursor handling
-  - [ ] disableCursor()
-  - [ ] enableCursor()
-  - [ ] setCursorColor()
-  - [ ] setCursorFont()
-  - [ ] setCursorPos()
-  - [ ] setCursorStyle()
-- [ ] General functions
-  - [ ] setContrast()
-  - [ ] setPrintPos()
-  - [ ] setHardwareBackup()
-  - [ ] setRGB()
-  - [ ] setDefaultMidColor()
-
-####Operate a display with ucglib
-Ucglib is a graphics library with support for color TFT displays.
-
-Ucglib v1.3.3
-
-#####SPI connection
-The HSPI module is used, so certain pins are fixed:
-* HSPI CLK  = GPIO14
-* HSPI MOSI = GPIO13
-* HSPI MISO = GPIO12 (not used)
-
-All other pins can be assigned to any available GPIO:
-* CS
-* D/C
-* RES (optional for some displays)
-
-Also refer to the initialization sequence eg in [GraphicsTest.lua](lua_examples/ucglib/GraphicsRest.lua):
-```lua
-spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, 8, 8)
-```
-
-#####Library usage
-The Lua bindings for this library closely follow ucglib's object oriented C++ API. Based on the ucg class, you create an object for your display type.
-
-ILI9341 via SPI:
-```lua
-cs  = 8 -- GPIO15, pull-down 10k to GND
-dc  = 4 -- GPIO2
-res = 0 -- GPIO16, RES is optional YMMV
-disp = ucg.ili9341_18x240x320_hw_spi(cs, dc, res)
-```
-
-This object provides all of ucglib's methods to control the display.
-Again, refer to [GraphicsTest.lua](lua_examples/ucglib/GraphicsTest.lua) to get an impression how this is achieved with Lua code. Visit the [ucglib homepage](https://github.com/olikraus/ucglib) for technical details.
-
-#####Displays
-To get access to the display constructors, add the desired entries to the display table in [app/include/ucg_config.h](app/include/ucg_config.h):
-```c
-#define UCG_DISPLAY_TABLE                          \
-    UCG_DISPLAY_TABLE_ENTRY(ili9341_18x240x320_hw_spi, ucg_dev_ili9341_18x240x320, ucg_ext_ili9341_18) \
-    UCG_DISPLAY_TABLE_ENTRY(st7735_18x128x160_hw_spi, ucg_dev_st7735_18x128x160, ucg_ext_st7735_18) \
-```
-
-#####Fonts
-ucglib comes with a wide range of fonts for small displays. Since they need to be compiled into the firmware image, you'd need to include them in [app/include/ucg_config.h](app/include/ucg_config.h) and recompile. Simply add the desired fonts to the font table:
-```c
-#define UCG_FONT_TABLE                              \
-    UCG_FONT_TABLE_ENTRY(font_7x13B_tr)             \
-    UCG_FONT_TABLE_ENTRY(font_helvB12_hr)           \
-    UCG_FONT_TABLE_ENTRY(font_helvB18_hr)           \
-    UCG_FONT_TABLE_ENTRY(font_ncenR12_tr)           \
-    UCG_FONT_TABLE_ENTRY(font_ncenR14_hr)
-```
-They'll be available as `ucg.<font_name>` in Lua.
-
-
-####Control a WS2812 based light strip
-```lua
-	-- set the color of one LED on GPIO2 to red
-	ws2812.writergb(4, string.char(255, 0, 0))
-	-- set the color of 10 LEDs on GPIO0 to blue
-	ws2812.writergb(3, string.char(0, 0, 255):rep(10))
-	-- first LED green, second LED white
-	ws2812.writergb(4, string.char(0, 255, 0, 255, 255, 255))
-```
-
-####coap client and server
-```lua
--- use copper addon for firefox
-cs=coap.Server()
-cs:listen(5683)
-
-myvar=1
-cs:var("myvar") -- get coap://192.168.18.103:5683/v1/v/myvar will return the value of myvar: 1
-
-all='[1,2,3]'
-cs:var("all", coap.JSON) -- sets content type to json
-
--- function should tack one string, return one string.
-function myfun(payload)
-  print("myfun called")
-  respond = "hello"
-  return respond
-end
-cs:func("myfun") -- post coap://192.168.18.103:5683/v1/f/myfun will call myfun
-
-cc = coap.Client()
-cc:get(coap.CON, "coap://192.168.18.100:5683/.well-known/core")
-cc:post(coap.NON, "coap://192.168.18.100:5683/", "Hello")
-```
-
-####cjson
-```lua
--- Note that when cjson deal with large content, it may fails a memory allocation, and leaks a bit of memory.
--- so it's better to detect that and schedule a restart.
---
--- Translate Lua value to/from JSON
--- text = cjson.encode(value)
--- value = cjson.decode(text)
-json_text = '[ true, { "foo": "bar" } ]'
-value = cjson.decode(json_text)
--- Returns: { true, { foo = "bar" } }
-value = { true, { foo = "bar" } }
-json_text = cjson.encode(value)
--- Returns: '[true,{"foo":"bar"}]'
-```
-
-####Read an HX711 load cell ADC.
-Note: currently only chanel A with gain 128 is supported.
-The HX711 is an inexpensive 24bit ADC with programmable 128x, 64x, and 32x gain.
-```lua
-	-- Initialize the hx711 with clk on pin 5 and data on pin 6
-	hx711.init(5,6)
-	-- Read ch A with 128 gain.
-	raw_data = hx711.read(0)
-```
-
-####Universal DHT Sensor support
-Support DHT11, DHT21, DHT22, DHT33, DHT44, etc.
-Use all-in-one function to read DHT sensor.
-```lua
-
-pin = 5
-status,temp,humi,temp_decimial,humi_decimial = dht.readxx(pin)
-if( status == dht.OK ) then
-  -- Integer firmware using this example
-  print(
-    string.format(
-      "DHT Temperature:%d.%03d;Humidity:%d.%03d\r\n",
-      math.floor(temp),
-      temp_decimial,
-      math.floor(humi),
-      humi_decimial
-    )
-  )
-  -- Float firmware using this example
-  print("DHT Temperature:"..temp..";".."Humidity:"..humi)
-elseif( status == dht.ERROR_CHECKSUM ) then
-  print( "DHT Checksum error." );
-elseif( status == dht.ERROR_TIMEOUT ) then
-  print( "DHT Time out." );
-end
-
+#define DEVELOP_VERSION
 ```

+ 9 - 8
app/Makefile

@@ -24,24 +24,24 @@ SPECIAL_MKTARGETS=$(APP_MKTARGETS)
 SUBDIRS= 					\
 	user 					\
 	driver 					\
-	json 					\
-	upgrade 				\
 	platform 				\
 	libc 					\
 	lua 					\
 	lwip 					\
 	coap 					\
 	mqtt 					\
+	task 					\
 	u8glib 					\
 	ucglib					\
 	smart 					\
-	wofs 					\
 	modules 				\
 	spiffs 					\
 	cjson 					\
 	crypto 					\
 	dhtlib					\
-	tsl2561
+	tsl2561					\
+	net					\
+	http
 
 endif # } PDIR
 
@@ -69,9 +69,8 @@ LD_FILE = $(LDDIR)/nodemcu.ld
 COMPONENTS_eagle.app.v6 = 			\
 	user/libuser.a 				\
 	driver/libdriver.a 			\
-	json/libjson.a 				\
-	upgrade/libupgrade.a 			\
 	platform/libplatform.a 			\
+	task/libtask.a 				\
 	libc/liblibc.a 				\
 	lua/liblua.a 				\
 	lwip/liblwip.a 				\
@@ -80,12 +79,13 @@ COMPONENTS_eagle.app.v6 = 			\
 	u8glib/u8glib.a 			\
 	ucglib/ucglib.a 			\
 	smart/smart.a 				\
-	wofs/wofs.a 				\
 	spiffs/spiffs.a 			\
 	cjson/libcjson.a 			\
 	crypto/libcrypto.a 			\
 	dhtlib/libdhtlib.a 			\
 	tsl2561/tsl2561lib.a			\
+	http/libhttp.a				\
+	net/libnodemcu_net.a			\
 	modules/libmodules.a			\
 
 # Inspect the modules library and work out which modules need to be linked.
@@ -112,10 +112,11 @@ LINKFLAGS_eagle.app.v6 = 			\
 	-lpp 					\
 	-lnet80211 				\
 	-lwpa 					\
+	-lwpa2					\
 	-lmain 					\
-	-ljson 					\
 	-lsmartconfig 				\
 	-lssl 					\
+	-lcrypto				\
 	$(DEP_LIBS_eagle.app.v6) 		\
 	-Wl,--end-group 			\
 	-lm

+ 5 - 0
app/coap/coap.h

@@ -192,6 +192,11 @@ void coap_setup(void);
 void endpoint_setup(void);
 
 int coap_buildOptionHeader(uint32_t optDelta, size_t length, uint8_t *buf, size_t buflen);
+int check_token(coap_packet_t *pkt);
+
+#include "uri.h"
+int coap_make_request(coap_rw_buffer_t *scratch, coap_packet_t *pkt, coap_msgtype_t t, coap_method_t m, coap_uri_t *uri, const uint8_t *payload, size_t payload_len);
+
 
 #ifdef __cplusplus
 }

+ 2 - 0
app/crypto/Makefile

@@ -15,6 +15,8 @@ ifndef PDIR
 GEN_LIBS = libcrypto.a
 endif
 
+STD_CFLAGS=-std=gnu11 -Wimplicit
+
 #############################################################
 # Configuration i.e. compile options etc.
 # Target specific stuff (defines etc.) goes in here!

+ 33 - 0
app/crypto/digests.c

@@ -122,6 +122,39 @@ int ICACHE_FLASH_ATTR crypto_hash (const digest_mech_info_t *mi,
 }
 
 
+int ICACHE_FLASH_ATTR crypto_fhash (const digest_mech_info_t *mi,
+  read_fn read, int readarg,
+  uint8_t *digest)
+{
+  if (!mi)
+    return EINVAL;
+
+  // Initialise
+  void *ctx = (void *)os_malloc (mi->ctx_size);
+  if (!ctx)
+    return ENOMEM;
+  mi->create (ctx);
+
+  // Hash bytes from file in blocks
+  uint8_t* buffer = (uint8_t*)os_malloc (mi->block_size);
+  if (!buffer)
+    return ENOMEM;
+  
+  int read_len = 0;
+  do {
+    read_len = read(readarg, buffer, mi->block_size);
+    mi->update (ctx, buffer, read_len);
+  } while (read_len == mi->block_size);
+
+  // Finish up
+  mi->finalize (digest, ctx);
+
+  os_free (buffer);
+  os_free (ctx);
+  return 0;
+}
+
+
 int ICACHE_FLASH_ATTR crypto_hmac (const digest_mech_info_t *mi,
    const char *data, size_t data_len,
    const char *key, size_t key_len,

+ 11 - 0
app/crypto/digests.h

@@ -6,6 +6,7 @@
 typedef void (*create_ctx_fn)(void *ctx);
 typedef void (*update_ctx_fn)(void *ctx, const uint8_t *msg, int len);
 typedef void (*finalize_ctx_fn)(uint8_t *digest, void *ctx);
+typedef size_t ( *read_fn )(int fd, void *ptr, size_t len);
 
 /**
  * Description of a message digest mechanism.
@@ -53,6 +54,16 @@ const digest_mech_info_t *crypto_digest_mech (const char *mech);
  */
 int crypto_hash (const digest_mech_info_t *mi, const char *data, size_t data_len, uint8_t *digest);
 
+/**
+ * Wrapper function for performing a one-in-all hashing operation of a file.
+ * @param mi       A mech from @c crypto_digest_mech(). A null pointer @c mi
+ *                 is harmless, but will of course result in an error return.
+ * @param read     Pointer to the read function (e.g. fs_read)
+ * @param readarg  Argument to pass to the read function (e.g. file descriptor)
+ * @param digest   Output buffer, must be at least @c mi->digest_size in size.
+ * @return 0 on success, non-zero on error.
+ */
+int crypto_fhash (const digest_mech_info_t *mi, read_fn read, int readarg, uint8_t *digest);
 
 /**
  * Generate a HMAC signature.

+ 131 - 0
app/crypto/mech.c

@@ -0,0 +1,131 @@
+/*
+ * Copyright 2016 Dius Computing Pty Ltd. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the
+ *   distribution.
+ * - Neither the name of the copyright holders nor the names of
+ *   its contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Johny Mattsson <jmattsson@dius.com.au>
+ */
+
+#include "mech.h"
+#include "sdk-aes.h"
+#include "c_string.h"
+
+/* ----- AES ---------------------------------------------------------- */
+
+static const struct aes_funcs
+{
+  void *(*init) (const char *key, size_t keylen);
+  void (*crypt) (void *ctx, const char *in, char *out);
+  void (*deinit) (void *ctx);
+} aes_funcs[] =
+{
+  { aes_encrypt_init, aes_encrypt, aes_encrypt_deinit },
+  { aes_decrypt_init, aes_decrypt, aes_decrypt_deinit }
+};
+
+static bool do_aes (crypto_op_t *co, bool with_cbc)
+{
+  const struct aes_funcs *funcs = &aes_funcs[co->op];
+
+  void *ctx = funcs->init (co->key, co->keylen);
+  if (!ctx)
+    return false;
+
+  char iv[AES_BLOCKSIZE] = { 0 };
+  if (with_cbc && co->ivlen)
+    c_memcpy (iv, co->iv, co->ivlen < AES_BLOCKSIZE ? co->ivlen : AES_BLOCKSIZE);
+
+  const char *src = co->data;
+  char *dst = co->out;
+
+  size_t left = co->datalen;
+  while (left)
+  {
+    char block[AES_BLOCKSIZE] = { 0 };
+    size_t n = left > AES_BLOCKSIZE ? AES_BLOCKSIZE : left;
+    c_memcpy (block, src, n);
+
+    if (with_cbc && co->op == OP_ENCRYPT)
+    {
+      const char *xor = (src == co->data) ? iv : dst - AES_BLOCKSIZE;
+      int i;
+      for (i = 0; i < AES_BLOCKSIZE; ++i)
+        block[i] ^= xor[i];
+    }
+
+    funcs->crypt (ctx, block, dst);
+
+    if (with_cbc && co->op == OP_DECRYPT)
+    {
+      const char *xor = (src == co->data) ? iv : src - AES_BLOCKSIZE;
+      int i;
+      for (i = 0; i < AES_BLOCKSIZE; ++i)
+        dst[i] ^= xor[i];
+    }
+
+    left -= n;
+    src += n;
+    dst += n;
+  }
+
+  funcs->deinit (ctx);
+  return true;
+}
+
+
+static bool do_aes_ecb (crypto_op_t *co)
+{
+  return do_aes (co, false);
+}
+
+static bool do_aes_cbc (crypto_op_t *co)
+{
+  return do_aes (co, true);
+}
+
+
+/* ----- mechs -------------------------------------------------------- */
+
+static const crypto_mech_t mechs[] =
+{
+  { "AES-ECB",  do_aes_ecb, AES_BLOCKSIZE },
+  { "AES-CBC",  do_aes_cbc, AES_BLOCKSIZE }
+};
+
+
+const crypto_mech_t *crypto_encryption_mech (const char *name)
+{
+  size_t i;
+  for (i = 0; i < sizeof (mechs) / sizeof (mechs[0]); ++i)
+  {
+    const crypto_mech_t *mech = mechs + i;
+    if (strcasecmp (name, mech->name) == 0)
+      return mech;
+  }
+  return 0;
+}

+ 30 - 0
app/crypto/mech.h

@@ -0,0 +1,30 @@
+#ifndef _MECH_H_
+#define _MECH_H_
+
+#include "c_types.h"
+
+typedef struct
+{
+  const char *key;
+  size_t keylen;
+  const char *iv;
+  size_t ivlen;
+  const char *data;
+  size_t datalen;
+  char *out;
+  size_t outlen;
+  enum { OP_ENCRYPT, OP_DECRYPT } op;
+} crypto_op_t;
+
+
+typedef struct
+{
+  const char *name;
+  bool (*run) (crypto_op_t *op);
+  uint16_t block_size;
+} crypto_mech_t;
+
+
+const crypto_mech_t *crypto_encryption_mech (const char *name);
+
+#endif

+ 14 - 0
app/crypto/sdk-aes.h

@@ -0,0 +1,14 @@
+#ifndef _SDK_AES_H_
+#define _SDK_AES_H_
+
+#define AES_BLOCKSIZE 16
+
+void *aes_encrypt_init (const char *key, size_t len);
+void aes_encrypt (void *ctx, const char *plain, char *crypt);
+void aes_encrypt_deinit (void *ctx);
+
+void *aes_decrypt_init (const char *key, size_t len);
+void aes_decrypt (void *ctx, const char *crypt, char *plain);
+void aes_decrypt_deinit (void *ctx);
+
+#endif

+ 1 - 0
app/driver/Makefile

@@ -15,6 +15,7 @@ ifndef PDIR
 GEN_LIBS = libdriver.a
 endif
 
+STD_CFLAGS=-std=gnu11 -Wimplicit
 
 #############################################################
 # Configuration i.e. compile options etc.

+ 3 - 2
app/driver/key.c

@@ -17,7 +17,7 @@
 
 #include "driver/key.h"
 
-LOCAL void key_intr_handler(struct keys_param *keys);
+LOCAL void ICACHE_RAM_ATTR key_intr_handler(void *arg);
 
 /******************************************************************************
  * FunctionName : key_init_single
@@ -130,8 +130,9 @@ key_50ms_cb(struct single_key_param *single_key)
  * Returns      : none
 *******************************************************************************/
 LOCAL void
-key_intr_handler(struct keys_param *keys)
+key_intr_handler(void *arg)
 {
+    struct keys_param *keys = arg;
     uint8 i;
     uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
 

+ 109 - 88
app/driver/pwm.c

@@ -14,6 +14,7 @@
 #include "os_type.h"
 #include "osapi.h"
 #include "gpio.h"
+#include "hw_timer.h"
 
 #include "user_interface.h"
 #include "driver/pwm.h"
@@ -21,6 +22,19 @@
 // #define PWM_DBG os_printf
 #define PWM_DBG
 
+// Enabling the next line will cause the interrupt handler to toggle
+// this output pin during processing so that the timing is obvious
+//
+// #define PWM_DBG_PIN	13   // GPIO7
+
+#ifdef PWM_DBG_PIN
+#define PWM_DBG_PIN_HIGH()    GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << PWM_DBG_PIN)
+#define PWM_DBG_PIN_LOW()    GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << PWM_DBG_PIN)
+#else
+#define PWM_DBG_PIN_HIGH()
+#define PWM_DBG_PIN_LOW()
+#endif
+
 LOCAL struct pwm_single_param pwm_single_toggle[2][PWM_CHANNEL + 1];
 LOCAL struct pwm_single_param *pwm_single;
 
@@ -32,7 +46,13 @@ LOCAL int8 pwm_out_io_num[PWM_CHANNEL] = {-1, -1, -1, -1, -1, -1};
 LOCAL uint8 pwm_channel_toggle[2];
 LOCAL uint8 *pwm_channel;
 
+// Toggle flips between 1 and 0 when we make updates so that the interrupt code
+// cn switch cleanly between the two states. The cinterrupt handler uses either
+// the pwm_single_toggle[0] or pwm_single_toggle[1]
+// pwm_toggle indicates which state should be used on the *next* timer interrupt 
+// freq boundary.
 LOCAL uint8 pwm_toggle = 1;
+LOCAL volatile uint8 pwm_current_toggle = 1;
 LOCAL uint8 pwm_timer_down = 1;
 
 LOCAL uint8 pwm_current_channel = 0;
@@ -41,27 +61,8 @@ LOCAL uint16 pwm_gpio = 0;
 
 LOCAL uint8 pwm_channel_num = 0;
 
-//XXX: 0xffffffff/(80000000/16)=35A
-#define US_TO_RTC_TIMER_TICKS(t)          \
-    ((t) ?                                   \
-     (((t) > 0x35A) ?                   \
-      (((t)>>2) * ((APB_CLK_FREQ>>4)/250000) + ((t)&0x3) * ((APB_CLK_FREQ>>4)/1000000))  :    \
-      (((t) *(APB_CLK_FREQ>>4)) / 1000000)) :    \
-     0)
-
-//FRC1
-#define FRC1_ENABLE_TIMER  BIT7
-
-typedef enum {
-    DIVDED_BY_1 = 0,
-    DIVDED_BY_16 = 4,
-    DIVDED_BY_256 = 8,
-} TIMER_PREDIVED_MODE;
-
-typedef enum {
-    TM_LEVEL_INT = 1,
-    TM_EDGE_INT   = 0,
-} TIMER_INT_MODE;
+LOCAL void ICACHE_RAM_ATTR pwm_tim1_intr_handler(os_param_t p);
+#define TIMER_OWNER ((os_param_t) 'P')
 
 LOCAL void ICACHE_FLASH_ATTR
 pwm_insert_sort(struct pwm_single_param pwm[], uint8 n)
@@ -74,7 +75,6 @@ pwm_insert_sort(struct pwm_single_param pwm[], uint8 n)
             struct pwm_single_param tmp;
 
             os_memcpy(&tmp, &pwm[i], sizeof(struct pwm_single_param));
-            os_memcpy(&pwm[i], &pwm[i - 1], sizeof(struct pwm_single_param));
 
             while (tmp.h_time < pwm[j].h_time) {
                 os_memcpy(&pwm[j + 1], &pwm[j], sizeof(struct pwm_single_param));
@@ -89,18 +89,8 @@ pwm_insert_sort(struct pwm_single_param pwm[], uint8 n)
     }
 }
 
-LOCAL volatile uint8 critical = 0;
-
-#define LOCK_PWM(c)  do {                       \
-    while( (c)==1 );                            \
-    (c) = 1;                                    \
-} while (0)
-
-#define UNLOCK_PWM(c) do {                      \
-    (c) = 0;                                    \
-} while (0)
-
-void ICACHE_FLASH_ATTR
+// Returns FALSE if we cannot start
+bool ICACHE_FLASH_ATTR
 pwm_start(void)
 {
     uint8 i, j;
@@ -109,10 +99,19 @@ pwm_start(void)
     PWM_DBG("pwm_out_io_num[0]:%d,[1]:%d,[2]:%d\n",pwm_out_io_num[0],pwm_out_io_num[1],pwm_out_io_num[2]);
     PWM_DBG("pwm.period:%d,pwm.duty[0]:%d,[1]:%d,[2]:%d\n",pwm.period,pwm.duty[0],pwm.duty[1],pwm.duty[2]);
 
-    LOCK_PWM(critical);   // enter critical
+    // First we need to make sure that the interrupt handler is running
+    // out of the same set of params as we expect
+    while (!pwm_timer_down && pwm_toggle != pwm_current_toggle) {
+      os_delay_us(100);
+    }
+    if (pwm_timer_down) {
+      pwm_toggle = pwm_current_toggle;
+    }
+
+    uint8_t new_toggle = pwm_toggle ^ 0x01;
 
-    struct pwm_single_param *local_single = pwm_single_toggle[pwm_toggle ^ 0x01];
-    uint8 *local_channel = &pwm_channel_toggle[pwm_toggle ^ 0x01];
+    struct pwm_single_param *local_single = pwm_single_toggle[new_toggle];
+    uint8 *local_channel = &pwm_channel_toggle[new_toggle];
 
     // step 1: init PWM_CHANNEL+1 channels param
     for (i = 0; i < pwm_channel_num; i++) {
@@ -132,9 +131,10 @@ pwm_start(void)
 
     *local_channel = pwm_channel_num + 1;
     PWM_DBG("1channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time);
-    // step 3: combine same duty channels
+    // step 3: combine same duty channels (or nearly the same duty). If there is
+    // under 2 us between pwm outputs, then treat them as the same.
     for (i = pwm_channel_num; i > 0; i--) {
-        if (local_single[i].h_time == local_single[i - 1].h_time) {
+        if (local_single[i].h_time <= local_single[i - 1].h_time + US_TO_RTC_TIMER_TICKS(2)) {
             local_single[i - 1].gpio_set |= local_single[i].gpio_set;
             local_single[i - 1].gpio_clear |= local_single[i].gpio_clear;
 
@@ -166,28 +166,43 @@ pwm_start(void)
         (*local_channel)--;
     }
 
+    // Make the new ones active
+    pwm_toggle = new_toggle;
+
     // if timer is down, need to set gpio and start timer
     if (pwm_timer_down == 1) {
         pwm_channel = local_channel;
         pwm_single = local_single;
+	pwm_current_toggle = pwm_toggle;
         // start
         gpio_output_set(local_single[0].gpio_set, local_single[0].gpio_clear, pwm_gpio, 0);
 
         // yeah, if all channels' duty is 0 or 255, don't need to start timer, otherwise start...
         if (*local_channel != 1) {
-            pwm_timer_down = 0;
-            RTC_REG_WRITE(FRC1_LOAD_ADDRESS, local_single[0].h_time);
-        }
-    }
-
-    if (pwm_toggle == 1) {
-        pwm_toggle = 0;
+	  PWM_DBG("Need to setup timer\n");
+	  if (!platform_hw_timer_init(TIMER_OWNER, NMI_SOURCE, FALSE)) {
+	    return FALSE;
+	  }
+	  pwm_timer_down = 0;
+	  platform_hw_timer_set_func(TIMER_OWNER, pwm_tim1_intr_handler, 0);
+	  platform_hw_timer_arm_ticks(TIMER_OWNER, local_single[0].h_time);
+	} else {
+	  PWM_DBG("Timer left idle\n");
+	  platform_hw_timer_close(TIMER_OWNER);
+	}
     } else {
-        pwm_toggle = 1;
+      // ensure that all outputs are outputs
+      gpio_output_set(0, 0, pwm_gpio, 0);
     }
 
-    UNLOCK_PWM(critical);   // leave critical
+#ifdef PWM_DBG_PIN
+    // Enable as output
+    gpio_output_set(0, 0, 1 << PWM_DBG_PIN, 0);
+#endif
+
     PWM_DBG("3channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time);
+
+    return TRUE;
 }
 
 /******************************************************************************
@@ -210,7 +225,6 @@ pwm_set_duty(uint16 duty, uint8 channel)
     if(i==pwm_channel_num)      // non found
         return;
 
-    LOCK_PWM(critical);   // enter critical
     if (duty < 1) {
         pwm.duty[channel] = 0;
     } else if (duty >= PWM_DEPTH) {
@@ -218,7 +232,6 @@ pwm_set_duty(uint16 duty, uint8 channel)
     } else {
     	pwm.duty[channel] = duty;
     }
-    UNLOCK_PWM(critical);   // leave critical
 }
 
 /******************************************************************************
@@ -230,7 +243,6 @@ pwm_set_duty(uint16 duty, uint8 channel)
 void ICACHE_FLASH_ATTR
 pwm_set_freq(uint16 freq, uint8 channel)
 {
-    LOCK_PWM(critical);   // enter critical
     if (freq > PWM_FREQ_MAX) {
         pwm.freq = PWM_FREQ_MAX;
     } else if (freq < 1) {
@@ -240,7 +252,6 @@ pwm_set_freq(uint16 freq, uint8 channel)
     }
 
     pwm.period = PWM_1S / pwm.freq;
-    UNLOCK_PWM(critical);   // leave critical
 }
 
 /******************************************************************************
@@ -306,35 +317,57 @@ pwm_get_freq(uint8 channel)
  * Returns      : NONE
 *******************************************************************************/
 LOCAL void ICACHE_RAM_ATTR
-pwm_tim1_intr_handler(void)
+pwm_tim1_intr_handler(os_param_t p)
 {
-    uint8 local_toggle = pwm_toggle;                        // pwm_toggle may change outside
-    RTC_CLR_REG_MASK(FRC1_INT_ADDRESS, FRC1_INT_CLR_MASK);
+  (void)p;
 
-    if (pwm_current_channel >= (*pwm_channel - 1)) {        // *pwm_channel may change outside
-        pwm_single = pwm_single_toggle[local_toggle];
-        pwm_channel = &pwm_channel_toggle[local_toggle];
+  PWM_DBG_PIN_HIGH();
 
-        gpio_output_set(pwm_single[*pwm_channel - 1].gpio_set,
-                        pwm_single[*pwm_channel - 1].gpio_clear,
-                        pwm_gpio,
-                        0);
+  int offset = 0;
 
-        pwm_current_channel = 0;
+  while (1) {
+    if (pwm_current_channel >= (*pwm_channel - 1)) {      
+      pwm_single = pwm_single_toggle[pwm_toggle];
+      pwm_channel = &pwm_channel_toggle[pwm_toggle];
+      pwm_current_toggle = pwm_toggle;
 
-        if (*pwm_channel != 1) {
-            RTC_REG_WRITE(FRC1_LOAD_ADDRESS, pwm_single[pwm_current_channel].h_time);
-        } else {
-            pwm_timer_down = 1;
-        }
+      gpio_output_set(pwm_single[*pwm_channel - 1].gpio_set,
+		      pwm_single[*pwm_channel - 1].gpio_clear,
+		      0,
+		      0);
+
+      pwm_current_channel = 0;
+
+      if (*pwm_channel == 1) {
+	pwm_timer_down = 1;
+	break;
+      }
     } else {
-        gpio_output_set(pwm_single[pwm_current_channel].gpio_set,
-                        pwm_single[pwm_current_channel].gpio_clear,
-                        pwm_gpio, 0);
+      gpio_output_set(pwm_single[pwm_current_channel].gpio_set,
+		      pwm_single[pwm_current_channel].gpio_clear,
+		      0, 0);
+
+      pwm_current_channel++;
+    }
 
-        pwm_current_channel++;
-        RTC_REG_WRITE(FRC1_LOAD_ADDRESS, pwm_single[pwm_current_channel].h_time);
+    int next_time = pwm_single[pwm_current_channel].h_time;
+    // Delay now holds the time (in ticks) since when the last timer expiry was
+    PWM_DBG_PIN_LOW();
+    int delay = platform_hw_timer_get_delay_ticks(TIMER_OWNER) + 4 - offset;
+
+    offset += next_time;
+
+    next_time = next_time - delay;
+
+    if (next_time > US_TO_RTC_TIMER_TICKS(4)) {
+      PWM_DBG_PIN_HIGH();
+      platform_hw_timer_arm_ticks(TIMER_OWNER, next_time);
+      break;
     }
+    PWM_DBG_PIN_HIGH();
+  }
+
+  PWM_DBG_PIN_LOW();
 }
 
 /******************************************************************************
@@ -342,19 +375,13 @@ pwm_tim1_intr_handler(void)
  * Description  : pwm gpio, params and timer initialization
  * Parameters   : uint16 freq : pwm freq param
  *                uint16 *duty : each channel's duty
- * Returns      : NONE
+ * Returns      : void
 *******************************************************************************/
 void ICACHE_FLASH_ATTR
 pwm_init(uint16 freq, uint16 *duty)
 {
     uint8 i;
 
-    RTC_REG_WRITE(FRC1_CTRL_ADDRESS,  //FRC2_AUTO_RELOAD|
-                  DIVDED_BY_16
-                  | FRC1_ENABLE_TIMER
-                  | TM_EDGE_INT);
-    RTC_REG_WRITE(FRC1_LOAD_ADDRESS, 0);
-
     // PIN_FUNC_SELECT(PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC);
     // PIN_FUNC_SELECT(PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC);
     // PIN_FUNC_SELECT(PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC);
@@ -372,10 +399,8 @@ pwm_init(uint16 freq, uint16 *duty)
     // pwm_set_freq_duty(freq, duty);
 
     pwm_start();
-    
-    ETS_FRC_TIMER1_INTR_ATTACH(pwm_tim1_intr_handler, NULL);
-    TM1_EDGE_INT_ENABLE();
-    ETS_FRC1_INTR_ENABLE();
+
+    PWM_DBG("pwm_init returning\n");
 }
 
 bool ICACHE_FLASH_ATTR
@@ -389,14 +414,12 @@ pwm_add(uint8 channel){
         if(pwm_out_io_num[i]==channel)  // already exist
             return true;
         if(pwm_out_io_num[i] == -1){ // empty exist
-            LOCK_PWM(critical);   // enter critical
             pwm_out_io_num[i] = channel;
             pwm.duty[i] = 0;
             pwm_gpio |= (1 << pin_num[channel]);
             PIN_FUNC_SELECT(pin_mux[channel], pin_func[channel]);
             GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[channel])), GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[channel]))) & (~ GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE))); //disable open drain;
             pwm_channel_num++;
-            UNLOCK_PWM(critical);   // leave critical
             return true;
         }
     }
@@ -412,7 +435,6 @@ pwm_delete(uint8 channel){
     uint8 i,j;
     for(i=0;i<pwm_channel_num;i++){
         if(pwm_out_io_num[i]==channel){  // exist
-            LOCK_PWM(critical);   // enter critical
             pwm_out_io_num[i] = -1;
             pwm_gpio &= ~(1 << pin_num[channel]);   //clear the bit
             for(j=i;j<pwm_channel_num-1;j++){
@@ -422,7 +444,6 @@ pwm_delete(uint8 channel){
             pwm_out_io_num[pwm_channel_num-1] = -1;
             pwm.duty[pwm_channel_num-1] = 0;
             pwm_channel_num--;
-            UNLOCK_PWM(critical);   // leave critical
             return true;
         }
     }

+ 320 - 0
app/driver/rotary.c

@@ -0,0 +1,320 @@
+/*
+ * Driver for interfacing to cheap rotary switches that
+ * have a quadrature output with an optional press button
+ *
+ * This sets up the relevant gpio as interrupt and then keeps track of
+ * the position of the switch in software. Changes are enqueued to task
+ * level and a task message posted when required. If the queue fills up
+ * then moves are ignored, but the last press/release will be included.
+ *
+ * Philip Gladstone, N1DQ
+ */
+
+#include "platform.h"
+#include "c_types.h"
+#include "../libc/c_stdlib.h"
+#include "../libc/c_stdio.h"
+#include "driver/rotary.h"
+#include "user_interface.h"
+#include "task/task.h"
+#include "ets_sys.h"
+
+//
+//  Queue is empty if read == write. 
+//  However, we always want to keep the previous value
+//  so writing is only allowed if write - read < QUEUE_SIZE - 1
+
+#define QUEUE_SIZE 	8
+
+#define GET_LAST_STATUS(d)	(d->queue[(d->write_offset-1) & (QUEUE_SIZE - 1)])
+#define GET_PREV_STATUS(d)	(d->queue[(d->write_offset-2) & (QUEUE_SIZE - 1)])
+#define HAS_QUEUED_DATA(d)	(d->read_offset < d->write_offset)
+#define HAS_QUEUE_SPACE(d)	(d->read_offset + QUEUE_SIZE - 1 > d->write_offset)
+
+#define REPLACE_STATUS(d, x)    (d->queue[(d->write_offset-1) & (QUEUE_SIZE - 1)] = (rotary_event_t) { (x), system_get_time() })
+#define QUEUE_STATUS(d, x)      (d->queue[(d->write_offset++) & (QUEUE_SIZE - 1)] = (rotary_event_t) { (x), system_get_time() })
+
+#define GET_READ_STATUS(d)	(d->queue[d->read_offset & (QUEUE_SIZE - 1)])
+#define ADVANCE_IF_POSSIBLE(d)  if (d->read_offset < d->write_offset) { d->read_offset++; }
+
+#define STATUS_IS_PRESSED(x)	((x & 0x80000000) != 0)
+
+typedef struct {
+  int8_t   phase_a_pin;
+  int8_t   phase_b_pin;
+  int8_t   press_pin;
+  uint32_t read_offset;  // Accessed by task
+  uint32_t write_offset;	// Accessed by ISR
+  uint32_t pin_mask;
+  uint32_t phase_a;
+  uint32_t phase_b;
+  uint32_t press;
+  uint32_t last_press_change_time;
+  int	   tasknumber;
+  rotary_event_t    queue[QUEUE_SIZE];
+} DATA;
+
+static DATA *data[ROTARY_CHANNEL_COUNT];
+
+static uint8_t task_queued;
+
+static void set_gpio_bits(void);
+
+static void rotary_clear_pin(int pin) 
+{
+  if (pin >= 0) {
+    gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[pin]), GPIO_PIN_INTR_DISABLE);
+    platform_gpio_mode(pin, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP);
+  }
+}
+
+// Just takes the channel number. Cleans up the resources used.
+int rotary_close(uint32_t channel) 
+{
+  if (channel >= sizeof(data) / sizeof(data[0])) {
+    return -1;
+  }
+
+  DATA *d = data[channel];
+
+  if (!d) {
+    return 0;
+  }
+
+  data[channel] = NULL;
+
+  rotary_clear_pin(d->phase_a_pin);
+  rotary_clear_pin(d->phase_b_pin);
+  rotary_clear_pin(d->press_pin);
+
+  c_free(d);
+
+  set_gpio_bits();
+
+  return 0;
+}
+
+static uint32_t  ICACHE_RAM_ATTR rotary_interrupt(uint32_t ret_gpio_status) 
+{
+  // This function really is running at interrupt level with everything
+  // else masked off. It should take as little time as necessary.
+  //
+  //
+
+  // This gets the set of pins which have changed status
+  uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
+
+  int i;
+  for (i = 0; i < sizeof(data) / sizeof(data[0]); i++) {
+    DATA *d = data[i];
+    if (!d || (gpio_status & d->pin_mask) == 0) {
+      continue;
+    }
+
+    GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & d->pin_mask);
+
+    uint32_t bits = GPIO_REG_READ(GPIO_IN_ADDRESS);
+
+    uint32_t last_status = GET_LAST_STATUS(d).pos;
+
+    uint32_t now = system_get_time();
+
+    uint32_t new_status;
+
+    new_status = last_status & 0x80000000;
+
+    // This is the debounce logic for the press switch. We ignore changes
+    // for 10ms after a change.
+    if (now - d->last_press_change_time > 10 * 1000) {
+      new_status = (bits & d->press) ? 0 : 0x80000000;
+      if (STATUS_IS_PRESSED(new_status ^ last_status)) {
+        d->last_press_change_time = now;
+      }
+    }
+
+    //  A   B
+    //  1   1   => 0
+    //  1   0   => 1
+    //  0   0   => 2
+    //  0   1   => 3
+
+    int micropos = 2;
+    if (bits & d->phase_b) {
+      micropos = 3;
+    }
+    if (bits & d->phase_a) {
+      micropos ^= 3;
+    }
+
+    int32_t rotary_pos = last_status;
+
+    switch ((micropos - last_status) & 3) {
+      case 0:
+        // No change, nothing to do
+	break;
+      case 1:
+        // Incremented by 1
+	rotary_pos++;
+	break;
+      case 3:
+        // Decremented by 1
+	rotary_pos--;
+	break;
+      default:
+        // We missed an interrupt
+	// We will ignore... but mark it.
+	rotary_pos += 1000000;
+	break;
+    }
+
+    new_status |= rotary_pos & 0x7fffffff;
+    
+    if (last_status != new_status) {
+      // Either we overwrite the status or we add a new one
+      if (!HAS_QUEUED_DATA(d) 
+	  || STATUS_IS_PRESSED(last_status ^ new_status)
+	  || STATUS_IS_PRESSED(last_status ^ GET_PREV_STATUS(d).pos)) {
+	if (HAS_QUEUE_SPACE(d)) {
+	  QUEUE_STATUS(d, new_status);
+	  if (!task_queued) {
+	    if (task_post_medium(d->tasknumber, (os_param_t) &task_queued)) {
+	      task_queued = 1;
+	    }
+	  }
+	} else {
+	  REPLACE_STATUS(d, new_status);
+	}
+      } else {
+	REPLACE_STATUS(d, new_status);
+      }
+    }
+    ret_gpio_status &= ~(d->pin_mask);
+  }
+
+  return ret_gpio_status;
+}
+
+// The pin numbers are actual platform GPIO numbers
+int rotary_setup(uint32_t channel, int phase_a, int phase_b, int press, task_handle_t tasknumber )
+{
+  if (channel >= sizeof(data) / sizeof(data[0])) {
+    return -1;
+  }
+
+  if (data[channel]) {
+    if (rotary_close(channel)) {
+      return -1;
+    }
+  }
+
+  DATA *d = (DATA *) c_zalloc(sizeof(DATA));
+  if (!d) {
+    return -1;
+  }
+
+  data[channel] = d;
+  int i;
+
+  d->tasknumber = tasknumber;
+
+  d->phase_a = 1 << pin_num[phase_a];
+  platform_gpio_mode(phase_a, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
+  gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[phase_a]), GPIO_PIN_INTR_ANYEDGE);
+  d->phase_a_pin = phase_a;
+
+  d->phase_b = 1 << pin_num[phase_b];
+  platform_gpio_mode(phase_b, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
+  gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[phase_b]), GPIO_PIN_INTR_ANYEDGE);
+  d->phase_b_pin = phase_b;
+
+  if (press >= 0) {
+    d->press = 1 << pin_num[press];
+    platform_gpio_mode(press, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
+    gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[press]), GPIO_PIN_INTR_ANYEDGE);
+  }
+  d->press_pin = press;
+
+  d->pin_mask = d->phase_a | d->phase_b | d->press;
+
+  set_gpio_bits();
+
+  return 0;
+}
+
+static void set_gpio_bits()
+{
+  uint32_t bits = 0;
+  for (int i = 0; i < ROTARY_CHANNEL_COUNT; i++) {
+    DATA *d = data[i];
+
+    if (d) {
+      bits = bits | d->pin_mask;
+    }
+  }
+
+  platform_gpio_register_intr_hook(bits, rotary_interrupt);
+}
+
+bool rotary_has_queued_event(uint32_t channel)
+{
+  if (channel >= sizeof(data) / sizeof(data[0])) {
+    return FALSE;
+  }
+
+  DATA *d = data[channel];
+
+  if (!d) {
+    return FALSE;
+  }
+
+  return HAS_QUEUED_DATA(d);
+}
+
+// Get the oldest event in the queue and remove it (if possible)
+bool rotary_getevent(uint32_t channel, rotary_event_t *resultp) 
+{
+  rotary_event_t result = { 0 };
+  
+  if (channel >= sizeof(data) / sizeof(data[0])) {
+    return FALSE;
+  }
+
+  DATA *d = data[channel];
+
+  if (!d) {
+    return FALSE;
+  }
+
+  ETS_GPIO_INTR_DISABLE();
+
+  bool status = FALSE;
+
+  if (HAS_QUEUED_DATA(d)) {
+    result = GET_READ_STATUS(d);
+    d->read_offset++;
+    status = TRUE;
+  } else {
+    result = GET_LAST_STATUS(d);
+  }
+
+  ETS_GPIO_INTR_ENABLE();
+
+  *resultp = result;
+
+  return status;
+}
+
+int rotary_getpos(uint32_t channel)
+{
+  if (channel >= sizeof(data) / sizeof(data[0])) {
+    return -1;
+  }
+
+  DATA *d = data[channel];
+
+  if (!d) {
+    return -1;
+  }
+
+  return GET_LAST_STATUS(d).pos;
+}

+ 33 - 0
app/driver/sigma_delta.c

@@ -0,0 +1,33 @@
+
+#include "driver/sigma_delta.h"
+
+
+void sigma_delta_setup( void )
+{
+    GPIO_REG_WRITE(GPIO_SIGMA_DELTA,
+                   GPIO_SIGMA_DELTA_SET(GPIO_SIGMA_DELTA_ENABLE) |
+                   GPIO_SIGMA_DELTA_TARGET_SET(0x00) |
+                   GPIO_SIGMA_DELTA_PRESCALE_SET(0x00));
+}
+
+void sigma_delta_stop( void )
+{
+    GPIO_REG_WRITE(GPIO_SIGMA_DELTA,
+                   GPIO_SIGMA_DELTA_SET(GPIO_SIGMA_DELTA_DISABLE) |
+                   GPIO_SIGMA_DELTA_TARGET_SET(0x00) |
+                   GPIO_SIGMA_DELTA_PRESCALE_SET(0x00) );
+}
+
+void sigma_delta_set_prescale_target( sint16 prescale, sint16 target )
+{
+    uint32_t prescale_mask, target_mask;
+
+    prescale_mask = prescale >= 0 ? GPIO_SIGMA_DELTA_PRESCALE_MASK : 0x00;
+    target_mask   = target   >= 0 ? GPIO_SIGMA_DELTA_TARGET_MASK   : 0x00;
+
+    // set prescale and target with one register access to avoid glitches
+    GPIO_REG_WRITE(GPIO_SIGMA_DELTA,
+                   (GPIO_REG_READ(GPIO_SIGMA_DELTA) & ~(prescale_mask | target_mask)) |
+                   (GPIO_SIGMA_DELTA_PRESCALE_SET(prescale) & prescale_mask) |
+                   (GPIO_SIGMA_DELTA_TARGET_SET(target) & target_mask));
+}

+ 40 - 35
app/driver/spi.c

@@ -70,37 +70,27 @@ void spi_master_init(uint8 spi_no, unsigned cpol, unsigned cpha, uint32_t clock_
 
 	if(spi_no>1) 		return; //handle invalid input number
 
-	if(spi_no==SPI){
-		WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005); 
-		PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1);//configure io to spi mode
-		PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1);//configure io to spi mode	
-		PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1);//configure io to spi mode	
-		PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1);//configure io to spi mode	
-	}
-	else if(spi_no==HSPI){
-		WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); 
-		PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure io to spi mode
-		PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure io to spi mode	
-		PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure io to spi mode	
-		PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure io to spi mode	
-	}
-
 	SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD|SPI_RD_BYTE_ORDER|SPI_WR_BYTE_ORDER|SPI_DOUTDIN);
 
-	//set clock polarity
-	// TODO: This doesn't work
-	//if (cpol == 1) {
-	//    SET_PERI_REG_MASK(SPI_CTRL2(spi_no), (SPI_CK_OUT_HIGH_MODE<<SPI_CK_OUT_HIGH_MODE_S));
-	//} else {
-	//    SET_PERI_REG_MASK(SPI_CTRL2(spi_no), (SPI_CK_OUT_LOW_MODE<<SPI_CK_OUT_LOW_MODE_S));
-	//}
-	//os_printf("SPI_CTRL2 is %08x\n",READ_PERI_REG(SPI_CTRL2(spi_no)));
-
+	// set clock polarity (Reference: http://bbs.espressif.com/viewtopic.php?f=49&t=1570)
+	// phase is dependent on polarity. See Issue #1161
+	if (cpol == 1) {
+		SET_PERI_REG_MASK(SPI_PIN(spi_no), SPI_IDLE_EDGE);
+	} else {
+		CLEAR_PERI_REG_MASK(SPI_PIN(spi_no), SPI_IDLE_EDGE);
+	}
+	
 	//set clock phase
-	if (cpha == 1) {
-		SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE|SPI_CK_I_EDGE);
+	if (cpha == cpol) {
+		// Mode 3: MOSI is set on falling edge of clock
+		// Mode 0: MOSI is set on falling edge of clock
+		CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE);
+		SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_I_EDGE);
 	} else {
-    		CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE|SPI_CK_I_EDGE);
+		// Mode 2: MOSI is set on rising edge of clock
+		// Mode 1: MOSI is set on rising edge of clock	
+		SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_OUT_EDGE);		
+		CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_CK_I_EDGE);
 	}
 
 	CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE|SPI_USR_MISO|SPI_USR_ADDR|SPI_USR_COMMAND|SPI_USR_DUMMY);
@@ -123,6 +113,21 @@ void spi_master_init(uint8 spi_no, unsigned cpol, unsigned cpha, uint32_t clock_
 					((1&SPI_CLKCNT_N)<<SPI_CLKCNT_N_S)|
 					((0&SPI_CLKCNT_H)<<SPI_CLKCNT_H_S)|
 					((1&SPI_CLKCNT_L)<<SPI_CLKCNT_L_S)); //clear bit 31,set SPI clock div
+
+	if(spi_no==SPI){
+		WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005); 
+		PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1);//configure io to spi mode
+		PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1);//configure io to spi mode	
+		PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1);//configure io to spi mode	
+		PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1);//configure io to spi mode	
+	}
+	else if(spi_no==HSPI){
+		WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); 
+		PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure io to spi mode
+		PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure io to spi mode	
+		PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure io to spi mode	
+		PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure io to spi mode	
+	}
 }
 
 /******************************************************************************
@@ -130,12 +135,12 @@ void spi_master_init(uint8 spi_no, unsigned cpol, unsigned cpha, uint32_t clock_
  * Description  : Enter provided data into MOSI buffer.
  *                The data is regarded as a sequence of bits with length 'bitlen'.
  *                It will be written left-aligned starting from position 'offset'.
- * Parameters   :   uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
- *                  uint8 offset - offset into MOSI buffer (number of bits)
- *                  uint8 bitlen - valid number of bits in data
+ * Parameters   :   uint8  spi_no - SPI module number, Only "SPI" and "HSPI" are valid
+ *                  uint16 offset - offset into MOSI buffer (number of bits)
+ *                  uint8  bitlen - valid number of bits in data
  *                  uint32 data  - data to be written into buffer
 *******************************************************************************/
-void spi_mast_set_mosi(uint8 spi_no, uint8 offset, uint8 bitlen, uint32 data)
+void spi_mast_set_mosi(uint8 spi_no, uint16 offset, uint8 bitlen, uint32 data)
 {
     uint8  wn, wn_offset, wn_bitlen;
     uint32 wn_data;
@@ -188,11 +193,11 @@ void spi_mast_set_mosi(uint8 spi_no, uint8 offset, uint8 bitlen, uint32 data)
  * Description  : Retrieve data from MISO buffer.
  *                The data is regarded as a sequence of bits with length 'bitlen'.
  *                It will be read starting left-aligned from position 'offset'.
- * Parameters   :   uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
- *                  uint8 offset - offset into MISO buffer (number of bits)
- *                  uint8 bitlen - requested number of bits in data
+ * Parameters   :   uint8  spi_no - SPI module number, Only "SPI" and "HSPI" are valid
+ *                  uint16 offset - offset into MISO buffer (number of bits)
+ *                  uint8  bitlen - requested number of bits in data
 *******************************************************************************/
-uint32 spi_mast_get_miso(uint8 spi_no, uint8 offset, uint8 bitlen)
+uint32 spi_mast_get_miso(uint8 spi_no, uint16 offset, uint8 bitlen)
 {
     uint8  wn, wn_offset, wn_bitlen;
     uint32 wn_data = 0;

+ 45 - 12
app/driver/uart.c

@@ -12,8 +12,10 @@
 #include "ets_sys.h"
 #include "osapi.h"
 #include "driver/uart.h"
+#include "task/task.h"
 #include "user_config.h"
 #include "user_interface.h"
+#include "osapi.h"
 
 #define UART0   0
 #define UART1   1
@@ -27,12 +29,13 @@
 
 
 // For event signalling
-static uint8 task = USER_TASK_PRIO_MAX;
-static os_signal_t sig;
+static task_handle_t sig = 0;
 
 // UartDev is defined and initialized in rom code.
 extern UartDevice UartDev;
 
+static os_timer_t autobaud_timer;
+
 LOCAL void ICACHE_RAM_ATTR
 uart0_rx_intr_handler(void *para);
 
@@ -248,7 +251,7 @@ uart0_rx_intr_handler(void *para)
         if (RcvChar == '\r' || RcvChar == '\n' ) {
             pRxBuff->BuffState = WRITE_OVER;
         }
-        
+
         if (pRxBuff->pWritePos == (pRxBuff->pRcvMsgBuff + RX_BUFF_SIZE)) {
             // overflow ...we may need more error handle here.
             pRxBuff->pWritePos = pRxBuff->pRcvMsgBuff ;
@@ -258,7 +261,7 @@ uart0_rx_intr_handler(void *para)
 
         if (pRxBuff->pWritePos == pRxBuff->pReadPos){   // overflow one byte, need push pReadPos one byte ahead
             if (pRxBuff->pReadPos == (pRxBuff->pRcvMsgBuff + RX_BUFF_SIZE)) {
-                pRxBuff->pReadPos = pRxBuff->pRcvMsgBuff ; 
+                pRxBuff->pReadPos = pRxBuff->pRcvMsgBuff ;
             } else {
                 pRxBuff->pReadPos++;
             }
@@ -267,8 +270,38 @@ uart0_rx_intr_handler(void *para)
         got_input = true;
     }
 
-    if (got_input && task != USER_TASK_PRIO_MAX)
-      system_os_post (task, sig, UART0);
+    if (got_input && sig)
+      task_post_low (sig, false);
+}
+
+static void 
+uart_autobaud_timeout(void *timer_arg)
+{
+  uint32_t uart_no = (uint32_t) timer_arg;
+  uint32_t divisor = uart_baudrate_detect(uart_no, 1);
+  static int called_count = 0;
+
+  // Shut off after two minutes to stop wasting CPU cycles if insufficient input received
+  if (called_count++ > 10 * 60 * 2 || divisor) {
+    os_timer_disarm(&autobaud_timer);
+  }
+
+  if (divisor) {
+    uart_div_modify(uart_no, divisor);
+  }
+}
+
+static void 
+uart_init_autobaud(uint32_t uart_no)
+{
+  os_timer_setfn(&autobaud_timer, uart_autobaud_timeout, (void *) uart_no);
+  os_timer_arm(&autobaud_timer, 100, TRUE);
+}
+
+static void 
+uart_stop_autobaud()
+{
+  os_timer_disarm(&autobaud_timer);
 }
 
 /******************************************************************************
@@ -281,9 +314,8 @@ uart0_rx_intr_handler(void *para)
  * Returns      : NONE
 *******************************************************************************/
 void ICACHE_FLASH_ATTR
-uart_init(UartBautRate uart0_br, UartBautRate uart1_br, uint8 task_prio, os_signal_t sig_input)
+uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input)
 {
-    task = task_prio;
     sig = sig_input;
 
     // rom use 74880 baut_rate, here reinitialize
@@ -292,16 +324,17 @@ uart_init(UartBautRate uart0_br, UartBautRate uart1_br, uint8 task_prio, os_sign
     UartDev.baut_rate = uart1_br;
     uart_config(UART1);
     ETS_UART_INTR_ENABLE();
-
-    // install uart1 putc callback
-#ifndef NODE_DEBUG
-    os_install_putc1((void *)uart1_write_char);
+#ifdef BIT_RATE_AUTOBAUD
+    uart_init_autobaud(0);
 #endif
 }
 
 void ICACHE_FLASH_ATTR
 uart_setup(uint8 uart_no)
 {
+#ifdef BIT_RATE_AUTOBAUD
+    uart_stop_autobaud();
+#endif
     ETS_UART_INTR_DISABLE();
     uart_config(uart_no);
     ETS_UART_INTR_ENABLE();

+ 5 - 4
app/upgrade/Makefile → app/http/Makefile

@@ -12,11 +12,10 @@
 #     a generated lib/image xxx.a ()
 #
 ifndef PDIR
-
-GEN_LIBS = libupgrade.a
-
+GEN_LIBS = libhttp.a
 endif
 
+STD_CFLAGS=-std=gnu11 -Wimplicit
 
 #############################################################
 # Configuration i.e. compile options etc.
@@ -41,7 +40,9 @@ endif
 
 INCLUDES := $(INCLUDES) -I $(PDIR)include
 INCLUDES += -I ./
-INCLUDES += -I ../../include/ets
+INCLUDES += -I ./include
+INCLUDES += -I ../include
+INCLUDES += -I ../../include
 PDIR := ../$(PDIR)
 sinclude $(PDIR)Makefile
 

+ 545 - 0
app/http/httpclient.c

@@ -0,0 +1,545 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Martin d'Allens <martin.dallens@gmail.com> wrote this file. As long as you retain
+ * this notice you can do whatever you want with this stuff. If we meet some day,
+ * and you think this stuff is worth it, you can buy me a beer in return.
+ * ----------------------------------------------------------------------------
+ */
+
+/*
+ * FIXME: sprintf->snprintf everywhere.
+ * FIXME: support null characters in responses.
+ */
+
+#include "osapi.h"
+#include "user_interface.h"
+#include "espconn.h"
+#include "mem.h"
+#include "limits.h"
+#include "httpclient.h"
+#include "stdlib.h"
+
+/* Internal state. */
+typedef struct request_args_t {
+	char		* hostname;
+	int		port;
+	bool		secure;
+	char		* method;
+	char		* path;
+	char		* headers;
+	char		* post_data;
+	char		* buffer;
+	int		buffer_size;
+	int		timeout;
+	os_timer_t	timeout_timer;
+	http_callback_t callback_handle;
+} request_args_t;
+
+static char * ICACHE_FLASH_ATTR esp_strdup( const char * str )
+{
+	if ( str == NULL )
+	{
+		return(NULL);
+	}
+	char * new_str = (char *) os_malloc( os_strlen( str ) + 1 ); /* 1 for null character */
+	if ( new_str == NULL )
+	{
+		HTTPCLIENT_DEBUG( "esp_strdup: malloc error" );
+		return(NULL);
+	}
+	os_strcpy( new_str, str );
+	return(new_str);
+}
+
+
+static int ICACHE_FLASH_ATTR
+esp_isupper( char c )
+{
+	return(c >= 'A' && c <= 'Z');
+}
+
+
+static int ICACHE_FLASH_ATTR
+esp_isalpha( char c )
+{
+	return( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') );
+}
+
+
+static int ICACHE_FLASH_ATTR
+esp_isspace( char c )
+{
+	return(c == ' ' || c == '\t' || c == '\n' || c == '\12');
+}
+
+
+static int ICACHE_FLASH_ATTR
+esp_isdigit( char c )
+{
+	return(c >= '0' && c <= '9');
+}
+
+
+static int ICACHE_FLASH_ATTR http_chunked_decode( const char * chunked, char * decode )
+{
+	int	i		= 0, j = 0;
+	int	decode_size	= 0;
+	char	*str		= (char *) chunked;
+	do
+	{
+		char * endstr;
+		/* [chunk-size] */
+		i = strtoul( str + j, NULL, 16 );
+		HTTPCLIENT_DEBUG( "Chunk Size:%d\r\n", i );
+		if ( i <= 0 )
+			break;
+		/* [chunk-size-end-ptr] */
+		endstr = (char *) os_strstr( str + j, "\r\n" );
+		/* [chunk-ext] */
+		j += endstr - (str + j);
+		/* [CRLF] */
+		j += 2;
+		/* [chunk-data] */
+		decode_size += i;
+		os_memcpy( (char *) &decode[decode_size - i], (char *) str + j, i );
+		j += i;
+		/* [CRLF] */
+		j += 2;
+	}
+	while ( true );
+
+	/*
+	 *
+	 * footer CRLF
+	 *
+	 */
+
+	return(j);
+}
+
+
+static void ICACHE_FLASH_ATTR http_receive_callback( void * arg, char * buf, unsigned short len )
+{
+	struct espconn	* conn	= (struct espconn *) arg;
+	request_args_t	* req	= (request_args_t *) conn->reverse;
+
+	if ( req->buffer == NULL )
+	{
+		return;
+	}
+
+	/* Let's do the equivalent of a realloc(). */
+	const int	new_size = req->buffer_size + len;
+	char		* new_buffer;
+	if ( new_size > BUFFER_SIZE_MAX || NULL == (new_buffer = (char *) os_malloc( new_size ) ) )
+	{
+		HTTPCLIENT_DEBUG( "Response too long (%d)\n", new_size );
+		req->buffer[0] = '\0';                                                                  /* Discard the buffer to avoid using an incomplete response. */
+		if ( req->secure )
+			espconn_secure_disconnect( conn );
+		else
+			espconn_disconnect( conn );
+		return;                                                                                 /* The disconnect callback will be called. */
+	}
+
+	os_memcpy( new_buffer, req->buffer, req->buffer_size );
+	os_memcpy( new_buffer + req->buffer_size - 1 /*overwrite the null character*/, buf, len );      /* Append new data. */
+	new_buffer[new_size - 1] = '\0';                                                                /* Make sure there is an end of string. */
+
+	os_free( req->buffer );
+	req->buffer		= new_buffer;
+	req->buffer_size	= new_size;
+}
+
+
+static void ICACHE_FLASH_ATTR http_send_callback( void * arg )
+{
+	struct espconn	* conn	= (struct espconn *) arg;
+	request_args_t	* req	= (request_args_t *) conn->reverse;
+
+	if ( req->post_data == NULL )
+	{
+		HTTPCLIENT_DEBUG( "All sent\n" );
+	}
+	else  
+	{
+		/* The headers were sent, now send the contents. */
+		HTTPCLIENT_DEBUG( "Sending request body\n" );
+		if ( req->secure )
+			espconn_secure_send( conn, (uint8_t *) req->post_data, strlen( req->post_data ) );
+		else
+			espconn_send( conn, (uint8_t *) req->post_data, strlen( req->post_data ) );
+		os_free( req->post_data );
+		req->post_data = NULL;
+	}
+}
+
+
+static void ICACHE_FLASH_ATTR http_connect_callback( void * arg )
+{
+	HTTPCLIENT_DEBUG( "Connected\n" );
+	struct espconn	* conn	= (struct espconn *) arg;
+	request_args_t	* req	= (request_args_t *) conn->reverse;
+
+	espconn_regist_recvcb( conn, http_receive_callback );
+	espconn_regist_sentcb( conn, http_send_callback );
+
+	char post_headers[32] = "";
+
+	if ( req->post_data != NULL ) /* If there is data then add Content-Length header. */
+	{
+		os_sprintf( post_headers, "Content-Length: %d\r\n", strlen( req->post_data ) );
+	}
+
+	if(req->headers == NULL) /* Avoid NULL pointer, it may cause exception */
+	{
+		req->headers = (char *)os_malloc(sizeof(char));
+		req->headers[0] = '\0';
+	}
+
+	char buf[69 + strlen( req->method ) + strlen( req->path ) + strlen( req->hostname ) +
+		 strlen( req->headers ) + strlen( post_headers )];
+	int len = os_sprintf( buf,
+			      "%s %s HTTP/1.1\r\n"
+			      "Host: %s:%d\r\n"
+			      "Connection: close\r\n"
+			      "User-Agent: ESP8266\r\n"
+			      "%s"
+			      "%s"
+			      "\r\n",
+			      req->method, req->path, req->hostname, req->port, req->headers, post_headers );
+
+	if ( req->secure )
+		espconn_secure_send( conn, (uint8_t *) buf, len );
+	else
+		espconn_send( conn, (uint8_t *) buf, len );
+	if(req->headers != NULL)
+		os_free( req->headers );
+	req->headers = NULL;
+	HTTPCLIENT_DEBUG( "Sending request header\n" );
+}
+
+
+static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg )
+{
+	HTTPCLIENT_DEBUG( "Disconnected\n" );
+	struct espconn *conn = (struct espconn *) arg;
+
+	if ( conn == NULL )
+	{
+		return;
+	}
+
+	if ( conn->proto.tcp != NULL )
+	{
+		os_free( conn->proto.tcp );
+	}
+	if ( conn->reverse != NULL )
+	{
+		request_args_t	* req		= (request_args_t *) conn->reverse;
+		int		http_status	= -1;
+		char		* body		= "";
+
+		// Turn off timeout timer
+		os_timer_disarm( &(req->timeout_timer) );
+
+		if ( req->buffer == NULL )
+		{
+			HTTPCLIENT_DEBUG( "Buffer probably shouldn't be NULL\n" );
+		}
+		else if ( req->buffer[0] != '\0' )
+		{
+			/* FIXME: make sure this is not a partial response, using the Content-Length header. */
+			const char * version_1_0 = "HTTP/1.0 ";
+			const char * version_1_1 = "HTTP/1.1 ";
+			if (( os_strncmp( req->buffer, version_1_0, strlen( version_1_0 ) ) != 0 ) &&
+				( os_strncmp( req->buffer, version_1_1, strlen( version_1_1 ) ) != 0 ))
+			{
+				HTTPCLIENT_DEBUG( "Invalid version in %s\n", req->buffer );
+			}
+			else  
+			{
+				http_status	= atoi( req->buffer + strlen( version_1_0 ) );
+			        body            = (char *) os_strstr(req->buffer, "\r\n\r\n");
+
+				if (NULL == body) {
+					  /* Find missing body */
+					  HTTPCLIENT_DEBUG("Body shouldn't be NULL\n");
+					  /* To avoid NULL body */
+					  body = "";
+				} else {                                        
+					  /* Skip CR & LF */
+					  body = body + 4;
+				}
+								   
+				if ( os_strstr( req->buffer, "Transfer-Encoding: chunked" ) )
+				{
+					int	body_size = req->buffer_size - (body - req->buffer);
+					char	chunked_decode_buffer[body_size];
+					os_memset( chunked_decode_buffer, 0, body_size );
+					/* Chuncked data */
+					http_chunked_decode( body, chunked_decode_buffer );
+					os_memcpy( body, chunked_decode_buffer, body_size );
+				}
+			}
+		}
+		if ( req->callback_handle != NULL ) /* Callback is optional. */
+		{
+			req->callback_handle( body, http_status, req->buffer );
+		}
+		if (req->buffer) {
+		  os_free( req->buffer );
+		}
+		os_free( req->hostname );
+		os_free( req->method );
+		os_free( req->path );
+		os_free( req );
+	}
+	/* Fix memory leak. */
+	espconn_delete( conn );
+	os_free( conn );
+}
+
+
+static void ICACHE_FLASH_ATTR http_error_callback( void *arg, sint8 errType )
+{
+	HTTPCLIENT_DEBUG( "Disconnected with error\n" );
+	http_disconnect_callback( arg );
+}
+
+
+static void ICACHE_FLASH_ATTR http_timeout_callback( void *arg )
+{
+	HTTPCLIENT_DEBUG( "Connection timeout\n" );
+	struct espconn * conn = (struct espconn *) arg;
+	if ( conn == NULL )
+	{
+		return;
+	}
+	if ( conn->reverse == NULL )
+	{
+		return;
+	}
+	request_args_t * req = (request_args_t *) conn->reverse;
+	/* Call disconnect */
+	if ( req->secure )
+		espconn_secure_disconnect( conn );
+	else
+		espconn_disconnect( conn );
+}
+
+
+static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_t * addr, void * arg )
+{
+	request_args_t * req = (request_args_t *) arg;
+
+	if ( addr == NULL )
+	{
+		HTTPCLIENT_DEBUG( "DNS failed for %s\n", hostname );
+		if ( req->callback_handle != NULL )
+		{
+			req->callback_handle( "", -1, "" );
+		}
+		os_free( req );
+	}
+	else  
+	{
+		HTTPCLIENT_DEBUG( "DNS found %s " IPSTR "\n", hostname, IP2STR( addr ) );
+
+		struct espconn * conn = (struct espconn *) os_zalloc( sizeof(struct espconn) );
+		conn->type			= ESPCONN_TCP;
+		conn->state			= ESPCONN_NONE;
+		conn->proto.tcp			= (esp_tcp *) os_zalloc( sizeof(esp_tcp) );
+		conn->proto.tcp->local_port	= espconn_port();
+		conn->proto.tcp->remote_port	= req->port;
+		conn->reverse			= req;
+
+		os_memcpy( conn->proto.tcp->remote_ip, addr, 4 );
+
+		espconn_regist_connectcb( conn, http_connect_callback );
+		espconn_regist_disconcb( conn, http_disconnect_callback );
+		espconn_regist_reconcb( conn, http_error_callback );
+
+		/* Set connection timeout timer */
+		os_timer_disarm( &(req->timeout_timer) );
+		os_timer_setfn( &(req->timeout_timer), (os_timer_func_t *) http_timeout_callback, conn );
+		os_timer_arm( &(req->timeout_timer), req->timeout, false );
+
+		if ( req->secure )
+		{
+			espconn_secure_set_size( ESPCONN_CLIENT, 5120 ); /* set SSL buffer size */
+			espconn_secure_connect( conn );
+		} 
+		else 
+		{
+			espconn_connect( conn );
+		}
+	}
+}
+
+
+void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool secure, const char * method, const char * path, const char * headers, const char * post_data, http_callback_t callback_handle )
+{
+	HTTPCLIENT_DEBUG( "DNS request\n" );
+
+	request_args_t * req = (request_args_t *) os_zalloc( sizeof(request_args_t) );
+	req->hostname		= esp_strdup( hostname );
+	req->port		= port;
+	req->secure		= secure;
+	req->method		= esp_strdup( method );
+	req->path		= esp_strdup( path );
+	req->headers		= esp_strdup( headers );
+	req->post_data		= esp_strdup( post_data );
+	req->buffer_size	= 1;
+	req->buffer		= (char *) os_malloc( 1 );
+	req->buffer[0]		= '\0';                                         /* Empty string. */
+	req->callback_handle	= callback_handle;
+	req->timeout		= HTTP_REQUEST_TIMEOUT_MS;
+
+	ip_addr_t	addr;
+	err_t		error = espconn_gethostbyname( (struct espconn *) req,  /* It seems we don't need a real espconn pointer here. */
+						       hostname, &addr, http_dns_callback );
+
+	if ( error == ESPCONN_INPROGRESS )
+	{
+		HTTPCLIENT_DEBUG( "DNS pending\n" );
+	}
+	else if ( error == ESPCONN_OK )
+	{
+		/* Already in the local names table (or hostname was an IP address), execute the callback ourselves. */
+		http_dns_callback( hostname, &addr, req );
+	}
+	else  
+	{
+		if ( error == ESPCONN_ARG )
+		{
+			HTTPCLIENT_DEBUG( "DNS arg error %s\n", hostname );
+		}else  {
+			HTTPCLIENT_DEBUG( "DNS error code %d\n", error );
+		}
+		http_dns_callback( hostname, NULL, req ); /* Handle all DNS errors the same way. */
+	}
+}
+
+
+/*
+ * Parse an URL of the form http://host:port/path
+ * <host> can be a hostname or an IP address
+ * <port> is optional
+ */
+void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, const char * headers, const char * post_data, http_callback_t callback_handle )
+{
+	/*
+	 * FIXME: handle HTTP auth with http://user:pass@host/
+	 * FIXME: get rid of the #anchor part if present.
+	 */
+
+	char	hostname[128]	= "";
+	int	port		= 80;
+	bool	secure		= false;
+
+	bool	is_http		= os_strncmp( url, "http://", strlen( "http://" ) ) == 0;
+	bool	is_https	= os_strncmp( url, "https://", strlen( "https://" ) ) == 0;
+
+	if ( is_http )
+		url += strlen( "http://" );             /* Get rid of the protocol. */
+	else if ( is_https )
+	{
+		port	= 443;
+		secure	= true;
+		url	+= strlen( "https://" );        /* Get rid of the protocol. */
+	} 
+	else 
+	{
+		HTTPCLIENT_DEBUG( "URL is not HTTP or HTTPS %s\n", url );
+		return;
+	}
+
+	char * path = os_strchr( url, '/' );
+	if ( path == NULL )
+	{
+		path = os_strchr( url, '\0' );  /* Pointer to end of string. */
+	}
+
+	char * colon = os_strchr( url, ':' );
+	if ( colon > path )
+	{
+		colon = NULL;                   /* Limit the search to characters before the path. */
+	}
+
+	if (path - url >= sizeof(hostname)) {
+		HTTPCLIENT_DEBUG( "hostname is too long %s\n", url );
+		return;
+	}
+
+	if ( colon == NULL )                    /* The port is not present. */
+	{
+		os_memcpy( hostname, url, path - url );
+		hostname[path - url] = '\0';
+	}
+	else  
+	{
+		port = atoi( colon + 1 );
+		if ( port == 0 )
+		{
+			HTTPCLIENT_DEBUG( "Port error %s\n", url );
+			return;
+		}
+
+		os_memcpy( hostname, url, colon - url );
+		hostname[colon - url] = '\0';
+	}
+
+
+	if ( path[0] == '\0' ) /* Empty path is not allowed. */
+	{
+		path = "/";
+	}
+
+	HTTPCLIENT_DEBUG( "hostname=%s\n", hostname );
+	HTTPCLIENT_DEBUG( "port=%d\n", port );
+	HTTPCLIENT_DEBUG( "method=%s\n", method );
+	HTTPCLIENT_DEBUG( "path=%s\n", path );
+	http_raw_request( hostname, port, secure, method, path, headers, post_data, callback_handle );
+}
+
+
+/*
+ * Parse an URL of the form http://host:port/path
+ * <host> can be a hostname or an IP address
+ * <port> is optional
+ */
+void ICACHE_FLASH_ATTR http_post( const char * url, const char * headers, const char * post_data, http_callback_t callback_handle )
+{
+	http_request( url, "POST", headers, post_data, callback_handle );
+}
+
+
+void ICACHE_FLASH_ATTR http_get( const char * url, const char * headers, http_callback_t callback_handle )
+{
+	http_request( url, "GET", headers, NULL, callback_handle );
+}
+
+
+void ICACHE_FLASH_ATTR http_delete( const char * url, const char * headers, const char * post_data, http_callback_t callback_handle )
+{
+	http_request( url, "DELETE", headers, post_data, callback_handle );
+}
+
+
+void ICACHE_FLASH_ATTR http_put( const char * url, const char * headers, const char * post_data, http_callback_t callback_handle )
+{
+	http_request( url, "PUT", headers, post_data, callback_handle );
+}
+
+
+void ICACHE_FLASH_ATTR http_callback_example( char * response, int http_status, char * full_response )
+{
+	os_printf( "http_status=%d\n", http_status );
+	if ( http_status != HTTP_STATUS_GENERIC_ERROR )
+	{
+		os_printf( "strlen(full_response)=%d\n", strlen( full_response ) );
+		os_printf( "response=%s<EOF>\n", response );
+	}
+}

+ 97 - 0
app/http/httpclient.h

@@ -0,0 +1,97 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Martin d'Allens <martin.dallens@gmail.com> wrote this file. As long as you retain
+ * this notice you can do whatever you want with this stuff. If we meet some day,
+ * and you think this stuff is worth it, you can buy me a beer in return.
+ * ----------------------------------------------------------------------------
+ */
+
+#ifndef __HTTPCLIENT_H__
+#define __HTTPCLIENT_H__
+
+#if defined(GLOBAL_DEBUG_ON)
+#define HTTPCLIENT_DEBUG_ON
+#endif
+#if defined(HTTPCLIENT_DEBUG_ON)
+#define HTTPCLIENT_DEBUG(format, ...) os_printf(format, ##__VA_ARGS__)
+#else
+#define HTTPCLIENT_DEBUG(format, ...)
+#endif
+
+#if defined(USES_SDK_BEFORE_V140)
+#define espconn_send espconn_sent
+#define espconn_secure_send espconn_secure_sent
+#endif
+
+/*
+ * In case of TCP or DNS error the callback is called with this status.
+ */
+#define HTTP_STATUS_GENERIC_ERROR  (-1)
+
+/*
+ * Size of http responses that will cause an error.
+ */
+#define BUFFER_SIZE_MAX            (0x2000)
+
+/*
+ * Timeout of http request.
+ */
+#define HTTP_REQUEST_TIMEOUT_MS    (10000)
+
+/*
+ * "full_response" is a string containing all response headers and the response body.
+ * "response_body and "http_status" are extracted from "full_response" for convenience.
+ *
+ * A successful request corresponds to an HTTP status code of 200 (OK).
+ * More info at http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
+ */
+typedef void (* http_callback_t)(char * response_body, int http_status, char * full_response);
+
+/*
+ * Call this function to skip URL parsing if the arguments are already in separate variables.
+ */
+void ICACHE_FLASH_ATTR http_raw_request(const char * hostname, int port, bool secure, const char * method, const char * path, const char * headers, const char * post_data, http_callback_t callback_handle);
+
+/*
+ * Request data from URL use custom method.
+ * The data should be encoded as any format.
+ * Try:
+ * http_request("http://httpbin.org/post", "OPTIONS", "Content-type: text/plain", "Hello world", http_callback_example);
+ */
+void ICACHE_FLASH_ATTR http_request(const char * url, const char * method, const char * headers, const char * post_data, http_callback_t callback_handle);
+
+/*
+ * Post data to a web form.
+ * The data should be encoded as any format.
+ * Try:
+ * http_post("http://httpbin.org/post", "Content-type: application/json", "{\"hello\": \"world\"}", http_callback_example);
+ */
+void ICACHE_FLASH_ATTR http_post(const char * url, const char * headers, const char * post_data, http_callback_t callback_handle);
+
+/*
+ * Download a web page from its URL.
+ * Try:
+ * http_get("http://wtfismyip.com/text", NULL, http_callback_example);
+ */
+void ICACHE_FLASH_ATTR http_get(const char * url, const char * headers, http_callback_t callback_handle);
+/*
+ * Delete a web page from its URL.
+ * Try:
+ * http_delete("http://wtfismyip.com/text", NULL, http_callback_example);
+ */
+void ICACHE_FLASH_ATTR http_delete(const char * url, const char * headers, const char * post_data, http_callback_t callback_handle);
+/*
+ * Update data to a web form.
+ * The data should be encoded as any format.
+ * Try:
+ * http_put("http://httpbin.org/post", "Content-type: application/json", "{\"hello\": \"world\"}", http_callback_example);
+ */
+void ICACHE_FLASH_ATTR http_put(const char * url, const char * headers, const char * post_data, http_callback_t callback_handle);
+
+/*
+ * Output on the UART.
+ */
+void http_callback_example(char * response, int http_status, char * full_response);
+
+#endif // __HTTPCLIENT_H__

+ 3 - 0
app/include/arch/cc.h

@@ -38,7 +38,10 @@
 #include "c_types.h"
 #include "ets_sys.h"
 #include "osapi.h"
+
+#ifndef EFAULT
 #define EFAULT 14
+#endif
 
 //#define LWIP_PROVIDE_ERRNO
 

+ 1 - 1
app/include/driver/pwm.h

@@ -33,7 +33,7 @@ struct pwm_param {
 // #define PWM_2_OUT_IO_FUNC  FUNC_GPIO13
 
 void pwm_init(uint16 freq, uint16 *duty);
-void pwm_start(void);
+bool pwm_start(void);
 
 void pwm_set_duty(uint16 duty, uint8 channel);
 uint16 pwm_get_duty(uint8 channel);

+ 6 - 0
app/include/driver/readline.h

@@ -0,0 +1,6 @@
+#ifndef READLINE_APP_H
+#define READLINE_APP_H
+
+bool uart_getc(char *c);
+
+#endif /* READLINE_APP_H */

+ 26 - 0
app/include/driver/rotary.h

@@ -0,0 +1,26 @@
+/*
+ * Definitions to access the Rotary driver
+ */
+#ifndef __ROTARY_H__
+#define __ROTARY_H__
+
+#include "c_types.h"
+
+#define ROTARY_CHANNEL_COUNT	3
+
+typedef struct {
+  uint32_t pos;
+  uint32_t time_us;
+} rotary_event_t;
+
+int rotary_setup(uint32_t channel, int phaseA, int phaseB, int press, task_handle_t tasknumber);
+
+bool rotary_getevent(uint32_t channel, rotary_event_t *result);
+
+bool rotary_has_queued_event(uint32_t channel);
+
+int rotary_getpos(uint32_t channel);
+
+int rotary_close(uint32_t channel);
+
+#endif

+ 11 - 0
app/include/driver/sigma_delta.h

@@ -0,0 +1,11 @@
+#ifndef SIGMA_DELTA_APP_H
+#define SIGMA_DELTA_APP_H
+
+#include "eagle_soc.h"
+#include "os_type.h"
+
+void sigma_delta_setup( void );
+void sigma_delta_stop( void );
+void sigma_delta_set_prescale_target( sint16 prescale, sint16 target );
+
+#endif

+ 3 - 3
app/include/driver/spi.h

@@ -2,7 +2,7 @@
 #define SPI_APP_H
 
 #include "spi_register.h"
-#include "ets_sys.h"
+#include "rom.h"
 #include "osapi.h"
 #include "uart.h"
 #include "os_type.h"
@@ -20,9 +20,9 @@ void spi_lcd_9bit_write(uint8 spi_no,uint8 high_bit,uint8 low_8bit);
 //spi master init funtion
 void spi_master_init(uint8 spi_no, unsigned cpol, unsigned cpha, uint32_t clock_div);
 // fill MOSI buffer
-void spi_set_mosi(uint8 spi_no, uint8 offset, uint8 bitlen, uint32 data);
+void spi_mast_set_mosi(uint8 spi_no, uint16 offset, uint8 bitlen, uint32 data);
 // retrieve data from MISO buffer
-uint32 spi_get_miso(uint8 spi_no, uint8 offset, uint8 bitlen);
+uint32 spi_mast_get_miso(uint8 spi_no, uint16 offset, uint8 bitlen);
 // initiate SPI transaction
 void spi_mast_transaction(uint8 spi_no, uint8 cmd_bitlen, uint16 cmd_data, uint8 addr_bitlen, uint32 addr_data,
                           uint16 mosi_bitlen, uint8 dummy_bitlen, sint16 miso_bitlen);

+ 0 - 13
app/include/driver/spi_master.h

@@ -1,13 +0,0 @@
-#ifndef __SPI_MASTER_H__
-#define __SPI_MASTER_H__
-
-#include "driver/spi_register.h"
-
-/*SPI number define*/
-#define SPI         0
-#define HSPI        1
-
-void spi_master_init(uint8 spi_no);
-void spi_master_9bit_write(uint8 spi_no, uint8 high_bit, uint8 low_8bit);
-
-#endif

+ 1 - 0
app/include/driver/spi_register.h

@@ -102,6 +102,7 @@
 #define SPI_CS2_DIS (BIT(2))
 #define SPI_CS1_DIS (BIT(1))
 #define SPI_CS0_DIS (BIT(0))
+#define SPI_IDLE_EDGE (BIT(29))
 
 #define SPI_SLAVE(i)                          (REG_SPI_BASE(i)  + 0x30)
 #define SPI_SYNC_RESET (BIT(31))

+ 1 - 1
app/include/driver/uart.h

@@ -101,7 +101,7 @@ typedef struct {
     int                      buff_uart_no;  //indicate which uart use tx/rx buffer
 } UartDevice;
 
-void uart_init(UartBautRate uart0_br, UartBautRate uart1_br, uint8 task_prio, os_signal_t sig_input);
+void uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input);
 void uart0_alt(uint8 on);
 void uart0_sendStr(const char *str);
 void uart0_putc(const char c);

+ 1 - 1
app/include/lwip/app/espconn.h

@@ -169,7 +169,7 @@ typedef struct _espconn_msg{
 	struct espconn *pespconn;
 	comon_pkt pcommon;
 	uint8 count_opt;
-	int16_t hs_status;	//the status of the handshake
+	sint16_t hs_status;	//the status of the handshake
 	void *preverse;
 	void *pssl;
 	struct _espconn_msg *pnext;

+ 1 - 1
app/include/lwip/app/espconn_tcp.h

@@ -29,7 +29,7 @@ extern void espconn_kill_oldest_pcb(void);
  * Returns      : none
 *******************************************************************************/
 
-extern void espconn_tcp_disconnect(espconn_msg *pdiscon);
+extern void espconn_tcp_disconnect(espconn_msg *pdiscon,u8 type);
 
 /******************************************************************************
  * FunctionName : espconn_tcp_client

+ 0 - 1
app/include/lwipopts.h

@@ -789,7 +789,6 @@
 #ifndef LWIP_MDNS
 #define LWIP_MDNS                      1
 #endif
-/*
 /*
    ----------------------------------
    ---------- DNS options -----------

+ 1 - 1
app/include/module.h

@@ -67,7 +67,7 @@
   const LOCK_IN_SECTION(".lua_rotable") \
     luaR_table MODULE_PASTE_(lua_rotable_,name) = { luaname, map }
 
-#if !(MIN_OPT_LEVEL==2 && LUA_OPTIMIZE_MEMORY==2)
+#if !defined(LUA_CROSS_COMPILER) && !(MIN_OPT_LEVEL==2 && LUA_OPTIMIZE_MEMORY==2)
 # error "NodeMCU modules must be built with LTR enabled (MIN_OPT_LEVEL=2 and LUA_OPTIMIZE_MEMORY=2)"
 #endif
 

+ 16 - 0
app/include/nodemcu_mdns.h

@@ -0,0 +1,16 @@
+#ifndef _NODEMCU_MDNS_H
+#define _NODEMCU_MDNS_H
+
+struct nodemcu_mdns_info {
+	const char *host_name;
+	const char *host_desc;
+	const char *service_name;
+	uint16 service_port;
+	const char *txt_data[10];
+};
+
+void nodemcu_mdns_close(void);
+bool nodemcu_mdns_init(struct nodemcu_mdns_info *);
+
+
+#endif

+ 43 - 1
app/include/rom.h

@@ -4,6 +4,7 @@
 #define _ROM_H_
 
 #include "c_types.h"
+#include "ets_sys.h"
 
 // SHA1 is assumed to match the netbsd sha1.h headers
 #define SHA1_DIGEST_LENGTH		20
@@ -44,7 +45,7 @@ extern unsigned char * base64_decode(const unsigned char *src, size_t len, size_
 extern void mem_init(void * start_addr);
 
 // Interrupt Service Routine functions
-typedef void (*ets_isr_fn) (void *arg, uint32_t sp);
+typedef void (*ets_isr_fn) (void *arg);
 extern int ets_isr_attach (unsigned int interrupt, ets_isr_fn, void *arg);
 extern void ets_isr_mask (unsigned intr);
 extern void ets_isr_unmask (unsigned intr);
@@ -108,4 +109,45 @@ typedef void (*exception_handler_fn) (struct exception_frame *ef, uint32_t cause
  */
 exception_handler_fn _xtos_set_exception_handler (uint32_t cause, exception_handler_fn handler);
 
+
+void ets_update_cpu_frequency (uint32_t mhz);
+uint32_t ets_get_cpu_frequency (void);
+
+void *ets_memcpy (void *dst, const void *src, size_t n);
+void *ets_memmove (void *dst, const void *src, size_t n);
+void *ets_memset (void *dst, int c, size_t n);
+int ets_memcmp (const void *s1, const void *s2, size_t n);
+
+char *ets_strcpy (char *dst, const char *src);
+size_t ets_strlen (const char *s);
+int ets_strcmp (const char *s1, const char *s2);
+int ets_strncmp (const char *s1, const char *s2, size_t n);
+char *ets_strncpy(char *dest, const char *src, size_t n);
+char *ets_strstr(const char *haystack, const char *needle);
+
+void ets_delay_us (uint32_t us);
+
+int ets_printf(const char *format, ...)  __attribute__ ((format (printf, 1, 2)));
+
+void ets_str2macaddr (uint8_t *dst, const char *str);
+
+void ets_timer_disarm (ETSTimer *a);
+void ets_timer_setfn (ETSTimer *t, ETSTimerFunc *fn, void *parg);
+
+void Cache_Read_Enable(uint32_t b0, uint32_t b1, uint32_t use_40108000);
+void Cache_Read_Disable(void);
+
+void ets_intr_lock(void);
+void ets_intr_unlock(void);
+
+void ets_install_putc1(void *routine);
+
+int rand(void);
+void srand(unsigned int);
+
+void uart_div_modify(int no, unsigned int freq);
+
+/* Returns 0 on success, 1 on failure */
+uint8_t SPIRead(uint32_t src_addr, uint32_t *des_addr, uint32_t size);
+
 #endif

+ 1 - 0
app/include/rtc/rtctime_internal.h

@@ -126,6 +126,7 @@
 #include <ets_sys.h>
 #include "rom.h"
 #include "rtcaccess.h"
+#include "user_interface.h"
 
 // Layout of the RTC storage space:
 //

+ 34 - 0
app/include/task/task.h

@@ -0,0 +1,34 @@
+#ifndef _TASK_H_
+#define _TASK_H_
+
+#include "ets_sys.h"
+#include "osapi.h"
+#include "os_type.h"
+
+/* use LOW / MEDIUM / HIGH since it isn't clear from the docs which is higher */
+
+#define TASK_PRIORITY_LOW     0
+#define TASK_PRIORITY_MEDIUM  1
+#define TASK_PRIORITY_HIGH    2
+#define TASK_PRIORITY_COUNT   3
+
+/*
+* Signals are a 32-bit number of the form header:14; count:16, priority:2.  The header
+* is just a fixed fingerprint and the count is allocated serially by the task get_id()
+* function.
+*/
+#define task_post(priority,handle,param) system_os_post(priority, ((handle) | priority), param)
+#define task_post_low(handle,param)    task_post(TASK_PRIORITY_LOW,    handle, param)
+#define task_post_medium(handle,param) task_post(TASK_PRIORITY_MEDIUM, handle, param)
+#define task_post_high(handle,param)   task_post(TASK_PRIORITY_HIGH,   handle, param)
+
+#define task_handle_t os_signal_t
+#define task_param_t os_param_t
+
+typedef void (*task_callback_t)(task_param_t param, uint8 prio);
+
+bool task_init_handler(uint8 priority, uint8 qlen);
+task_handle_t task_get_id(task_callback_t t);
+
+#endif
+

+ 2 - 2
app/include/ucg_config.h

@@ -30,10 +30,10 @@
 //    UCG_DISPLAY_TABLE_ENTRY(ili9163_18x128x128_hw_spi, ucg_dev_ili9163_18x128x128, ucg_ext_ili9163_18) \
 //    UCG_DISPLAY_TABLE_ENTRY(ili9341_18x240x320_hw_spi, ucg_dev_ili9341_18x240x320, ucg_ext_ili9341_18) \
 //    UCG_DISPLAY_TABLE_ENTRY(pcf8833_16x132x132_hw_spi, ucg_dev_pcf8833_16x132x132, ucg_ext_pcf8833_16) \
-//    UCG_DISPLAY_TABLE_ENTRY(seps225_16x128x128_univision_hw_spi, ucg_dev_seps225_16x128x128_univision, ucg_ext_seps225_16) \
+//    UCG_DISPLAY_TABLE_ENTRY(seps225_16x128x128_uvis_hw_spi, ucg_dev_seps225_16x128x128_univision, ucg_ext_seps225_16) \
 //    UCG_DISPLAY_TABLE_ENTRY(ssd1351_18x128x128_hw_spi, ucg_dev_ssd1351_18x128x128_ilsoft, ucg_ext_ssd1351_18) \
 //    UCG_DISPLAY_TABLE_ENTRY(ssd1351_18x128x128_ft_hw_spi, ucg_dev_ssd1351_18x128x128_ft, ucg_ext_ssd1351_18) \
-//    UCG_DISPLAY_TABLE_ENTRY(ssd1331_18x96x64_univision_hw_spi, ucg_dev_ssd1331_18x96x64_univision, ucg_ext_ssd1331_18) \
+//    UCG_DISPLAY_TABLE_ENTRY(ssd1331_18x96x64_uvis_hw_spi, ucg_dev_ssd1331_18x96x64_univision, ucg_ext_ssd1331_18) \
 //    UCG_DISPLAY_TABLE_ENTRY(st7735_18x128x160_hw_spi, ucg_dev_st7735_18x128x160, ucg_ext_st7735_18) \
 
 #define UCG_DISPLAY_TABLE_ENTRY(binding, device, extension)

+ 42 - 18
app/include/user_config.h

@@ -12,20 +12,28 @@
 #define FLASH_AUTOSIZE
 #define FLASH_SAFE_API
 
-// Byte 107 of esp_init_data_default, only one of these 3 can be picked
-#define ESP_INIT_DATA_ENABLE_READVDD33
-//#define ESP_INIT_DATA_ENABLE_READADC
-//#define ESP_INIT_DATA_FIXED_VDD33_VALUE 33
+// This adds the asserts in LUA. It also adds some useful extras to the
+// node module. This is all silent in normal operation and so can be enabled
+// without any harm (except for the code size increase and slight slowdown)
+//#define DEVELOPMENT_TOOLS
+
+#ifdef DEVELOPMENT_TOOLS
+extern void luaL_assertfail(const char *file, int line, const char *message);
+#define lua_assert(x)    ((x) ? (void) 0 : luaL_assertfail(__FILE__, __LINE__, #x))
+#endif
 
+// This enables lots of debug output and changes the serial bit rate. This
+// is normally only used by hardcore developers
 // #define DEVELOP_VERSION
 #ifdef DEVELOP_VERSION
 #define NODE_DEBUG
 #define COAP_DEBUG
-#define BIT_RATE_DEFAULT BIT_RATE_74880
-#else
-#define BIT_RATE_DEFAULT BIT_RATE_9600
 #endif /* DEVELOP_VERSION */
 
+#define BIT_RATE_DEFAULT BIT_RATE_115200
+
+// This enables automatic baud rate detection at startup
+#define BIT_RATE_AUTOBAUD
 
 #define NODE_ERROR
 
@@ -41,29 +49,28 @@
 #define NODE_ERR
 #endif	/* NODE_ERROR */
 
+#define GPIO_INTERRUPT_ENABLE
+#define GPIO_INTERRUPT_HOOK_ENABLE
+// #define GPIO_SAFE_NO_INTR_ENABLE
+
 #define ICACHE_STORE_TYPEDEF_ATTR __attribute__((aligned(4),packed))
 #define ICACHE_STORE_ATTR __attribute__((aligned(4)))
 #define ICACHE_RAM_ATTR __attribute__((section(".iram0.text")))
+#ifdef  GPIO_SAFE_NO_INTR_ENABLE
+#define NO_INTR_CODE ICACHE_RAM_ATTR __attribute__ ((noinline))
+#else
+#define NO_INTR_CODE inline
+#endif
 
-#define CLIENT_SSL_ENABLE
-#define GPIO_INTERRUPT_ENABLE
+//#define CLIENT_SSL_ENABLE
 //#define MD2_ENABLE
 #define SHA2_ENABLE
 
-// #define BUILD_WOFS		1
 #define BUILD_SPIFFS	1
-
 #define SPIFFS_CACHE 1
 
 // #define LUA_NUMBER_INTEGRAL
 
-#define LUA_OPTRAM
-#ifdef LUA_OPTRAM
-#define LUA_OPTIMIZE_MEMORY			2
-#else
-#define LUA_OPTIMIZE_MEMORY         0
-#endif	/* LUA_OPTRAM */
-
 #define READLINE_INTERVAL 80
 #define LUA_TASK_PRIO USER_TASK_PRIO_0
 #define LUA_PROCESS_LINE_SIG 2
@@ -83,6 +90,23 @@
 
 #define ENDUSER_SETUP_AP_SSID "SetupGadget"
 
+/*
+ * A valid hostname only contains alphanumeric and hyphen(-) characters, with no hyphens at first or last char
+ * if WIFI_STA_HOSTNAME not defined: hostname will default to NODE-xxxxxx (xxxxxx being last 3 octets of MAC address)
+ * if WIFI_STA_HOSTNAME defined: hostname must only contain alphanumeric characters
+ * if WIFI_STA_HOSTNAME_APPEND_MAC not defined: Hostname MUST be 32 chars or less
+ * if WIFI_STA_HOSTNAME_APPEND_MAC defined: Hostname MUST be 26 chars or less, since last 3 octets of MAC address will be appended
+ * if defined hostname is invalid: hostname will default to NODE-xxxxxx (xxxxxx being last 3 octets of MAC address)
+*/
+//#define WIFI_STA_HOSTNAME "NodeMCU"
+//#define WIFI_STA_HOSTNAME_APPEND_MAC
+
+//#define WIFI_SMART_ENABLE
+
+#define WIFI_STATION_STATUS_MONITOR_ENABLE
+#define WIFI_SDK_EVENT_MONITOR_ENABLE
+#define WIFI_EVENT_MONITOR_DISCONNECT_REASON_LIST_ENABLE
+
 #define STRBUF_DEFAULT_INCREMENT 32
 
 #endif	/* __USER_CONFIG_H__ */

+ 26 - 12
app/include/user_modules.h

@@ -13,37 +13,51 @@
 
 #ifndef LUA_CROSS_COMPILER
 
+// The default configuration is designed to run on all ESP modules including the 512 KB modules like ESP-01 and only
+// includes general purpose interface modules which require at most two GPIO pins.
+// See https://github.com/nodemcu/nodemcu-firmware/pull/1127 for discussions.
+// New modules should be disabled by default and added in alphabetical order.
 #define LUA_USE_MODULES_ADC
+//#define LUA_USE_MODULES_AM2320
+//#define LUA_USE_MODULES_APA102
 #define LUA_USE_MODULES_BIT
 //#define LUA_USE_MODULES_BMP085
-#define LUA_USE_MODULES_CJSON
-#define LUA_USE_MODULES_COAP
-#define LUA_USE_MODULES_CRYPTO
+//#define LUA_USE_MODULES_BME280
+//#define LUA_USE_MODULES_CJSON
+//#define LUA_USE_MODULES_COAP
+//#define LUA_USE_MODULES_CRYPTO
 #define LUA_USE_MODULES_DHT
+//#define LUA_USE_MODULES_ENCODER
 //#define LUA_USE_MODULES_ENDUSER_SETUP // USE_DNS in dhcpserver.h needs to be enabled for this module to work.
 #define LUA_USE_MODULES_FILE
 #define LUA_USE_MODULES_GPIO
+//#define LUA_USE_MODULES_HTTP
 //#define LUA_USE_MODULES_HX711
 #define LUA_USE_MODULES_I2C
+//#define LUA_USE_MODULES_MDNS
 #define LUA_USE_MODULES_MQTT
 #define LUA_USE_MODULES_NET
 #define LUA_USE_MODULES_NODE
 #define LUA_USE_MODULES_OW
-#define LUA_USE_MODULES_PWM
-#define LUA_USE_MODULES_RC
-#define LUA_USE_MODULES_RTCFIFO
-#define LUA_USE_MODULES_RTCMEM
-#define LUA_USE_MODULES_RTCTIME
-#define LUA_USE_MODULES_SNTP
+//#define LUA_USE_MODULES_PERF
+//#define LUA_USE_MODULES_PWM
+//#define LUA_USE_MODULES_RC
+//#define LUA_USE_MODULES_ROTARY
+//#define LUA_USE_MODULES_RTCFIFO
+//#define LUA_USE_MODULES_RTCMEM
+//#define LUA_USE_MODULES_RTCTIME
+//#define LUA_USE_MODULES_SIGMA_DELTA
+//#define LUA_USE_MODULES_SNTP
 #define LUA_USE_MODULES_SPI
+//#define LUA_USE_MODULES_STRUCT
 #define LUA_USE_MODULES_TMR
-#define LUA_USE_MODULES_TSL2561
-#define LUA_USE_MODULES_U8G
+//#define LUA_USE_MODULES_TSL2561
+//#define LUA_USE_MODULES_U8G
 #define LUA_USE_MODULES_UART
 //#define LUA_USE_MODULES_UCG
 #define LUA_USE_MODULES_WIFI
 //#define LUA_USE_MODULES_WS2801
-#define LUA_USE_MODULES_WS2812
+//#define LUA_USE_MODULES_WS2812
 
 
 #endif  /* LUA_CROSS_COMPILER */

+ 4 - 4
app/include/user_version.h

@@ -2,13 +2,13 @@
 #define __USER_VERSION_H__
 
 #define NODE_VERSION_MAJOR		1U
-#define NODE_VERSION_MINOR		4U
-#define NODE_VERSION_REVISION	0U
+#define NODE_VERSION_MINOR		5U
+#define NODE_VERSION_REVISION	1U
 #define NODE_VERSION_INTERNAL   0U
 
-#define NODE_VERSION	"NodeMCU 1.4.0"
+#define NODE_VERSION	"NodeMCU 1.5.1"
 #ifndef BUILD_DATE
-#define BUILD_DATE	  "20151006"
+#define BUILD_DATE	  "unspecified"
 #endif
 
 extern char SDK_VERSION[];

+ 0 - 281
app/json/jsonparse.c

@@ -1,281 +0,0 @@
-/*
- * Copyright (c) 2011-2012, Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the Institute nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * This file is part of the Contiki operating system.
- */
-
-#ifdef JSON_FORMAT
-
-#include "json/jsonparse.h"
-#include "osapi.h"
-//#include <stdlib.h>
-//#include <string.h>
-
-/*--------------------------------------------------------------------*/
-static int ICACHE_FLASH_ATTR
-push(struct jsonparse_state *state, char c)
-{
-  state->stack[state->depth] = c;
-  state->depth++;
-  state->vtype = 0;
-  return state->depth < JSONPARSE_MAX_DEPTH;
-}
-/*--------------------------------------------------------------------*/
-static char ICACHE_FLASH_ATTR
-pop(struct jsonparse_state *state)
-{
-  if(state->depth == 0) {
-    return JSON_TYPE_ERROR;
-  }
-  state->depth--;
-  return state->stack[state->depth];
-}
-/*--------------------------------------------------------------------*/
-/* will pass by the value and store the start and length of the value for
-   atomic types */
-/*--------------------------------------------------------------------*/
-static void ICACHE_FLASH_ATTR
-atomic(struct jsonparse_state *state, char type)
-{
-  char c;
-
-  state->vstart = state->pos;
-  state->vtype = type;
-  if(type == JSON_TYPE_STRING || type == JSON_TYPE_PAIR_NAME) {
-    while((c = state->json[state->pos++]) && c != '"') {
-      if(c == '\\') {
-        state->pos++;           /* skip current char */
-      }
-    }
-    state->vlen = state->pos - state->vstart - 1;
-  } else if(type == JSON_TYPE_NUMBER) {
-    do {
-      c = state->json[state->pos];
-      if((c < '0' || c > '9') && c != '.') {
-        c = 0;
-      } else {
-        state->pos++;
-      }
-    } while(c);
-    /* need to back one step since first char is already gone */
-    state->vstart--;
-    state->vlen = state->pos - state->vstart;
-  }
-  /* no other types for now... */
-}
-/*--------------------------------------------------------------------*/
-static void ICACHE_FLASH_ATTR
-skip_ws(struct jsonparse_state *state)
-{
-  char c;
-
-  while(state->pos < state->len &&
-        ((c = state->json[state->pos]) == ' ' || c == '\n')) {
-    state->pos++;
-  }
-}
-/*--------------------------------------------------------------------*/
-void ICACHE_FLASH_ATTR
-jsonparse_setup(struct jsonparse_state *state, const char *json, int len)
-{
-  state->json = json;
-  state->len = len;
-  state->pos = 0;
-  state->depth = 0;
-  state->error = 0;
-  state->stack[0] = 0;
-}
-/*--------------------------------------------------------------------*/
-int ICACHE_FLASH_ATTR
-jsonparse_next(struct jsonparse_state *state)
-{
-  char c;
-  char s;
-
-  skip_ws(state);
-  c = state->json[state->pos];
-  s = jsonparse_get_type(state);
-  state->pos++;
-
-  switch(c) {
-  case '{':
-    push(state, c);
-    return c;
-  case '}':
-    if(s == ':' && state->vtype != 0) {
-/*       printf("Popping vtype: '%c'\n", state->vtype); */
-      pop(state);
-      s = jsonparse_get_type(state);
-    }
-    if(s == '{') {
-      pop(state);
-    } else {
-      state->error = JSON_ERROR_SYNTAX;
-      return JSON_TYPE_ERROR;
-    }
-    return c;
-  case ']':
-    if(s == '[') {
-      pop(state);
-    } else {
-      state->error = JSON_ERROR_UNEXPECTED_END_OF_ARRAY;
-      return JSON_TYPE_ERROR;
-    }
-    return c;
-  case ':':
-    push(state, c);
-    return c;
-  case ',':
-    /* if x:y ... , */
-    if(s == ':' && state->vtype != 0) {
-      pop(state);
-    } else if(s == '[') {
-      /* ok! */
-    } else {
-      state->error = JSON_ERROR_SYNTAX;
-      return JSON_TYPE_ERROR;
-    }
-    return c;
-  case '"':
-    if(s == '{' || s == '[' || s == ':') {
-      atomic(state, c = (s == '{' ? JSON_TYPE_PAIR_NAME : c));
-    } else {
-      state->error = JSON_ERROR_UNEXPECTED_STRING;
-      return JSON_TYPE_ERROR;
-    }
-    return c;
-  case '[':
-    if(s == '{' || s == '[' || s == ':') {
-      push(state, c);
-    } else {
-      state->error = JSON_ERROR_UNEXPECTED_ARRAY;
-      return JSON_TYPE_ERROR;
-    }
-    return c;
-  default:
-    if(s == ':' || s == '[') {
-      if(c <= '9' && c >= '0') {
-        atomic(state, JSON_TYPE_NUMBER);
-        return JSON_TYPE_NUMBER;
-      }
-    }
-  }
-  return 0;
-}
-/*--------------------------------------------------------------------*/
-/* get the json value of the current position
- * works only on "atomic" values such as string, number, null, false, true
- */
-/*--------------------------------------------------------------------*/
-int ICACHE_FLASH_ATTR
-jsonparse_copy_value(struct jsonparse_state *state, char *str, int size)
-{
-  int i;
-  char z = 0;
-  char y = 0;
-
-  if(state->vtype == 0) {
-    return 0;
-  }
-  size = size <= state->vlen ? (size - 1) : state->vlen;
-  for(i = 0; i < size; i++) {
-    if (y == 0 && state->json[state->vstart + i] == '\\') {
-        y = 1;
-        z++;
-        continue;
-    }
-    y = 0;
-    str[i - z] = state->json[state->vstart + i];
-  }
-  str[i - z] = 0;
-  return state->vtype;
-}
-/*--------------------------------------------------------------------*/
-int ICACHE_FLASH_ATTR
-jsonparse_get_value_as_int(struct jsonparse_state *state)
-{
-  if(state->vtype != JSON_TYPE_NUMBER) {
-    return 0;
-  }
-  return atoi(&state->json[state->vstart]);
-}
-/*--------------------------------------------------------------------*/
-long ICACHE_FLASH_ATTR
-jsonparse_get_value_as_long(struct jsonparse_state *state)
-{
-  if(state->vtype != JSON_TYPE_NUMBER) {
-    return 0;
-  }
-  return atol(&state->json[state->vstart]);
-}
-
-/*--------------------------------------------------------------------*/
-unsigned long ICACHE_FLASH_ATTR
-jsonparse_get_value_as_ulong(struct jsonparse_state *state)
-{
-  if(state->vtype != JSON_TYPE_NUMBER) {
-    return 0;
-  }
-  return strtoul(&state->json[state->vstart], '\0', 0);
-}
-
-/*--------------------------------------------------------------------*/
-/* strcmp - assume no strange chars that needs to be stuffed in string... */
-/*--------------------------------------------------------------------*/
-int ICACHE_FLASH_ATTR
-jsonparse_strcmp_value(struct jsonparse_state *state, const char *str)
-{
-  if(state->vtype == 0) {
-    return -1;
-  }
-  return os_strncmp(str, &state->json[state->vstart], state->vlen);
-}
-/*--------------------------------------------------------------------*/
-int ICACHE_FLASH_ATTR
-jsonparse_get_len(struct jsonparse_state *state)
-{
-  return state->vlen;
-}
-/*--------------------------------------------------------------------*/
-int ICACHE_FLASH_ATTR
-jsonparse_get_type(struct jsonparse_state *state)
-{
-  if(state->depth == 0) {
-    return 0;
-  }
-  return state->stack[state->depth - 1];
-}
-/*--------------------------------------------------------------------*/
-int ICACHE_FLASH_ATTR
-jsonparse_has_next(struct jsonparse_state *state)
-{
-  return state->pos < state->len;
-}
-/*--------------------------------------------------------------------*/
-#endif
-

+ 0 - 296
app/json/jsontree.c

@@ -1,296 +0,0 @@
-/*
- * Copyright (c) 2011-2012, Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the Institute nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * This file is part of the Contiki operating system.
- */
-
-/**
- * \file
- *         JSON output generation
- * \author
- *         Niclas Finne <nfi@sics.se>
- *         Joakim Eriksson <joakime@sics.se>
- */
-#ifdef JSON_FORMAT
-//#include "contiki.h"
-#include "json/jsontree.h"
-#include "json/jsonparse.h"
-#include "osapi.h"
-//#include <string.h>
-
-#define DEBUG 0
-#if DEBUG
-//#include <stdio.h>
-#define PRINTF(...) os_printf(__VA_ARGS__)
-#else
-#define PRINTF(...)
-#endif
-
-/*---------------------------------------------------------------------------*/
-void ICACHE_FLASH_ATTR
-jsontree_write_atom(const struct jsontree_context *js_ctx, const char *text)
-{
-  if(text == NULL) {
-    js_ctx->putchar('0');
-  } else {
-    while(*text != '\0') {
-      js_ctx->putchar(*text++);
-    }
-  }
-}
-/*---------------------------------------------------------------------------*/
-void ICACHE_FLASH_ATTR
-jsontree_write_string(const struct jsontree_context *js_ctx, const char *text)
-{
-  js_ctx->putchar('"');
-  if(text != NULL) {
-    while(*text != '\0') {
-      if(*text == '"') {
-        js_ctx->putchar('\\');
-      }
-      js_ctx->putchar(*text++);
-    }
-  }
-  js_ctx->putchar('"');
-}
-/*---------------------------------------------------------------------------*/
-void ICACHE_FLASH_ATTR
-jsontree_write_int(const struct jsontree_context *js_ctx, int value)
-{
-  char buf[10];
-  int l;
-
-  if(value < 0) {
-    js_ctx->putchar('-');
-    value = -value;
-  }
-
-  l = sizeof(buf) - 1;
-  do {
-    buf[l--] = '0' + (value % 10);
-    value /= 10;
-  } while(value > 0 && l >= 0);
-
-  while(++l < sizeof(buf)) {
-    js_ctx->putchar(buf[l]);
-  }
-}
-
-/*---------------------------------------------------------------------------*/
-void ICACHE_FLASH_ATTR
-jsontree_write_int_array(const struct jsontree_context *js_ctx, const int *text, uint32 length)
-{
-  uint32 i = 0;
-  if(text == NULL) {
-    js_ctx->putchar('0');
-  } else {
-    for (i = 0; i < length - 1; i ++) {
-      jsontree_write_int(js_ctx, *text++);
-	  js_ctx->putchar(',');
-    }
-	jsontree_write_int(js_ctx, *text);
-  }
-}
-
-
-/*---------------------------------------------------------------------------*/
-void ICACHE_FLASH_ATTR
-jsontree_setup(struct jsontree_context *js_ctx, struct jsontree_value *root,
-               int (* putchar)(int))
-{
-  js_ctx->values[0] = root;
-  js_ctx->putchar = putchar;
-  js_ctx->path = 0;
-  jsontree_reset(js_ctx);
-}
-/*---------------------------------------------------------------------------*/
-void ICACHE_FLASH_ATTR
-jsontree_reset(struct jsontree_context *js_ctx)
-{
-  js_ctx->depth = 0;
-  js_ctx->index[0] = 0;
-}
-/*---------------------------------------------------------------------------*/
-const char *ICACHE_FLASH_ATTR
-jsontree_path_name(const struct jsontree_context *js_ctx, int depth)
-{
-  if(depth < js_ctx->depth && js_ctx->values[depth]->type == JSON_TYPE_OBJECT) {
-    return ((struct jsontree_object *)js_ctx->values[depth])->
-      pairs[js_ctx->index[depth]].name;
-  }
-  return "";
-}
-/*---------------------------------------------------------------------------*/
-int ICACHE_FLASH_ATTR
-jsontree_print_next(struct jsontree_context *js_ctx)
-{
-  struct jsontree_value *v;
-  int index;
-
-  v = js_ctx->values[js_ctx->depth];
-
-  /* Default operation after switch is to back up one level */
-  switch(v->type) {
-  case JSON_TYPE_OBJECT:
-  case JSON_TYPE_ARRAY: {
-    struct jsontree_array *o = (struct jsontree_array *)v;
-    struct jsontree_value *ov;
-
-    index = js_ctx->index[js_ctx->depth];
-    if(index == 0) {
-      js_ctx->putchar(v->type);
-      js_ctx->putchar('\n');
-    }
-    if(index >= o->count) {
-      js_ctx->putchar('\n');
-      js_ctx->putchar(v->type + 2);
-      /* Default operation: back up one level! */
-      break;
-    }
-
-    if(index > 0) {
-      js_ctx->putchar(',');
-      js_ctx->putchar('\n');
-    }
-    if(v->type == JSON_TYPE_OBJECT) {
-      jsontree_write_string(js_ctx,
-                            ((struct jsontree_object *)o)->pairs[index].name);
-      js_ctx->putchar(':');
-      ov = ((struct jsontree_object *)o)->pairs[index].value;
-    } else {
-      ov = o->values[index];
-    }
-    /* TODO check max depth */
-    js_ctx->depth++;          /* step down to value... */
-    js_ctx->index[js_ctx->depth] = 0; /* and init index */
-    js_ctx->values[js_ctx->depth] = ov;
-    /* Continue on this new level */
-    return 1;
-  }
-  case JSON_TYPE_STRING:
-    jsontree_write_string(js_ctx, ((struct jsontree_string *)v)->value);
-    /* Default operation: back up one level! */
-    break;
-  case JSON_TYPE_INT:
-    jsontree_write_int(js_ctx, ((struct jsontree_int *)v)->value);
-    /* Default operation: back up one level! */
-    break;
-  case JSON_TYPE_CALLBACK: {   /* pre-formatted json string currently */
-    struct jsontree_callback *callback;
-
-    callback = (struct jsontree_callback *)v;
-    if(js_ctx->index[js_ctx->depth] == 0) {
-      /* First call: reset the callback status */
-      js_ctx->callback_state = 0;
-    }
-    if(callback->output == NULL) {
-      jsontree_write_string(js_ctx, "");
-    } else if(callback->output(js_ctx)) {
-      /* The callback wants to output more */
-      js_ctx->index[js_ctx->depth]++;
-      return 1;
-    }
-    /* Default operation: back up one level! */
-    break;
-  }
-  default:
-    PRINTF("\nError: Illegal json type:'%c'\n", v->type);
-    return 0;
-  }
-  /* Done => back up one level! */
-  if(js_ctx->depth > 0) {
-    js_ctx->depth--;
-    js_ctx->index[js_ctx->depth]++;
-    return 1;
-  }
-  return 0;
-}
-/*---------------------------------------------------------------------------*/
-static struct jsontree_value *ICACHE_FLASH_ATTR
-find_next(struct jsontree_context *js_ctx)
-{
-  struct jsontree_value *v;
-  int index;
-
-  do {
-    v = js_ctx->values[js_ctx->depth];
-
-    /* Default operation after switch is to back up one level */
-    switch(v->type) {
-    case JSON_TYPE_OBJECT:
-    case JSON_TYPE_ARRAY: {
-      struct jsontree_array *o = (struct jsontree_array *)v;
-      struct jsontree_value *ov;
-
-      index = js_ctx->index[js_ctx->depth];
-      if(index >= o->count) {
-        /* Default operation: back up one level! */
-        break;
-      }
-
-      if(v->type == JSON_TYPE_OBJECT) {
-        ov = ((struct jsontree_object *)o)->pairs[index].value;
-      } else {
-        ov = o->values[index];
-      }
-      /* TODO check max depth */
-      js_ctx->depth++;        /* step down to value... */
-      js_ctx->index[js_ctx->depth] = 0;       /* and init index */
-      js_ctx->values[js_ctx->depth] = ov;
-      /* Continue on this new level */
-      return ov;
-    }
-    default:
-      /* Default operation: back up one level! */
-      break;
-    }
-    /* Done => back up one level! */
-    if(js_ctx->depth > 0) {
-      js_ctx->depth--;
-      js_ctx->index[js_ctx->depth]++;
-    } else {
-      return NULL;
-    }
-  } while(1);
-}
-/*---------------------------------------------------------------------------*/
-struct jsontree_value *ICACHE_FLASH_ATTR
-jsontree_find_next(struct jsontree_context *js_ctx, int type)
-{
-  struct jsontree_value *v;
-
-  while((v = find_next(js_ctx)) != NULL && v->type != type &&
-        js_ctx->path < js_ctx->depth) {
-    /* search */
-  }
-  js_ctx->callback_state = 0;
-  return js_ctx->path < js_ctx->depth ? v : NULL;
-}
-/*---------------------------------------------------------------------------*/
-#endif
-

+ 0 - 1
app/libc/Makefile

@@ -38,7 +38,6 @@ endif
 
 INCLUDES := $(INCLUDES) -I $(PDIR)include
 INCLUDES += -I ./
-INCLUDES += -I ../wofs
 PDIR := ../$(PDIR)
 sinclude $(PDIR)Makefile
 

+ 4 - 3
app/libc/c_stdio.c

@@ -272,7 +272,6 @@ strtoupper(char *p)
 //#include <string.h>
 //#include <pmon.h>
 #include "c_string.h"
-typedef int int32_t;
 typedef unsigned int u_int32_t;
 typedef unsigned int u_int;
 typedef unsigned long u_long;
@@ -1095,12 +1094,14 @@ exponent(char *p, int exp, int fmtch)
 #endif /* FLOATINGPT */
 
 
-void c_sprintf(char *s, char *fmt, ...)
+int c_sprintf(char *s, const char *fmt, ...)
 {
+    int n;
     va_list arg;
     va_start(arg, fmt);
-    vsprintf(s, fmt, arg);
+    n = vsprintf(s, fmt, arg);
     va_end(arg);
+    return n;
 }
 
 #endif

+ 2 - 2
app/libc/c_stdio.h

@@ -60,10 +60,10 @@ extern void output_redirect(const char *str);
 #define c_sprintf os_sprintf
 #else
 #include "c_stdarg.h"
-void c_sprintf(char* s,char *fmt, ...);
+int c_sprintf(char* s,const char *fmt, ...);
 #endif
 
-// #define c_vsprintf ets_vsprintf
+#define c_vsprintf ets_vsprintf
 #define c_printf(...) do {					\
 	unsigned char __print_buf[BUFSIZ];		\
 	c_sprintf(__print_buf, __VA_ARGS__);	\

+ 2 - 0
app/libc/c_stdlib.h

@@ -10,6 +10,8 @@
 #include "c_stddef.h"
 #include "mem.h"
 
+#include <stdlib.h>
+
 #define EXIT_FAILURE 1
 #define EXIT_SUCCESS 0
 

+ 113 - 0
app/libc/c_string.c

@@ -1,4 +1,5 @@
 #include "c_string.h"
+#include "c_stdlib.h"
 
 // const char *c_strstr(const char * __s1, const char * __s2){
 // }
@@ -14,3 +15,115 @@
 
 // int c_strcoll(const char * s1, const char * s2){
 // }
+//
+char *c_strdup(const char *c) {
+  int len = os_strlen(c) + 1;
+  char *ret = os_malloc(len);
+  if (ret) {
+    memcpy(ret, c, len);
+  }
+  return ret;
+}
+
+/*	$OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+c_strlcpy(char *dst, const char *src, size_t siz)
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0 && --n != 0) {
+		do {
+			if ((*d++ = *s++) == 0)
+				break;
+		} while (--n != 0);
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src */
+	if (n == 0) {
+		if (siz != 0)
+			*d = '\0';		/* NUL-terminate dst */
+		while (*s++)
+			;
+	}
+
+	return(s - src - 1);	/* count does not include NUL */
+}
+
+/*	$OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left).  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+c_strlcat(char *dst, const char *src, size_t siz)
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+	size_t dlen;
+
+	/* Find the end of dst and adjust bytes left but don't go past end */
+	while (n-- != 0 && *d != '\0')
+		d++;
+	dlen = d - dst;
+	n = siz - dlen;
+
+	if (n == 0)
+		return(dlen + strlen(s));
+	while (*s != '\0') {
+		if (n != 1) {
+			*d++ = *s;
+			n--;
+		}
+		s++;
+	}
+	*d = '\0';
+
+	return(dlen + (s - src));	/* count does not include NUL */
+}
+

+ 6 - 0
app/libc/c_string.h

@@ -39,5 +39,11 @@
 // size_t c_strcspn(const char * s1, const char * s2);
 // const char *c_strpbrk(const char * /*s1*/, const char * /*s2*/);
 // int c_strcoll(const char * /*s1*/, const char * /*s2*/);
+//
+
+extern size_t c_strlcpy(char *dst, const char *src, size_t siz);
+extern size_t c_strlcat(char *dst, const char *src, size_t siz);
+extern char *c_strdup(const char *src);
+
 
 #endif /* _C_STRING_H_ */

+ 2 - 1
app/lua/Makefile

@@ -15,6 +15,8 @@ ifndef PDIR
 GEN_LIBS = liblua.a
 endif
 
+STD_CFLAGS=-std=gnu11 -Wimplicit
+
 #############################################################
 # Configuration i.e. compile options etc.
 # Target specific stuff (defines etc.) goes in here!
@@ -38,7 +40,6 @@ endif
 
 INCLUDES := $(INCLUDES) -I $(PDIR)include
 INCLUDES += -I ./
-INCLUDES += -I ../wofs
 INCLUDES += -I ../spiffs
 INCLUDES += -I ../libc
 INCLUDES += -I ../modules

+ 3 - 0
app/lua/lauxlib.c

@@ -806,6 +806,9 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
   return nptr;
 }
 
+LUALIB_API void luaL_assertfail(const char *file, int line, const char *message) {
+  c_printf("ASSERT@%s(%d): %s\n", file, line, message); 
+}
 
 static int panic (lua_State *L) {
   (void)L;  /* to avoid warnings */

+ 1 - 1
app/lua/lauxlib.h

@@ -100,7 +100,7 @@ LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
 LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
                                          const char *fname, int szhint);
 
-
+LUALIB_API void luaL_assertfail(const char *file, int line, const char *message);
 
 
 /*

+ 3 - 1
app/lua/lua.c

@@ -11,6 +11,8 @@
 #include "c_string.h"
 #include "flash_fs.h"
 #include "user_version.h"
+#include "driver/readline.h"
+#include "driver/uart.h"
 
 #define lua_c
 
@@ -466,7 +468,7 @@ int lua_main (int argc, char **argv) {
 
 void lua_handle_input (bool force)
 {
-  if (force || readline (&gLoad))
+  if (gLoad.L && (force || readline (&gLoad)))
     dojob (&gLoad);
 }
 

+ 1 - 0
app/lua/lua.h

@@ -19,6 +19,7 @@
 #include "c_stdarg.h"
 #include "c_stddef.h"
 #include "c_types.h"
+#include <ctype.h>
 #endif
 
 #include "luaconf.h"

+ 1 - 5
app/lua/luaconf.h

@@ -632,7 +632,7 @@ extern int readline4lua(const char *prompt, char *buffer, int length);
 /*
 @@ The luai_num* macros define the primitive operations over numbers.
 */
-#if defined(LUA_CORE)
+#if defined(LUA_CORE) || defined(LUA_LIB)
 #ifdef LUA_CROSS_COMPILER
 #include <math.h>
 #else
@@ -880,10 +880,6 @@ union luai_Cast { double l_d; long l_l; };
 
 #define LUA_INTFRMLEN		"l"
 #define LUA_INTFRM_T		long
-#ifndef LUA_CROSS_COMPILER
-typedef short int16_t;
-typedef long int32_t;
-#endif
 #endif
 
 

+ 36 - 3
app/lwip/app/espconn.c

@@ -847,7 +847,7 @@ espconn_get_connection_info(struct espconn *pespconn, remot_info **pcon_info, ui
 			break;
 		case ESPCONN_UDP:
 			while(plist != NULL){
-				if (plist->pespconn && plist->pespconn->type == ESPCONN_UDP){
+				if (plist->pespconn == pespconn){
 					premot[pespconn->link_cnt].state = plist->pespconn->state;
 					premot[pespconn->link_cnt].remote_port = plist->pcommon.remote_port;
 					os_memcpy(premot[pespconn->link_cnt].remote_ip, plist->pcommon.remote_ip, 4);
@@ -860,6 +860,8 @@ espconn_get_connection_info(struct espconn *pespconn, remot_info **pcon_info, ui
 			break;
 	}
 	*pcon_info = premot;
+	if (pespconn->link_cnt == 0)
+		return ESPCONN_ARG;
 	return ESPCONN_OK;
 }
 
@@ -954,12 +956,43 @@ espconn_disconnect(struct espconn *espconn)
     	/*protect for redisconnection*/
     	if (espconn->state == ESPCONN_CLOSE)
     		return ESPCONN_INPROGRESS;
-    	espconn_tcp_disconnect(pnode);
+    	espconn_tcp_disconnect(pnode,0);	//1 force, 0 normal
+    	return ESPCONN_OK;
+    } else
+    	return ESPCONN_ARG;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_abort
+ * Description  : Forcely abort with host
+ * Parameters   : espconn -- the espconn used to disconnect the connection
+ * Returns      : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_abort(struct espconn *espconn)
+{
+	espconn_msg *pnode = NULL;
+	bool value = false;
+
+    if (espconn == NULL) {
+        return ESPCONN_ARG;;
+    } else if (espconn ->type != ESPCONN_TCP)
+    	return ESPCONN_ARG;
+
+    /*Find the node depend on the espconn message*/
+    value = espconn_find_connection(espconn, &pnode);
+
+    if (value){
+    	/*protect for redisconnection*/
+    	if (espconn->state == ESPCONN_CLOSE)
+    		return ESPCONN_INPROGRESS;
+    	espconn_tcp_disconnect(pnode,1);	//1 force, 0 normal
     	return ESPCONN_OK;
     } else
     	return ESPCONN_ARG;
 }
 
+
 /******************************************************************************
  * FunctionName : espconn_get_packet_info
  * Description  : get the packet info with host
@@ -1215,7 +1248,7 @@ espconn_port(void)
     static uint32 randnum = 0;
 
     do {
-        port = system_get_time();
+        port = os_random();
 
         if (port < 0) {
             port = os_random() - port;

+ 22 - 14
app/lwip/app/espconn_tcp.c

@@ -38,12 +38,12 @@ os_event_t espconn_TaskQueue[espconn_TaskQueueLen];
 static err_t
 espconn_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
 static void
-espconn_client_close(void *arg, struct tcp_pcb *pcb);
+espconn_client_close(void *arg, struct tcp_pcb *pcb,u8 type);
 
 static err_t
 espconn_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
 static void
-espconn_server_close(void *arg, struct tcp_pcb *pcb);
+espconn_server_close(void *arg, struct tcp_pcb *pcb,u8 type);
 
 ///////////////////////////////common function/////////////////////////////////
 /******************************************************************************
@@ -495,14 +495,14 @@ espconn_tcp_sent(void *arg, uint8 *psent, uint16 length)
  * Parameters   : arg -- Additional argument to pass to the callback function
  * Returns      : none
 *******************************************************************************/
-void ICACHE_FLASH_ATTR espconn_tcp_disconnect(espconn_msg *pdiscon)
+void ICACHE_FLASH_ATTR espconn_tcp_disconnect(espconn_msg *pdiscon,u8 type)
 {
 	if (pdiscon != NULL){
 		/*disconnect with the host by send the FIN frame*/
 		if (pdiscon->preverse != NULL)
-			espconn_server_close(pdiscon, pdiscon->pcommon.pcb);
+			espconn_server_close(pdiscon, pdiscon->pcommon.pcb,type);
 		else
-			espconn_client_close(pdiscon, pdiscon->pcommon.pcb);
+			espconn_client_close(pdiscon, pdiscon->pcommon.pcb,type);
 	} else{
 		espconn_printf("espconn_tcp_disconnect err.\n");
 	}
@@ -517,7 +517,7 @@ void ICACHE_FLASH_ATTR espconn_tcp_disconnect(espconn_msg *pdiscon)
  * Returns      : none
 *******************************************************************************/
 static void ICACHE_FLASH_ATTR
-espconn_client_close(void *arg, struct tcp_pcb *pcb)
+espconn_client_close(void *arg, struct tcp_pcb *pcb, u8 type)
 {
     err_t err;
     espconn_msg *pclose = arg;
@@ -525,7 +525,11 @@ espconn_client_close(void *arg, struct tcp_pcb *pcb)
 	pclose->pcommon.pcb = pcb;
 	/*avoid recalling the disconnect function*/
 	tcp_recv(pcb, NULL);
-	err = tcp_close(pcb);
+
+	if(type == 0)
+		err = tcp_close(pcb);
+	else
+		{tcp_abort(pcb); err = ERR_OK;}
 
 	if (err != ERR_OK) {
 		/* closing failed, try again later */
@@ -647,7 +651,7 @@ espconn_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
     }
 
     if (err == ERR_OK && p == NULL) {
-        espconn_client_close(precv_cb, pcb);
+        espconn_client_close(precv_cb, pcb,0);
     }
 
     return ERR_OK;
@@ -943,7 +947,7 @@ espconn_tcp_client(struct espconn *espconn)
  * Returns      : none
 *******************************************************************************/
 static void ICACHE_FLASH_ATTR
-espconn_server_close(void *arg, struct tcp_pcb *pcb)
+espconn_server_close(void *arg, struct tcp_pcb *pcb,u8 type)
 {
     err_t err;
     espconn_msg *psclose = arg;
@@ -951,7 +955,11 @@ espconn_server_close(void *arg, struct tcp_pcb *pcb)
 	psclose->pcommon.pcb = pcb;
 	/*avoid recalling the disconnect function*/
 	tcp_recv(pcb, NULL);
-	err = tcp_close(pcb);
+
+	if(type ==0)
+		err = tcp_close(pcb);
+	else
+		{tcp_abort(pcb); err = ERR_OK;}
 
     if (err != ERR_OK) {
         /* closing failed, try again later */
@@ -1024,7 +1032,7 @@ espconn_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
             pbuf_free(p);
         }
 
-        espconn_server_close(precv_cb, pcb);
+        espconn_server_close(precv_cb, pcb,0);
     }
 
     return ERR_OK;
@@ -1086,7 +1094,7 @@ espconn_server_poll(void *arg, struct tcp_pcb *pcb)
 		if (pspoll_cb->pcommon.timeout != 0){/*no data sent in one active connection's set timeout, close.*/
 			if (pspoll_cb->pcommon.recv_check >= pspoll_cb->pcommon.timeout) {
 				pspoll_cb->pcommon.recv_check = 0;
-				espconn_server_close(pspoll_cb, pcb);
+				espconn_server_close(pspoll_cb, pcb,0);
 			}
 		} else {
 			espconn_msg *ptime_msg = pserver_list;
@@ -1095,7 +1103,7 @@ espconn_server_poll(void *arg, struct tcp_pcb *pcb)
 					if (ptime_msg->pcommon.timeout != 0){/*no data sent in server's set timeout, close.*/
 						if (pspoll_cb->pcommon.recv_check >= ptime_msg->pcommon.timeout){
 							pspoll_cb->pcommon.recv_check = 0;
-							espconn_server_close(pspoll_cb, pcb);
+							espconn_server_close(pspoll_cb, pcb,0);
 						}
 					} else {/*don't close for ever*/
 						pspoll_cb->pcommon.recv_check = 0;
@@ -1106,7 +1114,7 @@ espconn_server_poll(void *arg, struct tcp_pcb *pcb)
 			}
 		}
     } else {
-        espconn_server_close(pspoll_cb, pcb);
+        espconn_server_close(pspoll_cb, pcb,0);
     }
 
     return ERR_OK;

+ 1 - 1
app/lwip/app/espconn_udp.c

@@ -301,7 +301,7 @@ espconn_udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p,
 	if (wifi_get_opmode() != 1) {
 		wifi_get_ip_info(1, &ipconfig);
 
-		if (!ip_addr_netcmp((struct ip_addr *)precv->pespconn->proto.udp->remote_ip, &ipconfig.ip, &ipconfig.netmask)) {
+		if (!ip_addr_netcmp(addr, &ipconfig.ip, &ipconfig.netmask)) {
 			wifi_get_ip_info(0, &ipconfig);
 		}
 	} else {

+ 24 - 13
app/lwip/core/mdns.c

@@ -193,8 +193,8 @@ static char host_name[MDNS_NAME_LENGTH];
 static char service_name[MDNS_NAME_LENGTH];
 static char server_name[MDNS_NAME_LENGTH];
 //static char puck_datasheet[PUCK_DATASHEET_SIZE];
-static struct udp_pcb *mdns_pcb;
-
+static struct udp_pcb *mdns_pcb = NULL;
+static struct mdns_info * ms_info = NULL;
 static struct ip_addr multicast_addr;
 static struct ip_addr host_addr;
 static uint8 register_flag = 0;
@@ -887,7 +887,7 @@ mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr,
 	}
 	goto memerr2;
 	memerr2:
-	mem_free(mdns_payload);
+	os_memset(mdns_payload , 0 ,DNS_MSG_SIZE);
 	memerr1:
 	/* free pbuf */
 	pbuf_free(p);
@@ -900,8 +900,12 @@ mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr,
 void ICACHE_FLASH_ATTR
 mdns_close(void)
 {
-	if (mdns_pcb != NULL)
+	if (mdns_pcb != NULL && ms_info != NULL)
 		udp_remove(mdns_pcb);
+		os_free(ms_info);
+		mdns_pcb = NULL;
+		ms_info = NULL;
+
 }
 
 void ICACHE_FLASH_ATTR
@@ -1030,26 +1034,33 @@ mdns_init(struct mdns_info *info) {
 	multicast_addr.addr = DNS_MULTICAST_ADDRESS;
 	struct ip_addr ap_host_addr;
 	struct ip_info ipconfig;
-	if (info->ipAddr == 0) {
+	ms_info = (struct mdns_info *)os_zalloc(sizeof(struct mdns_info));
+	if (ms_info != NULL) {
+		os_memcpy(ms_info,info,sizeof(struct mdns_info));
+	} else {
+		os_printf("ms_info alloc failed\n");
+		return;
+	}
+	if (ms_info->ipAddr == 0) {
 		os_printf("mdns ip error!\n ");
 		return;
 	}
-	host_addr.addr = info->ipAddr ;
+	host_addr.addr = ms_info->ipAddr ;
 	LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
 	//get the datasheet from PUCK
-	mdns_set_hostname(info->host_name);
-	mdns_set_servername(info->server_name);
-	mdns_set_name(info->host_name);
+	mdns_set_hostname(ms_info->host_name);
+	mdns_set_servername(ms_info->server_name);
+	mdns_set_name(ms_info->host_name);
 
 	// get the host name as instrumentName_serialNumber for MDNS
 	// set the name of the service, the same as host name
 	os_printf("host_name = %s\n", host_name);
 	os_printf("server_name = %s\n", PUCK_SERVICE);
-	if (info->server_port == 0)
+	if (ms_info->server_port == 0)
 	{
 		PUCK_PORT = 80;
 	} else {
-		PUCK_PORT = info->server_port;
+		PUCK_PORT = ms_info->server_port;
 	}
 
 	/* initialize mDNS */
@@ -1080,14 +1091,14 @@ mdns_init(struct mdns_info *info) {
 
 		/*loopback function for the multicast(224.0.0.251) messages received at port 5353*/
 //		mdns_enable();
-		udp_recv(mdns_pcb, mdns_recv, info);
+		udp_recv(mdns_pcb, mdns_recv, ms_info);
 		mdns_flag = 1;
 		/*
 		 * Register the name of the instrument
 		 */
 
 		os_timer_disarm(&mdns_timer);
-		os_timer_setfn(&mdns_timer, (os_timer_func_t *)mdns_reg,info);
+		os_timer_setfn(&mdns_timer, (os_timer_func_t *)mdns_reg,ms_info);
 		os_timer_arm(&mdns_timer, 1000, 1);
 	}
 }

+ 9 - 5
app/lwip/core/tcp.c

@@ -632,15 +632,19 @@ tcp_new_port(void)
   int i;
   struct tcp_pcb *pcb;
 #ifndef TCP_LOCAL_PORT_RANGE_START
-#define TCP_LOCAL_PORT_RANGE_START 4096
+#define TCP_LOCAL_PORT_RANGE_START 1024
 #define TCP_LOCAL_PORT_RANGE_END   0x7fff
 #endif
-  static u16_t port __attribute__((section(".port"))) = TCP_LOCAL_PORT_RANGE_START;
+  static u16_t port = TCP_LOCAL_PORT_RANGE_START;
   
  again:
-  if (++port >= TCP_LOCAL_PORT_RANGE_END) {
-    port = TCP_LOCAL_PORT_RANGE_START;
-  }
+//  if (++port >= TCP_LOCAL_PORT_RANGE_END) {
+//    port = TCP_LOCAL_PORT_RANGE_START;
+//  }
+  port = os_random();
+  port %= TCP_LOCAL_PORT_RANGE_END;
+  if (port < TCP_LOCAL_PORT_RANGE_START)
+	  port += TCP_LOCAL_PORT_RANGE_START;
   /* Check all PCB lists. */
   for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {  
     for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {

+ 1 - 0
app/modules/.gitignore

@@ -0,0 +1 @@
+server-ca.crt.h

+ 3 - 1
app/modules/Makefile

@@ -15,6 +15,8 @@ ifndef PDIR
 GEN_LIBS = libmodules.a
 endif
 
+STD_CFLAGS=-std=gnu11 -Wimplicit
+
 #############################################################
 # Configuration i.e. compile options etc.
 # Target specific stuff (defines etc.) goes in here!
@@ -45,11 +47,11 @@ INCLUDES += -I ../u8glib
 INCLUDES += -I ../ucglib
 INCLUDES += -I ../lua
 INCLUDES += -I ../platform
-INCLUDES += -I ../wofs
 INCLUDES += -I ../spiffs
 INCLUDES += -I ../smart
 INCLUDES += -I ../cjson
 INCLUDES += -I ../dhtlib
+INCLUDES += -I ../http
 PDIR := ../$(PDIR)
 sinclude $(PDIR)Makefile
 

+ 40 - 1
app/modules/adc.c

@@ -24,10 +24,49 @@ static int adc_readvdd33( lua_State* L )
   return 1;
 }
 
+// Lua: adc.force_init_mode(x)
+static int adc_init107( lua_State *L )
+{
+  uint8_t byte107 = luaL_checkinteger (L, 1);
+
+  uint32 init_sector = flash_safe_get_sec_num () - 4;
+
+  // Note 32bit alignment so we can safely cast to uint32 for the flash api
+  char init_data[SPI_FLASH_SEC_SIZE] __attribute__((aligned(4)));
+
+  if (SPI_FLASH_RESULT_OK != flash_safe_read (
+    init_sector * SPI_FLASH_SEC_SIZE,
+    (uint32 *)init_data, sizeof(init_data)))
+      return luaL_error(L, "flash read error");
+
+  // If it's already the correct value, we don't need to force it
+  if (init_data[107] == byte107)
+  {
+    lua_pushboolean (L, false);
+    return 1;
+  }
+
+  // Nope, it differs, we need to rewrite it
+  init_data[107] = byte107;
+  if (SPI_FLASH_RESULT_OK != flash_safe_erase_sector (init_sector))
+    return luaL_error(L, "flash erase error");
+
+  if (SPI_FLASH_RESULT_OK != flash_safe_write (
+    init_sector * SPI_FLASH_SEC_SIZE,
+    (uint32 *)init_data, sizeof(init_data)))
+      return luaL_error(L, "flash write error");
+
+  lua_pushboolean (L, true);
+  return 1;
+}
+
 // Module function map
 static const LUA_REG_TYPE adc_map[] = {
   { LSTRKEY( "read" ),      LFUNCVAL( adc_sample ) },
-  { LSTRKEY( "readvdd33" ), LFUNCVAL( adc_readvdd33) },
+  { LSTRKEY( "readvdd33" ), LFUNCVAL( adc_readvdd33 ) },
+  { LSTRKEY( "force_init_mode" ), LFUNCVAL( adc_init107 ) },
+  { LSTRKEY( "INIT_ADC" ),  LNUMVAL( 0x00 ) },
+  { LSTRKEY( "INIT_VDD33" ),LNUMVAL( 0xff ) },
   { LNILKEY, LNILVAL }
 };
 

+ 153 - 0
app/modules/am2320.c

@@ -0,0 +1,153 @@
+/*
+ * app/modules/am2320.c
+ *
+ * Copyright (c) 2016 Henk Vergonet <Henk.Vergonet@gmail.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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "module.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "lwip/udp.h"
+#include <errno.h>
+
+static const uint32_t am2320_i2c_id = 0;
+static const uint8_t am2320_i2c_addr = 0xb8 >> 1;
+
+static uint16_t crc16(uint8_t *ptr, unsigned int len)
+{
+    uint16_t	crc =0xFFFF;
+    uint8_t	i;
+
+    while(len--) {
+	crc ^= *ptr++;
+	for(i=0;i<8;i++) {
+	    if(crc & 0x01) {
+		crc >>= 1;
+		crc ^= 0xA001;
+	    } else {
+		crc >>= 1;
+	    }
+	}
+    }
+    return crc;
+}
+
+/* make sure buf has lenght len+2 in order to accommodate for extra bytes */
+static int _read(uint32_t id, void *buf, uint8_t len, uint8_t off)
+{
+    int i;
+    uint8_t *b = (uint8_t *)buf;
+    uint16_t crc;
+
+    // step 1: Wake sensor
+    platform_i2c_send_start(id);
+    platform_i2c_send_address(id, am2320_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
+    os_delay_us(800);
+    platform_i2c_send_stop(id);
+
+    // step 2: Send read command
+    platform_i2c_send_start(id);
+    platform_i2c_send_address(id, am2320_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
+    platform_i2c_send_byte(id, 0x03);
+    platform_i2c_send_byte(id, off);
+    platform_i2c_send_byte(id, len);
+    platform_i2c_send_stop(id);
+
+    // step 3: Read the data
+    os_delay_us(1500);
+    platform_i2c_send_start(id);
+    platform_i2c_send_address(id, am2320_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER);
+    os_delay_us(30);
+    for(i=0; i<len+2; i++)
+	b[i] = platform_i2c_recv_byte(id,1);
+    crc  = platform_i2c_recv_byte(id,1);
+    crc |= platform_i2c_recv_byte(id,0) << 8;
+    platform_i2c_send_stop(id);
+
+    if(b[0] != 0x3 || b[1] != len)
+	return -EIO;
+    if(crc != crc16(b,len+2))
+	return -EIO;
+    return 0;
+}
+
+static int am2320_init(lua_State* L)
+{
+    uint32_t sda;
+    uint32_t scl;
+    int ret;
+    struct {
+    	uint8_t  cmd;
+    	uint8_t  len;
+	uint16_t model;
+	uint8_t	 version;
+	uint32_t id;
+    } nfo;
+
+    if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) {
+        return luaL_error(L, "wrong arg range");
+    }
+
+    sda = luaL_checkinteger(L, 1);
+    scl = luaL_checkinteger(L, 2);
+
+    if (scl == 0 || sda == 0) {
+        return luaL_error(L, "no i2c for D0");
+    }
+
+    platform_i2c_setup(am2320_i2c_id, sda, scl, PLATFORM_I2C_SPEED_SLOW);
+
+    os_delay_us(1500); // give some time to settle things down
+    ret = _read(am2320_i2c_id, &nfo, sizeof(nfo)-2, 0x08);
+    if(ret)
+        return luaL_error(L, "transmission error");
+
+    lua_pushinteger(L, ntohs(nfo.model));
+    lua_pushinteger(L, nfo.version);
+    lua_pushinteger(L, ntohl(nfo.id));
+    return 3;
+}
+
+static int am2320_read(lua_State* L)
+{
+    int ret;
+    struct {
+    	uint8_t  cmd;
+    	uint8_t  len;
+	uint16_t rh;
+	uint16_t temp;
+    } nfo;
+ 
+    ret = _read(am2320_i2c_id, &nfo, sizeof(nfo)-2, 0x00);
+    if(ret)
+        return luaL_error(L, "transmission error");
+
+    ret = ntohs(nfo.temp);
+    if(ret & 0x8000)
+    	ret = -(ret & 0x7fff);
+
+    lua_pushinteger(L, ntohs(nfo.rh));
+    lua_pushinteger(L, ret);
+    return 2;
+}
+
+static const LUA_REG_TYPE am2320_map[] = {
+    { LSTRKEY( "read" ), LFUNCVAL( am2320_read )},
+    { LSTRKEY( "init" ), LFUNCVAL( am2320_init )},
+    { LNILKEY, LNILVAL}
+};
+
+NODEMCU_MODULE(AM2320, "am2320", am2320_map, NULL);

+ 115 - 0
app/modules/apa102.c

@@ -0,0 +1,115 @@
+#include "c_stdlib.h"
+#include "c_string.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#include "lrotable.h"
+#include "module.h"
+#include "platform.h"
+#include "user_interface.h"
+
+
+#define NOP asm volatile(" nop \n\t")
+
+
+static inline void apa102_send_byte(uint32_t data_pin, uint32_t clock_pin, uint8_t byte) {
+  int i;
+  for (i = 0; i < 8; i++) {
+    if (byte & 0x80) {
+      GPIO_OUTPUT_SET(data_pin, PLATFORM_GPIO_HIGH); // Set pin high
+    } else {
+      GPIO_OUTPUT_SET(data_pin, PLATFORM_GPIO_LOW); // Set pin low
+    }
+    GPIO_OUTPUT_SET(clock_pin, PLATFORM_GPIO_HIGH); // Set pin high
+    byte <<= 1;
+    NOP;
+    NOP;
+    GPIO_OUTPUT_SET(clock_pin, PLATFORM_GPIO_LOW); // Set pin low
+    NOP;
+    NOP;
+  }
+}
+
+
+static void apa102_send_buffer(uint32_t data_pin, uint32_t clock_pin, uint32_t *buf, uint32_t nbr_frames) {
+  int i;
+
+  // Send 32-bit Start Frame that's all 0x00
+  apa102_send_byte(data_pin, clock_pin, 0x00);
+  apa102_send_byte(data_pin, clock_pin, 0x00);
+  apa102_send_byte(data_pin, clock_pin, 0x00);
+  apa102_send_byte(data_pin, clock_pin, 0x00);
+
+  // Send 32-bit LED Frames
+  for (i = 0; i < nbr_frames; i++) {
+    uint8_t *byte = (uint8_t *) buf++;
+
+    // Set the first 3 bits of that byte to 1.
+    // This makes the lua interface easier to use since you
+    // don't have to worry about creating invalid LED Frames.
+    byte[0] |= 0xE0;
+    apa102_send_byte(data_pin, clock_pin, byte[0]);
+    apa102_send_byte(data_pin, clock_pin, byte[1]);
+    apa102_send_byte(data_pin, clock_pin, byte[2]);
+    apa102_send_byte(data_pin, clock_pin, byte[3]);
+  }
+
+  // Send 32-bit End Frames
+  uint32_t required_postamble_frames = (nbr_frames + 1) / 2;
+  for (i = 0; i < required_postamble_frames; i++) {
+    apa102_send_byte(data_pin, clock_pin, 0xFF);
+    apa102_send_byte(data_pin, clock_pin, 0xFF);
+    apa102_send_byte(data_pin, clock_pin, 0xFF);
+    apa102_send_byte(data_pin, clock_pin, 0xFF);
+  }
+}
+
+
+// Lua: apa102.write(data_pin, clock_pin, "string")
+// Byte quads in the string are interpreted as (brightness, B, G, R) values.
+// Only the first 5 bits of the brightness value is actually used (0-31).
+// This function does not corrupt your buffer.
+//
+// apa102.write(1, 3, string.char(31, 0, 255, 0)) uses GPIO5 for DATA and GPIO0 for CLOCK and sets the first LED green, with the brightness 31 (out of 0-32)
+// apa102.write(5, 6, string.char(255, 0, 0, 255):rep(10)) uses GPIO14 for DATA and GPIO12 for CLOCK and sets ten LED to red, with the brightness 31 (out of 0-32).
+//                                                         Brightness values are clamped to 0-31.
+static int apa102_write(lua_State* L) {
+  uint8_t data_pin = luaL_checkinteger(L, 1);
+  MOD_CHECK_ID(gpio, data_pin);
+  uint32_t alt_data_pin = pin_num[data_pin];
+
+  uint8_t clock_pin = luaL_checkinteger(L, 2);
+  MOD_CHECK_ID(gpio, clock_pin);
+  uint32_t alt_clock_pin = pin_num[clock_pin];
+
+  size_t buf_len;
+  const char *buf = luaL_checklstring(L, 3, &buf_len);
+  uint32_t nbr_frames = buf_len / 4;
+
+  if (nbr_frames > 100000) {
+    return luaL_error(L, "The supplied buffer is too long, and might cause the callback watchdog to bark.");
+  }
+
+  // Initialize the output pins
+  platform_gpio_mode(data_pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
+  GPIO_OUTPUT_SET(alt_data_pin, PLATFORM_GPIO_HIGH); // Set pin high
+  platform_gpio_mode(clock_pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
+  GPIO_OUTPUT_SET(alt_clock_pin, PLATFORM_GPIO_LOW); // Set pin low
+
+  // Send the buffers
+  apa102_send_buffer(alt_data_pin, alt_clock_pin, (uint32_t *) buf, (uint32_t) nbr_frames);
+  return 0;
+}
+
+
+const LUA_REG_TYPE apa102_map[] =
+{
+  { LSTRKEY( "write" ), LFUNCVAL( apa102_write )},
+  { LNILKEY, LNILVAL}
+};
+
+LUALIB_API int luaopen_apa102(lua_State *L) {
+  LREGISTER(L, "apa102", apa102_map);
+  return 0;
+}
+
+NODEMCU_MODULE(APA102, "apa102", apa102_map, luaopen_apa102);

+ 442 - 0
app/modules/bme280.c

@@ -0,0 +1,442 @@
+// ***************************************************************************
+// BMP280 module for ESP8266 with nodeMCU
+// 
+// Written by Lukas Voborsky, @voborsky
+// 
+// MIT license, http://opensource.org/licenses/MIT
+// ***************************************************************************
+
+//#define NODE_DEBUG
+
+#include "module.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "c_math.h"
+
+/****************************************************/
+/**\name	registers definition  */
+/***************************************************/
+#define BME280_REGISTER_CONTROL               (0xF4)
+#define BME280_REGISTER_CONTROL_HUM           (0xF2)
+#define BME280_REGISTER_CONFIG                (0xF5)
+#define BME280_REGISTER_CHIPID                (0xD0)
+#define BME280_REGISTER_VERSION               (0xD1)
+#define BME280_REGISTER_SOFTRESET             (0xE0)
+#define BME280_REGISTER_CAL26                 (0xE1)
+#define BME280_REGISTER_TEMP                  (0xFA)
+#define BME280_REGISTER_PRESS                 (0xF7)
+#define BME280_REGISTER_HUM                   (0xFD)
+
+#define BME280_REGISTER_DIG_T                 (0x88)
+#define BME280_REGISTER_DIG_P                 (0x8E)
+#define BME280_REGISTER_DIG_H1                (0xA1)
+#define BME280_REGISTER_DIG_H2                (0xE1)
+/****************************************************/
+/**\name	I2C ADDRESS DEFINITIONS  */
+/***************************************************/
+#define BME280_I2C_ADDRESS1                  (0x76)
+#define BME280_I2C_ADDRESS2                  (0x77)
+/****************************************************/
+/**\name	POWER MODE DEFINITIONS  */
+/***************************************************/
+/* Sensor Specific constants */
+#define BME280_SLEEP_MODE                    (0x00)
+#define BME280_FORCED_MODE                   (0x01)
+#define BME280_NORMAL_MODE                   (0x03)
+#define BME280_SOFT_RESET_CODE               (0xB6)
+/****************************************************/
+/**\name	OVER SAMPLING DEFINITIONS  */
+/***************************************************/
+#define BME280_OVERSAMP_1X                    (0x01)
+#define BME280_OVERSAMP_2X                    (0x02)
+#define BME280_OVERSAMP_4X                    (0x03)
+#define BME280_OVERSAMP_8X                    (0x04)
+#define BME280_OVERSAMP_16X                   (0x05)
+/****************************************************/
+/**\name	STANDBY DEFINITIONS  */
+/***************************************************/
+#define BME280_STANDBY_TIME_1_MS              (0x00)
+#define BME280_STANDBY_TIME_63_MS             (0x01)
+#define BME280_STANDBY_TIME_125_MS            (0x02)
+#define BME280_STANDBY_TIME_250_MS            (0x03)
+#define BME280_STANDBY_TIME_500_MS            (0x04)
+#define BME280_STANDBY_TIME_1000_MS           (0x05)
+#define BME280_STANDBY_TIME_10_MS             (0x06)
+#define BME280_STANDBY_TIME_20_MS             (0x07)
+/****************************************************/
+/**\name	FILTER DEFINITIONS  */
+/***************************************************/
+#define BME280_FILTER_COEFF_OFF               (0x00)
+#define BME280_FILTER_COEFF_2                 (0x01)
+#define BME280_FILTER_COEFF_4                 (0x02)
+#define BME280_FILTER_COEFF_8                 (0x03)
+#define BME280_FILTER_COEFF_16                (0x04)
+/****************************************************/
+/**\data type definition  */
+/***************************************************/
+#define BME280_S32_t int32_t
+#define BME280_U32_t uint32_t
+#define BME280_S64_t int64_t
+
+#define BME280_SAMPLING_DELAY  113 //maximum measurement time in ms for maximum oversampling for all measures = 1.25 + 2.3*16 + 2.3*16 + 0.575 + 2.3*16 + 0.575 ms
+
+#define r16s(reg)  ((int16_t)r16u(reg))
+#define r16sLE(reg)  ((int16_t)r16uLE(reg))
+
+#define bme280_adc_T(void) r24u(BME280_REGISTER_TEMP)
+#define bme280_adc_P(void) r24u(BME280_REGISTER_PRESS)
+#define bme280_adc_H(void) r16u(BME280_REGISTER_HUM)
+
+static const uint32_t bme280_i2c_id = 0;
+
+static uint8_t bme280_i2c_addr = BME280_I2C_ADDRESS1;
+static uint8_t bme280_isbme = 0; // 1 if the chip is BME280, 0 for BMP280
+static uint8_t bme280_mode = 0; // stores oversampling settings
+static uint8_t bme280_ossh = 0; // stores humidity oversampling settings
+os_timer_t bme280_timer; // timer for forced mode readout
+int lua_connected_readout_ref; // callback when readout is ready
+
+static struct {
+	uint16_t  dig_T1;
+	int16_t   dig_T2;
+	int16_t   dig_T3;
+	uint16_t  dig_P1;
+	int16_t   dig_P2;
+	int16_t   dig_P3;
+	int16_t   dig_P4;
+	int16_t   dig_P5;
+	int16_t   dig_P6;
+	int16_t   dig_P7;
+	int16_t   dig_P8;
+	int16_t   dig_P9;
+	uint8_t   dig_H1;
+	int16_t   dig_H2;
+	uint8_t   dig_H3;
+	int16_t   dig_H4;
+	int16_t   dig_H5;
+	int8_t    dig_H6;
+} bme280_data;
+
+static BME280_S32_t bme280_t_fine;
+static uint32_t bme280_h = 0;
+static double bme280_hc = 0.0;
+
+static uint8_t r8u(uint8_t reg) {
+	uint8_t ret;
+
+	platform_i2c_send_start(bme280_i2c_id);
+	platform_i2c_send_address(bme280_i2c_id, bme280_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
+	platform_i2c_send_byte(bme280_i2c_id, reg);
+	platform_i2c_send_stop(bme280_i2c_id);
+	platform_i2c_send_start(bme280_i2c_id);
+	platform_i2c_send_address(bme280_i2c_id, bme280_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER);
+	ret = platform_i2c_recv_byte(bme280_i2c_id, 0);
+	platform_i2c_send_stop(bme280_i2c_id);
+	//NODE_DBG("reg:%x, value:%x \n", reg, ret);
+	return ret;
+}
+
+static uint8_t w8u(uint8_t reg, uint8_t val) {
+	platform_i2c_send_start(bme280_i2c_id);
+	platform_i2c_send_address(bme280_i2c_id, bme280_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
+	platform_i2c_send_byte(bme280_i2c_id, reg);
+	platform_i2c_send_byte(bme280_i2c_id, val);
+	platform_i2c_send_stop(bme280_i2c_id);
+}
+
+static uint16_t r16u(uint8_t reg) {
+	uint8_t high = r8u(reg);
+	uint8_t low  = r8u(++reg);
+	return (high << 8) | low;
+}
+
+static uint16_t r16uLE(uint8_t reg) {
+	uint8_t low = r8u(reg);
+	uint8_t high  = r8u(++reg);
+	return (high << 8) | low;
+}
+
+static uint32_t r24u(uint8_t reg) {
+	uint8_t high = r8u(reg);
+	uint8_t mid  = r8u(++reg);
+	uint8_t low  = r8u(++reg);
+	return (uint32_t)(((high << 16) | (mid << 8) | low) >> 4);
+}
+
+// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.  
+// t_fine carries fine temperature as global value 
+static BME280_S32_t bme280_compensate_T(BME280_S32_t adc_T) {
+	BME280_S32_t var1, var2, T; 
+	var1  = ((((adc_T>>3) - ((BME280_S32_t)bme280_data.dig_T1<<1))) * ((BME280_S32_t)bme280_data.dig_T2)) >> 11; 
+	var2  = (((((adc_T>>4) - ((BME280_S32_t)bme280_data.dig_T1)) * ((adc_T>>4) - ((BME280_S32_t)bme280_data.dig_T1))) >> 12) *  
+		((BME280_S32_t)bme280_data.dig_T3)) >> 14; 
+	bme280_t_fine = var1 + var2; 
+	T  = (bme280_t_fine * 5 + 128) >> 8; 
+	return T; 
+}
+
+// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits). 
+// Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa 
+static BME280_U32_t bme280_compensate_P(BME280_S32_t adc_P) {
+	BME280_S64_t var1, var2, p; 
+	var1 = ((BME280_S64_t)bme280_t_fine) - 128000; 
+	var2 = var1 * var1 * (BME280_S64_t)bme280_data.dig_P6; 
+	var2 = var2 + ((var1*(BME280_S64_t)bme280_data.dig_P5)<<17); 
+	var2 = var2 + (((BME280_S64_t)bme280_data.dig_P4)<<35); 
+	var1 = ((var1 * var1 * (BME280_S64_t)bme280_data.dig_P3)>>8) + ((var1 * (BME280_S64_t)bme280_data.dig_P2)<<12); 
+	var1 = (((((BME280_S64_t)1)<<47)+var1))*((BME280_S64_t)bme280_data.dig_P1)>>33; 
+	if (var1 == 0) { 
+		return 0; // avoid exception caused by division by zero 
+	} 
+	p = 1048576-adc_P; 
+	p = (((p<<31)-var2)*3125)/var1; 
+	var1 = (((BME280_S64_t)bme280_data.dig_P9) * (p>>13) * (p>>13)) >> 25; 
+	var2 = (((BME280_S64_t)bme280_data.dig_P8) * p) >> 19; 
+	p = ((p + var1 + var2) >> 8) + (((BME280_S64_t)bme280_data.dig_P7)<<4); 
+	p = (p * 10) >> 8;
+	return (BME280_U32_t)p; 
+} 
+ 
+// Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits). 
+// Output value of “47445” represents 47445/1024 = 46.333 %RH 
+static BME280_U32_t bme280_compensate_H(BME280_S32_t adc_H) {
+	BME280_S32_t v_x1_u32r; 
+
+	v_x1_u32r = (bme280_t_fine - ((BME280_S32_t)76800)); 
+	v_x1_u32r = (((((adc_H << 14) - (((BME280_S32_t)bme280_data.dig_H4) << 20) - (((BME280_S32_t)bme280_data.dig_H5) * v_x1_u32r)) + 
+		((BME280_S32_t)16384)) >> 15) * (((((((v_x1_u32r * ((BME280_S32_t)bme280_data.dig_H6)) >> 10) * (((v_x1_u32r * 
+		((BME280_S32_t)bme280_data.dig_H3)) >> 11) + ((BME280_S32_t)32768))) >> 10) + ((BME280_S32_t)2097152)) * 
+		((BME280_S32_t)bme280_data.dig_H2) + 8192) >> 14)); 
+	v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((BME280_S32_t)bme280_data.dig_H1)) >> 4)); 
+	v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);  
+	v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);   
+	v_x1_u32r = v_x1_u32r>>12;
+	return (BME280_U32_t)((v_x1_u32r * 1000)>>10); 
+} 
+
+static int bme280_lua_init(lua_State* L) {
+	uint8_t sda;
+	uint8_t scl;
+	uint8_t config;
+	uint8_t ack;
+	
+	uint8_t const bit3 = 0b111;
+	uint8_t const bit2 = 0b11;
+
+	if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) {
+		return luaL_error(L, "wrong arg range");
+	}
+	sda = luaL_checkinteger(L, 1);
+	scl = luaL_checkinteger(L, 2);
+	
+	bme280_mode = (!lua_isnumber(L, 6)?BME280_NORMAL_MODE:(luaL_checkinteger(L, 6)&bit2)) // 6-th parameter: power mode
+		| ((!lua_isnumber(L, 4)?BME280_OVERSAMP_16X:(luaL_checkinteger(L, 4)&bit3)) << 2) // 4-th parameter: pressure oversampling
+		| ((!lua_isnumber(L, 3)?BME280_OVERSAMP_16X:(luaL_checkinteger(L, 3)&bit3)) << 5); // 3-rd parameter: temperature oversampling
+		
+	bme280_ossh = (!lua_isnumber(L, 5))?BME280_OVERSAMP_16X:(luaL_checkinteger(L, 5)&bit3); // 5-th parameter: humidity oversampling
+	
+	config = ((!lua_isnumber(L, 7)?BME280_STANDBY_TIME_20_MS:(luaL_checkinteger(L, 7)&bit3))<< 4) // 7-th parameter: inactive duration in normal mode
+		| ((!lua_isnumber(L, 8)?BME280_FILTER_COEFF_16:(luaL_checkinteger(L, 8)&bit3)) << 1); // 8-th parameter: IIR filter
+	NODE_DBG("mode: %x\nhumidity oss: %x\nconfig: %x\n", bme280_mode, bme280_ossh, config);
+	
+	platform_i2c_setup(bme280_i2c_id, sda, scl, PLATFORM_I2C_SPEED_SLOW);
+
+	bme280_i2c_addr = BME280_I2C_ADDRESS1;
+	platform_i2c_send_start(bme280_i2c_id);
+	ack = platform_i2c_send_address(bme280_i2c_id, bme280_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
+	platform_i2c_send_stop(bme280_i2c_id);
+	if (!ack) {
+		NODE_DBG("No ACK on address: %x\n", bme280_i2c_addr);
+		bme280_i2c_addr = BME280_I2C_ADDRESS2;
+		platform_i2c_send_start(bme280_i2c_id);
+		ack = platform_i2c_send_address(bme280_i2c_id, bme280_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
+		platform_i2c_send_stop(bme280_i2c_id);
+		if (!ack) {
+			NODE_DBG("No ACK on address: %x\n", bme280_i2c_addr);
+			return 0;
+		}
+	}
+
+	uint8_t chipid = r8u(BME280_REGISTER_CHIPID);
+	NODE_DBG("chip_id: %x\n", chipid);
+	bme280_isbme = (chipid == 0x60);
+	
+	uint8_t reg = BME280_REGISTER_DIG_T;
+	bme280_data.dig_T1 = r16uLE(reg); reg+=2;
+	bme280_data.dig_T2 = r16sLE(reg); reg+=2;
+	bme280_data.dig_T3 = r16sLE(reg);
+	//NODE_DBG("dig_T: %d\t%d\t%d\n", bme280_data.dig_T1, bme280_data.dig_T2, bme280_data.dig_T3);
+
+	reg = BME280_REGISTER_DIG_P;
+	bme280_data.dig_P1 = r16uLE(reg); reg+=2;
+	bme280_data.dig_P2 = r16sLE(reg); reg+=2;
+	bme280_data.dig_P3 = r16sLE(reg); reg+=2;
+	bme280_data.dig_P4 = r16sLE(reg); reg+=2;
+	bme280_data.dig_P5 = r16sLE(reg); reg+=2;
+	bme280_data.dig_P6 = r16sLE(reg); reg+=2;
+	bme280_data.dig_P7 = r16sLE(reg); reg+=2;
+	bme280_data.dig_P8 = r16sLE(reg); reg+=2;
+	bme280_data.dig_P9 = r16sLE(reg);
+	// NODE_DBG("dig_P: %d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", bme280_data.dig_P1, bme280_data.dig_P2, bme280_data.dig_P3, bme280_data.dig_P4, bme280_data.dig_P5, bme280_data.dig_P6, bme280_data.dig_P7, bme280_data.dig_P8, bme280_data.dig_P9);
+	
+	w8u(BME280_REGISTER_CONFIG, config);
+	if (bme280_isbme) {
+		reg = BME280_REGISTER_DIG_H1;
+		bme280_data.dig_H1 = r8u(reg);
+		reg = BME280_REGISTER_DIG_H2;
+		bme280_data.dig_H2 = r16sLE(reg); reg+=2;
+		bme280_data.dig_H3 = r8u(reg); reg++;
+		bme280_data.dig_H4 = ((int16_t)r8u(reg) << 4 | (r8u(reg+1) & 0xF)); reg+=2;
+		bme280_data.dig_H5 = ((int16_t)r8u(reg+1) << 4 | (r8u(reg) >> 4)); reg+=2;
+		bme280_data.dig_H6 = (int8_t)r8u(reg);
+		// NODE_DBG("dig_H: %d\t%d\t%d\t%d\t%d\t%d\n", bme280_data.dig_H1, bme280_data.dig_H2, bme280_data.dig_H3, bme280_data.dig_H4, bme280_data.dig_H5, bme280_data.dig_H6);
+
+		w8u(BME280_REGISTER_CONTROL_HUM, bme280_ossh);
+		lua_pushinteger(L, 2);
+	} else {
+		lua_pushinteger(L, 1);
+	}
+	w8u(BME280_REGISTER_CONTROL, bme280_mode);
+	
+	return 1;
+}
+
+static void bme280_readoutdone (void *arg)
+{
+	NODE_DBG("timer out\n");
+	lua_State *L = arg;
+	lua_rawgeti (L, LUA_REGISTRYINDEX, lua_connected_readout_ref);
+	lua_call (L, 0, 0);
+	luaL_unref (L, LUA_REGISTRYINDEX, lua_connected_readout_ref);
+	os_timer_disarm (&bme280_timer);
+}
+
+static int bme280_lua_startreadout(lua_State* L) {
+	uint32_t delay;
+	
+	if (lua_isnumber(L, 1)) {
+		delay = luaL_checkinteger(L, 1);
+		if (!delay) {delay = BME280_SAMPLING_DELAY;} // if delay is 0 then set the default delay
+	}
+	
+	if (!lua_isnoneornil(L, 2)) {
+		lua_pushvalue(L, 2);
+		lua_connected_readout_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+	} else {
+		lua_connected_readout_ref = LUA_NOREF;
+	}
+
+	w8u(BME280_REGISTER_CONTROL_HUM, bme280_ossh);
+	w8u(BME280_REGISTER_CONTROL, (bme280_mode & 0xFC) | BME280_FORCED_MODE);
+	NODE_DBG("control old: %x, control: %x, delay: %d\n", bme280_mode, (bme280_mode & 0xFC) | BME280_FORCED_MODE, delay);
+
+	if (lua_connected_readout_ref != LUA_NOREF) {
+		NODE_DBG("timer armed\n");
+		os_timer_disarm (&bme280_timer);
+		os_timer_setfn (&bme280_timer, (os_timer_func_t *)bme280_readoutdone, L);
+		os_timer_arm (&bme280_timer, delay, 0); // trigger callback when readout is ready
+	}
+	return 0;
+}
+
+static int bme280_lua_temp(lua_State* L) {
+	uint32_t adc_T = bme280_adc_T();
+	if (adc_T == 0x80000 || adc_T == 0xfffff)
+		return 0;
+	lua_pushinteger(L, bme280_compensate_T(adc_T));
+	lua_pushinteger(L, bme280_t_fine);
+	return 2;
+}
+
+static int bme280_lua_baro(lua_State* L) {
+	uint32_t adc_T = bme280_adc_T();
+	uint32_t T = bme280_compensate_T(adc_T);
+	uint32_t adc_P = bme280_adc_P();
+	if (adc_T == 0x80000 || adc_T == 0xfffff || adc_P ==0x80000 || adc_P == 0xfffff)
+		return 0;
+	lua_pushinteger(L, bme280_compensate_P(adc_P));
+	lua_pushinteger(L, T);
+	return 2;
+}
+
+static int bme280_lua_humi(lua_State* L) {
+	if (!bme280_isbme) return 0;
+	uint32_t adc_T = bme280_adc_T();
+	uint32_t T = bme280_compensate_T(adc_T);
+	uint32_t adc_H = bme280_adc_H();
+	if (adc_T == 0x80000 || adc_T == 0xfffff || adc_H == 0x8000 || adc_H == 0xffff)
+		return 0;
+	lua_pushinteger(L, bme280_compensate_H(adc_H));
+	lua_pushinteger(L, T);
+	return 2;
+}
+
+static int bme280_lua_qfe2qnh(lua_State* L) {
+	if (!lua_isnumber(L, 2)) {
+		return luaL_error(L, "wrong arg range");
+	}
+	int32_t qfe = luaL_checkinteger(L, 1);
+	int32_t h = luaL_checkinteger(L, 2);
+	
+	double hc;
+	if (bme280_h == h) {
+		hc = bme280_hc;
+	} else {
+		hc = pow((double)(1.0 - 2.25577e-5 * h), (double)(-5.25588));
+		bme280_hc = hc; bme280_h = h;
+	}
+	double qnh = (double)qfe * hc;
+	lua_pushinteger(L, (int32_t)(qnh + 0.5));
+	return 1;
+}
+
+static int bme280_lua_altitude(lua_State* L) {
+	if (!lua_isnumber(L, 2)) {
+		return luaL_error(L, "wrong arg range");
+	}
+	int32_t P = luaL_checkinteger(L, 1);
+	int32_t qnh = luaL_checkinteger(L, 2);
+
+	double h = (1.0 - pow((double)P/(double)qnh, 1.0/5.25588)) / 2.25577e-5 * 100.0;
+	lua_pushinteger(L, (int32_t)(h + (((h<0)?-1:(h>0)) * 0.5)));
+	return 1;
+}
+
+static double ln(double x) {
+	double y = (x-1)/(x+1);
+	double y2 = y*y;
+	double r = 0;
+	for (int8_t i=33; i>0; i-=2) { //we've got the power
+		r = 1.0/(double)i + y2 * r;
+	}
+	return 2*y*r;
+}
+
+static int bme280_lua_dewpoint(lua_State* L) {
+	const double c243 = 243.5;
+	const double c17 = 17.67;
+	if (!lua_isnumber(L, 2)) {
+		return luaL_error(L, "wrong arg range");
+	}
+	double H = luaL_checkinteger(L, 1)/100000.0;
+	double T = luaL_checkinteger(L, 2)/100.0;
+
+	double c = ln(H) + ((c17 * T) / (c243 + T));
+	double d = (c243 * c)/(c17 - c) * 100.0;
+	lua_pushinteger(L, (int32_t)(d + (((d<0)?-1:(d>0)) * 0.5)));
+	return 1;
+}
+
+static const LUA_REG_TYPE bme280_map[] = {
+	{ LSTRKEY( "init" ), LFUNCVAL(bme280_lua_init)},
+	{ LSTRKEY( "temp" ),  LFUNCVAL(bme280_lua_temp)},
+	{ LSTRKEY( "baro" ),  LFUNCVAL(bme280_lua_baro)},
+	{ LSTRKEY( "humi" ),  LFUNCVAL(bme280_lua_humi)},
+	{ LSTRKEY( "startreadout" ),  LFUNCVAL(bme280_lua_startreadout)},
+	{ LSTRKEY( "qfe2qnh" ),  LFUNCVAL(bme280_lua_qfe2qnh)},
+	{ LSTRKEY( "altitude" ),  LFUNCVAL(bme280_lua_altitude)},
+	{ LSTRKEY( "dewpoint" ),  LFUNCVAL(bme280_lua_dewpoint)},
+	{ LNILKEY, LNILVAL}
+};
+
+NODEMCU_MODULE(BME280, "bme280", bme280_map, NULL);

+ 3 - 3
app/modules/bmp085.c

@@ -74,7 +74,7 @@ static int ICACHE_FLASH_ATTR bmp085_init(lua_State* L) {
     bmp085_data.MC  = r16(bmp085_i2c_id, 0xBC);
     bmp085_data.MD  = r16(bmp085_i2c_id, 0xBE);
 
-    return 1;
+    return 0;
 }
 
 static uint32_t bmp085_temperature_raw_b5(void) {
@@ -128,7 +128,7 @@ static int32_t ICACHE_FLASH_ATTR bmp085_pressure_raw(int oss) {
     p3 = r8u(bmp085_i2c_id, 0xF8);
     p = (p1 << 16) | (p2 << 8) | p3;
     p = p >> (8 - oss);
- 
+
     return p;
 }
 
@@ -159,7 +159,7 @@ static int ICACHE_FLASH_ATTR bmp085_lua_pressure(lua_State* L) {
             oss = 3;
         }
     }
- 
+
     p = bmp085_pressure_raw(oss);
     B5 = bmp085_temperature_raw_b5();
 

+ 1 - 0
app/modules/cjson.c

@@ -43,6 +43,7 @@
 #include "c_limits.h"
 #include "lauxlib.h"
 #include "flash_api.h"
+#include "ctype.h"
 
 #include "strbuf.h"
 #include "cjson_mem.h"

+ 5 - 2
app/modules/coap.c

@@ -326,7 +326,7 @@ static int coap_request( lua_State* L, coap_method_t m )
   stack++;
   pesp_conn = cud->pesp_conn;
   ip_addr_t ipaddr;
-  uint8_t host[32];
+  uint8_t host[64];
 
   unsigned t;
   if ( lua_isnumber(L, stack) )
@@ -348,6 +348,9 @@ static int coap_request( lua_State* L, coap_method_t m )
   coap_uri_t *uri = coap_new_uri(url, l);   // should call free(uri) somewhere
   if (uri == NULL)
     return luaL_error( L, "uri wrong format." );
+  if (uri->host.length + 1 /* for the null */ > sizeof(host)) {
+    return luaL_error(L, "host too long");
+  }
 
   pesp_conn->proto.udp->remote_port = uri->port;
   NODE_DBG("UDP port is set: %d.\n", uri->port);
@@ -372,7 +375,7 @@ static int coap_request( lua_State* L, coap_method_t m )
   if(!pdu){
     if(uri)
       c_free(uri);
-    return;
+    return luaL_error (L, "alloc fail");
   }
 
   const char *payload = NULL;

+ 236 - 4
app/modules/crypto.c

@@ -1,11 +1,14 @@
 // Module for cryptography
 
+#include <c_errno.h>
 #include "module.h"
 #include "lauxlib.h"
 #include "platform.h"
 #include "c_types.h"
 #include "c_stdlib.h"
+#include "flash_fs.h"
 #include "../crypto/digests.h"
+#include "../crypto/mech.h"
 
 #include "user_interface.h"
 
@@ -34,7 +37,29 @@ static int crypto_sha1( lua_State* L )
   return 1;
 }
 
+#ifdef LUA_USE_MODULES_ENCODER
+static int call_encoder( lua_State* L, const char *function ) {
+  if (lua_gettop(L) != 1) { 
+    luaL_error(L, "%s must have one argument", function);
+  }
+  lua_getfield(L, LUA_GLOBALSINDEX, "encoder");
+  if (!lua_istable(L, -1) && !lua_isrotable(L, -1)) { // also need table just in case encoder has been overloaded
+    luaL_error(L, "Cannot find encoder.%s", function); 
+  }
+  lua_getfield(L, -1, function);
+  lua_insert(L, 1);    //move function below the argument
+  lua_pop(L, 1);       //and dump the encoder rotable from stack.
+  lua_call(L,1,1);     // call encoder.xxx(string)
+  return 1;
+}
 
+static int crypto_base64_encode (lua_State* L) { 
+  return call_encoder(L, "toBase64"); 
+}
+static int crypto_hex_encode (lua_State* L) { 
+  return call_encoder(L, "toHex"); 
+}
+#else
 static const char* bytes64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 /**
   * encoded = crypto.toBase64(raw)
@@ -81,7 +106,7 @@ static int crypto_hex_encode( lua_State* L)
   c_free(out);
   return 1;
 }
-
+#endif
 /**
   * masked = crypto.mask(message, mask)
   *
@@ -102,10 +127,9 @@ static int crypto_mask( lua_State* L )
   return 1;
 }
 
-
 static inline int bad_mech (lua_State *L) { return luaL_error (L, "unknown hash mech"); }
 static inline int bad_mem  (lua_State *L) { return luaL_error (L, "insufficient memory"); }
-
+static inline int bad_file (lua_State *L) { return luaL_error (L, "file does not exist"); }
 
 /* rawdigest = crypto.hash("MD5", str)
  * strdigest = crypto.toHex(rawdigest)
@@ -126,6 +150,132 @@ static int crypto_lhash (lua_State *L)
   return 1;
 }
 
+typedef struct digest_user_datum_t  {
+	const digest_mech_info_t *mech_info;
+	void *ctx;
+} digest_user_datum_t;
+
+/* General Usage for extensible hash functions:
+ * sha = crypto.new_hash("MD5")
+ * sha.update("Data")
+ * sha.update("Data2")
+ * strdigest = crypto.toHex(sha.finalize())
+ */
+
+/* crypto.new_hash("MECHTYPE") */
+static int crypto_new_hash (lua_State *L)
+{
+  const digest_mech_info_t *mi = crypto_digest_mech (luaL_checkstring (L, 1));
+  if (!mi)
+    return bad_mech (L);
+
+  void *ctx = os_malloc (mi->ctx_size);
+  if (ctx==NULL)
+    return bad_mem (L);
+
+  mi->create (ctx);
+
+  // create a userdataum with specific metatable
+  digest_user_datum_t *dudat = (digest_user_datum_t *)lua_newuserdata(L, sizeof(digest_user_datum_t));
+  luaL_getmetatable(L, "crypto.hash");
+  lua_setmetatable(L, -2);
+
+  // Set pointers to the mechanics and CTX
+  dudat->mech_info = mi;
+  dudat->ctx       = ctx;
+
+  return 1; // Pass userdata object back
+}
+
+/* Called as object, params:
+   1 - userdata "this"
+   2 - new string to add to the hash state  */
+static int crypto_hash_update (lua_State *L)
+{
+  NODE_DBG("enter crypto_hash_update.\n");
+  digest_user_datum_t *dudat;
+  size_t sl;
+
+  dudat = (digest_user_datum_t *)luaL_checkudata(L, 1, "crypto.hash");
+  luaL_argcheck(L, dudat, 1, "crypto.hash expected");
+
+  const digest_mech_info_t *mi = dudat->mech_info;
+
+  size_t len = 0;
+  const char *data = luaL_checklstring (L, 2, &len);
+
+  mi->update (dudat->ctx, data, len);
+
+  return 0;  // No return value
+}
+
+/* Called as object, no params. Returns digest of default size. */
+static int crypto_hash_finalize (lua_State *L)
+{
+  NODE_DBG("enter crypto_hash_update.\n");
+  digest_user_datum_t *dudat;
+  size_t sl;
+
+  dudat = (digest_user_datum_t *)luaL_checkudata(L, 1, "crypto.hash");
+  luaL_argcheck(L, dudat, 1, "crypto.hash expected");
+
+  const digest_mech_info_t *mi = dudat->mech_info;
+
+  uint8_t digest[mi->digest_size]; // Allocate as local
+  mi->finalize (digest, dudat->ctx);
+
+  lua_pushlstring (L, digest, sizeof (digest));
+  return 1;
+}
+
+/* Frees memory for the user datum and CTX hash state */
+static int crypto_hash_gcdelete (lua_State *L)
+{
+  NODE_DBG("enter crypto_hash_delete.\n");
+  digest_user_datum_t *dudat;
+
+  dudat = (digest_user_datum_t *)luaL_checkudata(L, 1, "crypto.hash");
+  luaL_argcheck(L, dudat, 1, "crypto.hash expected");
+
+  os_free(dudat->ctx);
+
+  return 0;
+}
+
+
+/* rawdigest = crypto.hash("MD5", filename)
+ * strdigest = crypto.toHex(rawdigest)
+ */
+static int crypto_flhash (lua_State *L)
+{
+  const digest_mech_info_t *mi = crypto_digest_mech (luaL_checkstring (L, 1));
+  if (!mi)
+    return bad_mech (L);
+  const char *filename = luaL_checkstring (L, 2);
+
+  // Open the file
+  int file_fd = fs_open (filename, FS_RDONLY);
+  if(file_fd < FS_OPEN_OK) {
+    return bad_file(L);
+  }
+
+  // Compute hash
+  uint8_t digest[mi->digest_size];
+  int returncode = crypto_fhash (mi, &fs_read, file_fd, digest);
+
+  // Finish up
+  fs_close(file_fd);
+
+  if (returncode == ENOMEM)
+    return bad_mem (L);
+  else if (returncode == EINVAL)
+    return bad_mech(L);
+  else
+    lua_pushlstring (L, digest, sizeof (digest));
+
+  return 1;
+}
+
 
 /* rawsignature = crypto.hmac("SHA1", str, key)
  * strsignature = crypto.toHex(rawsignature)
@@ -149,6 +299,78 @@ static int crypto_lhmac (lua_State *L)
 }
 
 
+
+static const crypto_mech_t *get_mech (lua_State *L, int idx)
+{
+  const char *name = luaL_checkstring (L, idx);
+
+  const crypto_mech_t *mech = crypto_encryption_mech (name);
+  if (mech)
+    return mech;
+
+  luaL_error (L, "unknown cipher: %s", name);
+  __builtin_unreachable ();
+}
+
+static int crypto_encdec (lua_State *L, bool enc)
+{
+  const crypto_mech_t *mech = get_mech (L, 1);
+  size_t klen;
+  const char *key = luaL_checklstring (L, 2, &klen);
+  size_t dlen;
+  const char *data = luaL_checklstring (L, 3, &dlen);
+
+  size_t ivlen;
+  const char *iv = luaL_optlstring (L, 4, "", &ivlen);
+
+  size_t bs = mech->block_size;
+  size_t outlen = ((dlen + bs -1) / bs) * bs;
+  char *buf = (char *)os_zalloc (outlen);
+  if (!buf)
+    return luaL_error (L, "crypto init failed");
+
+  crypto_op_t op =
+  {
+    key, klen,
+    iv, ivlen,
+    data, dlen,
+    buf, outlen,
+    enc ? OP_ENCRYPT : OP_DECRYPT
+  };
+  if (!mech->run (&op))
+  {
+    os_free (buf);
+    return luaL_error (L, "crypto op failed");
+  }
+  else
+  {
+    lua_pushlstring (L, buf, outlen);
+    // note: if lua_pushlstring runs out of memory, we leak buf :(
+    os_free (buf);
+    return 1;
+  }
+}
+
+static int lcrypto_encrypt (lua_State *L)
+{
+  return crypto_encdec (L, true);
+}
+
+static int lcrypto_decrypt (lua_State *L)
+{
+  return crypto_encdec (L, false);
+}
+
+// Hash function map
+static const LUA_REG_TYPE crypto_hash_map[] = {
+  { LSTRKEY( "update" ),  LFUNCVAL( crypto_hash_update ) },
+  { LSTRKEY( "finalize" ),   LFUNCVAL( crypto_hash_finalize ) },
+  { LSTRKEY( "__gc" ),    LFUNCVAL( crypto_hash_gcdelete ) },
+  { LSTRKEY( "__index" ), LROVAL( crypto_hash_map ) },
+  { LNILKEY, LNILVAL }
+};
+
+
 // Module function map
 static const LUA_REG_TYPE crypto_map[] = {
   { LSTRKEY( "sha1" ),     LFUNCVAL( crypto_sha1 ) },
@@ -156,8 +378,18 @@ static const LUA_REG_TYPE crypto_map[] = {
   { LSTRKEY( "toHex" ),    LFUNCVAL( crypto_hex_encode ) },
   { LSTRKEY( "mask" ),     LFUNCVAL( crypto_mask ) },
   { LSTRKEY( "hash"   ),   LFUNCVAL( crypto_lhash ) },
+  { LSTRKEY( "fhash"  ),   LFUNCVAL( crypto_flhash ) },
+  { LSTRKEY( "new_hash"   ),   LFUNCVAL( crypto_new_hash ) },
   { LSTRKEY( "hmac"   ),   LFUNCVAL( crypto_lhmac ) },
+  { LSTRKEY( "encrypt" ),  LFUNCVAL( lcrypto_encrypt ) },
+  { LSTRKEY( "decrypt" ),  LFUNCVAL( lcrypto_decrypt ) },
   { LNILKEY, LNILVAL }
 };
 
-NODEMCU_MODULE(CRYPTO, "crypto", crypto_map, NULL);
+int luaopen_crypto ( lua_State *L )
+{
+  luaL_rometatable(L, "crypto.hash", (void *)crypto_hash_map);  // create metatable for crypto.hash
+  return 0;
+}
+
+NODEMCU_MODULE(CRYPTO, "crypto", crypto_map, luaopen_crypto);

+ 159 - 0
app/modules/encoder.c

@@ -0,0 +1,159 @@
+// Module encoders
+
+#include "module.h"
+#include "lauxlib.h"
+#include "lmem.h"
+#include "c_string.h"
+#define BASE64_INVALID '\xff'
+#define BASE64_PADDING '='
+#define ISBASE64(c) (unbytes64[c] != BASE64_INVALID)
+
+static const uint8 b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static uint8 *toBase64 ( lua_State* L, const uint8 *msg, size_t *len){
+  size_t i, n = *len;
+
+  if (!n)  // handle empty string case 
+    return NULL;
+
+  uint8 * q, *out = (uint8 *)luaM_malloc(L, (n + 2) / 3 * 4);
+  uint8 bytes64[sizeof(b64)];
+  c_memcpy(bytes64, b64, sizeof(b64));   //Avoid lots of flash unaligned fetches
+  
+  for (i = 0, q = out; i < n; i += 3) {
+    int a = msg[i];
+    int b = (i + 1 < n) ? msg[i + 1] : 0;
+    int c = (i + 2 < n) ? msg[i + 2] : 0;
+    *q++ = bytes64[a >> 2];
+    *q++ = bytes64[((a & 3) << 4) | (b >> 4)];
+    *q++ = (i + 1 < n) ? bytes64[((b & 15) << 2) | (c >> 6)] : BASE64_PADDING;
+    *q++ = (i + 2 < n) ? bytes64[(c & 63)] : BASE64_PADDING;
+  }
+  *len = q - out;
+  return out;
+}
+
+static uint8 *fromBase64 ( lua_State* L, const uint8 *enc_msg, size_t *len){
+  int i, n = *len, blocks = (n>>2), pad = 0;
+  const uint8 *p;
+  uint8 unbytes64[UCHAR_MAX+1], *msg, *q;
+
+  if (!n)  // handle empty string case 
+    return NULL;
+ 
+  if (n & 3)
+    luaL_error (L, "Invalid base64 string"); 
+   
+  c_memset(unbytes64, BASE64_INVALID, sizeof(unbytes64));
+  for (i = 0; i < sizeof(b64)-1; i++) unbytes64[b64[i]] = i;  // sequential so no exceptions 
+  
+  if (enc_msg[n-1] == BASE64_PADDING) {
+    pad =  (enc_msg[n-2] != BASE64_PADDING) ? 1 : 2;
+    blocks--;  //exclude padding block
+  }    
+
+ for (i = 0; i < n - pad; i++) if (!ISBASE64(enc_msg[i])) luaL_error (L, "Invalid base64 string");
+  unbytes64[BASE64_PADDING] = 0;
+
+  msg = q = (uint8 *) luaM_malloc(L, 1+ (3 * n / 4)); 
+  for (i = 0, p = enc_msg; i<blocks; i++)  {
+    uint8 a = unbytes64[*p++]; 
+    uint8 b = unbytes64[*p++]; 
+    uint8 c = unbytes64[*p++]; 
+    uint8 d = unbytes64[*p++];
+    *q++ = (a << 2) | (b >> 4);
+    *q++ = (b << 4) | (c >> 2);
+    *q++ = (c << 6) | d;
+  }
+
+  if (pad) { //now process padding block bytes
+    uint8 a = unbytes64[*p++];
+    uint8 b = unbytes64[*p++];
+    *q++ = (a << 2) | (b >> 4);
+    if (pad == 1) *q++ = (b << 4) | (unbytes64[*p] >> 2);
+  }
+  *len = q - msg;
+  return msg;
+}
+
+static inline uint8 to_hex_nibble(uint8 b) {
+  return b + ( b < 10 ? '0' : 'a' - 10 );
+  }
+    
+static uint8 *toHex ( lua_State* L, const uint8 *msg, size_t *len){
+  int i, n = *len;
+  uint8 *q, *out = (uint8 *)luaM_malloc(L, n * 2);
+  for (i = 0, q = out; i < n; i++) {
+    *q++ = to_hex_nibble(msg[i] >> 4);
+    *q++ = to_hex_nibble(msg[i] & 0xf);
+  }
+  *len = 2*n; 
+  return out;
+}
+
+static uint8 *fromHex ( lua_State* L, const uint8 *msg, size_t *len){
+  int i, n = *len;
+  const uint8 *p;
+  uint8 b, *q, *out = (uint8 *)luaM_malloc(L, n * 2);
+  uint8 c;
+  
+  if (n &1)
+    luaL_error (L, "Invalid hex string");
+ 
+  for (i = 0, p = msg, q = out; i < n; i++) {
+     if (*p >= '0' && *p <= '9') {
+       b = *p++ - '0';
+     } else if (*p >= 'a' && *p <= 'f') {
+       b = *p++ - ('a' - 10);
+     } else if (*p >= 'A' && *p <= 'F') {
+       b = *p++ - ('A' - 10);
+     } else {
+       luaL_error (L, "Invalid hex string");
+     }
+     if ((i&1) == 0) {
+       c = b<<4;
+     } else {
+       *q++ = c+ b;
+     }    
+  }
+  *len = n>>1; 
+  return out;
+}
+
+// All encoder functions are of the form:
+// Lua:  output_string = encoder.function(input_string) 
+// Where input string maybe empty, but not nil
+// Hence these all call the do_func wrapper
+static int do_func (lua_State *L, uint8 * (*conv_func)(lua_State *, const uint8 *, size_t *)) {
+  size_t l;
+  const uint8 *input = luaL_checklstring(L, 1, &l);
+//  luaL_argcheck(L, l>0, 1, "input string empty");
+  uint8 *output = conv_func(L, input, &l);
+  
+  if (output) {
+    lua_pushlstring(L, output, l);
+    luaM_free(L, output);
+  } else {
+    lua_pushstring(L, "");
+  }
+  return 1;
+}
+
+#define DECLARE_FUNCTION(f) static int encoder_ ## f (lua_State *L) \
+{ return do_func(L, f); }
+
+  DECLARE_FUNCTION(fromBase64);
+  DECLARE_FUNCTION(toBase64);
+  DECLARE_FUNCTION(fromHex);
+  DECLARE_FUNCTION(toHex);
+
+// Module function map
+static const LUA_REG_TYPE encoder_map[] = {
+  { LSTRKEY("fromBase64"), LFUNCVAL(encoder_fromBase64)  },
+  { LSTRKEY("toBase64"),   LFUNCVAL(encoder_toBase64) },
+  { LSTRKEY("fromHex"),    LFUNCVAL(encoder_fromHex)  },
+  { LSTRKEY("toHex"),      LFUNCVAL(encoder_toHex) },
+  { LNILKEY, LNILVAL }
+};
+
+NODEMCU_MODULE(ENCODER, "encoder", encoder_map, NULL);

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 9 - 4
app/modules/enduser_setup.c


+ 35 - 30
app/modules/file.c

@@ -20,8 +20,8 @@ static int file_open( lua_State* L )
   }
 
   const char *fname = luaL_checklstring( L, 1, &len );
-  if( len > FS_NAME_MAX_LENGTH )
-    return luaL_error(L, "filename too long");
+  luaL_argcheck(L, len < FS_NAME_MAX_LENGTH && c_strlen(fname) == len, 1, "filename invalid");
+
   const char *mode = luaL_optstring(L, 2, "r");
 
   file_fd = fs_open(fname, fs_mode2flag(mode));
@@ -61,22 +61,7 @@ static int file_format( lua_State* L )
   return 0; 
 }
 
-#if defined(BUILD_WOFS)
-// Lua: list()
-static int file_list( lua_State* L )
-{
-  uint32_t start = 0;
-  size_t act_len = 0;
-  char fsname[ FS_NAME_MAX_LENGTH + 1 ];
-  lua_newtable( L );
-  while( FS_FILE_OK == wofs_next(&start, fsname, FS_NAME_MAX_LENGTH, &act_len) ){
-    lua_pushinteger(L, act_len);
-    lua_setfield( L, -2, fsname );
-  }
-  return 1;
-}
-
-#elif defined(BUILD_SPIFFS)
+#if defined(BUILD_SPIFFS)
 
 extern spiffs fs;
 
@@ -114,13 +99,27 @@ static int file_seek (lua_State *L)
   return 1;
 }
 
+// Lua: exists(filename)
+static int file_exists( lua_State* L )
+{
+  size_t len;
+  const char *fname = luaL_checklstring( L, 1, &len );    
+  luaL_argcheck(L, len <= FS_NAME_MAX_LENGTH, 1, "filename too long");
+
+  spiffs_stat stat;
+  int rc = SPIFFS_stat(&fs, (char *)fname, &stat);
+
+  lua_pushboolean(L, (rc == SPIFFS_OK ? 1 : 0));
+
+  return 1;
+}
+
 // Lua: remove(filename)
 static int file_remove( lua_State* L )
 {
   size_t len;
-  const char *fname = luaL_checklstring( L, 1, &len );
-  if( len > FS_NAME_MAX_LENGTH )
-    return luaL_error(L, "filename too long");
+  const char *fname = luaL_checklstring( L, 1, &len );    
+  luaL_argcheck(L, len <= FS_NAME_MAX_LENGTH, 1, "filename too long");
   file_close(L);
   SPIFFS_remove(&fs, (char *)fname);
   return 0;  
@@ -157,12 +156,10 @@ static int file_rename( lua_State* L )
   }
 
   const char *oldname = luaL_checklstring( L, 1, &len );
-  if( len > FS_NAME_MAX_LENGTH )
-    return luaL_error(L, "filename too long");
-
-  const char *newname = luaL_checklstring( L, 2, &len );
-  if( len > FS_NAME_MAX_LENGTH )
-    return luaL_error(L, "filename too long");
+  luaL_argcheck(L, len <= FS_NAME_MAX_LENGTH, 1, "filename too long");
+  
+  const char *newname = luaL_checklstring( L, 2, &len );  
+  luaL_argcheck(L, len <= FS_NAME_MAX_LENGTH, 2, "filename too long");
 
   if(SPIFFS_OK==myspiffs_rename( oldname, newname )){
     lua_pushboolean(L, 1);
@@ -175,7 +172,7 @@ static int file_rename( lua_State* L )
 // Lua: fsinfo()
 static int file_fsinfo( lua_State* L )
 {
-  uint32_t total, used;
+  u32_t total, used;
   SPIFFS_info(&fs, &total, &used);
   NODE_DBG("total: %d, used:%d\n", total, used);
   if(total>0x7FFFFFFF || used>0x7FFFFFFF || used > total)
@@ -295,6 +292,13 @@ static int file_writeline( lua_State* L )
   return 1;
 }
 
+static int file_fscfg (lua_State *L)
+{
+  lua_pushinteger (L, fs.cfg.phys_addr);
+  lua_pushinteger (L, fs.cfg.phys_size);
+  return 2;
+}
+
 // Module function map
 static const LUA_REG_TYPE file_map[] = {
   { LSTRKEY( "list" ),      LFUNCVAL( file_list ) },
@@ -305,13 +309,14 @@ static const LUA_REG_TYPE file_map[] = {
   { LSTRKEY( "read" ),      LFUNCVAL( file_read ) },
   { LSTRKEY( "readline" ),  LFUNCVAL( file_readline ) },
   { LSTRKEY( "format" ),    LFUNCVAL( file_format ) },
-#if defined(BUILD_SPIFFS) && !defined(BUILD_WOFS)
+#if defined(BUILD_SPIFFS)
   { LSTRKEY( "remove" ),    LFUNCVAL( file_remove ) },
   { LSTRKEY( "seek" ),      LFUNCVAL( file_seek ) },
   { LSTRKEY( "flush" ),     LFUNCVAL( file_flush ) },
-//{ LSTRKEY( "check" ),     LFUNCVAL( file_check ) },
   { LSTRKEY( "rename" ),    LFUNCVAL( file_rename ) },
   { LSTRKEY( "fsinfo" ),    LFUNCVAL( file_fsinfo ) },
+  { LSTRKEY( "fscfg" ),     LFUNCVAL( file_fscfg ) },
+  { LSTRKEY( "exists" ),    LFUNCVAL( file_exists ) },  
 #endif
   { LNILKEY, LNILVAL }
 };

+ 139 - 147
app/modules/gpio.c

@@ -2,152 +2,164 @@
 
 #include "module.h"
 #include "lauxlib.h"
+#include "lmem.h"
 #include "platform.h"
-
+#include "user_interface.h"
 #include "c_types.h"
 #include "c_string.h"
+#include "gpio.h"
 
 #define PULLUP PLATFORM_GPIO_PULLUP
 #define FLOAT PLATFORM_GPIO_FLOAT
 #define OUTPUT PLATFORM_GPIO_OUTPUT
+#define OPENDRAIN PLATFORM_GPIO_OPENDRAIN
 #define INPUT PLATFORM_GPIO_INPUT
 #define INTERRUPT PLATFORM_GPIO_INT
 #define HIGH PLATFORM_GPIO_HIGH
 #define LOW PLATFORM_GPIO_LOW
 
-
 #ifdef GPIO_INTERRUPT_ENABLE
-static int gpio_cb_ref[GPIO_PIN_NUM];
-static lua_State* gL = NULL;
 
-void lua_gpio_unref(unsigned pin){
-  if(gpio_cb_ref[pin] != LUA_NOREF){
-    if(gL!=NULL)
-      luaL_unref(gL, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
-  }
-  gpio_cb_ref[pin] = LUA_NOREF;
-}
+// We also know that the non-level interrupt types are < LOLEVEL, and that
+// HILEVEL is > LOLEVEL. Since this is burned into the hardware it is not
+// going to change.
+#define INTERRUPT_TYPE_IS_LEVEL(x)	((x) >= GPIO_PIN_INTR_LOLEVEL)
 
-void gpio_intr_callback( unsigned pin, unsigned level )
+static int gpio_cb_ref[GPIO_PIN_NUM];
+
+// This task is scheduled by the ISR and is used
+// to initiate the Lua-land gpio.trig() callback function
+// It also re-enables the pin interrupt, so that we get another callback queued
+static void gpio_intr_callback_task (task_param_t param, uint8 priority)
 {
+  unsigned pin = param >> 1;
+  unsigned level = param & 1;
+  UNUSED(priority);
+
   NODE_DBG("pin:%d, level:%d \n", pin, level);
-  if(gpio_cb_ref[pin] == LUA_NOREF)
-    return;
-  if(!gL)
-    return;
-  lua_rawgeti(gL, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
-  lua_pushinteger(gL, level);
-  lua_call(gL, 1, 0);
+  if(gpio_cb_ref[pin] != LUA_NOREF) {
+    // GPIO callbacks are run in L0 and inlcude the level as a parameter
+    lua_State *L = lua_getstate();
+    NODE_DBG("Calling: %08x\n", gpio_cb_ref[pin]);
+    //
+    if (!INTERRUPT_TYPE_IS_LEVEL(pin_int_type[pin])) {
+      // Edge triggered -- re-enable the interrupt
+      platform_gpio_intr_init(pin, pin_int_type[pin]);
+    }
+
+    // Do the actual callback
+    lua_rawgeti(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
+    lua_pushinteger(L, level);
+    lua_call(L, 1, 0);
+
+    if (INTERRUPT_TYPE_IS_LEVEL(pin_int_type[pin])) {
+      // Level triggered -- re-enable the callback
+      platform_gpio_intr_init(pin, pin_int_type[pin]);
+    }
+  }
 }
 
 // Lua: trig( pin, type, function )
 static int lgpio_trig( lua_State* L )
 {
-  unsigned type;
-  unsigned pin;
-  size_t sl;
-  
-  pin = luaL_checkinteger( L, 1 );
-  MOD_CHECK_ID( gpio, pin );
-  if(pin==0)
-    return luaL_error( L, "no interrupt for D0" );
-
-  const char *str = luaL_checklstring( L, 2, &sl );
-  if (str == NULL)
-    return luaL_error( L, "wrong arg type" );
-
-  if(sl == 2 && c_strcmp(str, "up") == 0){
-    type = GPIO_PIN_INTR_POSEDGE;
-  }else if(sl == 4 && c_strcmp(str, "down") == 0){
-    type = GPIO_PIN_INTR_NEGEDGE;
-  }else if(sl == 4 && c_strcmp(str, "both") == 0){
-    type = GPIO_PIN_INTR_ANYEDGE;
-  }else if(sl == 3 && c_strcmp(str, "low") == 0){
-    type = GPIO_PIN_INTR_LOLEVEL;
-  }else if(sl == 4 && c_strcmp(str, "high") == 0){
-    type = GPIO_PIN_INTR_HILEVEL;
-  }else{
-    type = GPIO_PIN_INTR_DISABLE;
-  }
+  unsigned pin = luaL_checkinteger( L, 1 );
+  static const char * const opts[] = {"none", "up", "down", "both", "low", "high", NULL};
+  static const int opts_type[] = {
+    GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_POSEDGE, GPIO_PIN_INTR_NEGEDGE,
+    GPIO_PIN_INTR_ANYEDGE, GPIO_PIN_INTR_LOLEVEL, GPIO_PIN_INTR_HILEVEL
+    };
+  luaL_argcheck(L, platform_gpio_exists(pin) && pin>0, 1, "Invalid interrupt pin");
+
+  int old_pin_ref = gpio_cb_ref[pin];
+  int type = opts_type[luaL_checkoption(L, 2, "none", opts)];
+
+  if (type == GPIO_PIN_INTR_DISABLE) {
+    // "none" clears the callback
+    gpio_cb_ref[pin] = LUA_NOREF;
 
-  // luaL_checkanyfunction(L, 3);
-  if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION){
-    lua_pushvalue(L, 3);  // copy argument (func) to the top of stack
-    if(gpio_cb_ref[pin] != LUA_NOREF)
-      luaL_unref(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
+  } else if (lua_gettop(L)==2 && old_pin_ref != LUA_NOREF) {
+    // keep the old one if no callback 
+    old_pin_ref = LUA_NOREF;
+
+  } else if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION) {
+    // set up the new callback if present
+    lua_pushvalue(L, 3);
     gpio_cb_ref[pin] = luaL_ref(L, LUA_REGISTRYINDEX);
+
+  } else {
+     // invalid combination, so clear down any old callback and throw an error
+    if(old_pin_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, old_pin_ref);
+    luaL_argcheck(L,  0, 3, "invalid callback type");
   }
 
+  // unreference any overwritten callback
+  if(old_pin_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, old_pin_ref);
+
+  NODE_DBG("Pin data: %d %d %08x, %d %d %d, %08x\n",
+          pin, type, pin_mux[pin], pin_num[pin], pin_func[pin], pin_int_type[pin], gpio_cb_ref[pin]);
   platform_gpio_intr_init(pin, type);
-  return 0;  
+  return 0;
 }
 #endif
 
 // Lua: mode( pin, mode, pullup )
 static int lgpio_mode( lua_State* L )
 {
-  unsigned mode, pullup = FLOAT;
-  unsigned pin;
+  unsigned pin = luaL_checkinteger( L, 1 );
+  unsigned mode = luaL_checkinteger( L, 2 );
+  unsigned pullup = luaL_optinteger( L, 3, FLOAT );
+
+  luaL_argcheck(L, platform_gpio_exists(pin) && (mode!=INTERRUPT || pin>0), 1, "Invalid pin");
+  luaL_argcheck(L, mode==OUTPUT || mode==OPENDRAIN || mode==INPUT
+ #ifdef GPIO_INTERRUPT_ENABLE
+                                               || mode==INTERRUPT
+ #endif
+                                                                  , 2,  "wrong arg type" );
+  if(pullup!=FLOAT) pullup = PULLUP;
+
+NODE_DBG("pin,mode,pullup= %d %d %d\n",pin,mode,pullup);
+NODE_DBG("Pin data at mode: %d %08x, %d %d %d, %08x\n",
+          pin, pin_mux[pin], pin_num[pin], pin_func[pin], pin_int_type[pin], gpio_cb_ref[pin]);
 
-  pin = luaL_checkinteger( L, 1 );
-  MOD_CHECK_ID( gpio, pin );
-  mode = luaL_checkinteger( L, 2 );
-  if ( mode!=OUTPUT && mode!=INPUT && mode!=INTERRUPT)
-    return luaL_error( L, "wrong arg type" );
-  if(pin==0 && mode==INTERRUPT)
-    return luaL_error( L, "no interrupt for D0" );
-  if(lua_isnumber(L, 3))
-    pullup = lua_tointeger( L, 3 );
-  if(pullup!=FLOAT)
-    pullup = PULLUP;
 #ifdef GPIO_INTERRUPT_ENABLE
-  gL = L;   // save to local gL, for callback function
-  if (mode!=INTERRUPT){     // disable interrupt
+  if (mode != INTERRUPT){     // disable interrupt
     if(gpio_cb_ref[pin] != LUA_NOREF){
       luaL_unref(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
+      gpio_cb_ref[pin] = LUA_NOREF;
     }
-    gpio_cb_ref[pin] = LUA_NOREF;
   }
 #endif
-  int r = platform_gpio_mode( pin, mode, pullup );
-  if( r<0 )
+
+  if( platform_gpio_mode( pin, mode, pullup ) < 0 )
     return luaL_error( L, "wrong pin num." );
-  return 0;  
+
+  return 0;
 }
 
 // Lua: read( pin )
 static int lgpio_read( lua_State* L )
 {
-  unsigned pin;
-  
-  pin = luaL_checkinteger( L, 1 );
+  unsigned pin = luaL_checkinteger( L, 1 );
   MOD_CHECK_ID( gpio, pin );
-
-  unsigned level = platform_gpio_read( pin );
-  lua_pushinteger( L, level );
-  return 1; 
+  lua_pushinteger( L, platform_gpio_read( pin ) );
+  return 1;
 }
 
 // Lua: write( pin, level )
 static int lgpio_write( lua_State* L )
 {
-  unsigned level;
-  unsigned pin;
-  
-  pin = luaL_checkinteger( L, 1 );
+  unsigned pin = luaL_checkinteger( L, 1 );
+  unsigned level = luaL_checkinteger( L, 2 );
+
   MOD_CHECK_ID( gpio, pin );
-  level = luaL_checkinteger( L, 2 );
-  if ( level!=HIGH && level!=LOW )
-    return luaL_error( L, "wrong arg type" );
+  luaL_argcheck(L, level==HIGH || level==LOW, 2, "wrong level type" );
+
   platform_gpio_write(pin, level);
-  return 0;  
+  return 0;
 }
 
 #define DELAY_TABLE_MAX_LEN 256
-#define noInterrupts ets_intr_lock
-#define interrupts ets_intr_unlock
 #define delayMicroseconds os_delay_us
-#define DIRECT_WRITE(pin, level)    (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), level))
 // Lua: serout( pin, firstLevel, delay_table, [repeatNum] )
 // -- serout( pin, firstLevel, delay_table, [repeatNum] )
 // gpio.mode(1,gpio.OUTPUT,gpio.PULLUP)
@@ -160,62 +172,41 @@ static int lgpio_write( lua_State* L )
 // gpio.serout(1,1,{8,18},8) -- serial 30% pwm 38k, lasts 8 cycles
 static int lgpio_serout( lua_State* L )
 {
-  unsigned level;
-  unsigned pin;
-  unsigned table_len = 0;
-  unsigned repeat = 0;
-  int delay_table[DELAY_TABLE_MAX_LEN];
-  
-  pin = luaL_checkinteger( L, 1 );
-  MOD_CHECK_ID( gpio, pin );
-  level = luaL_checkinteger( L, 2 );
-  if ( level!=HIGH && level!=LOW )
-    return luaL_error( L, "wrong arg type" );
-  if( lua_istable( L, 3 ) )
-  {
-    table_len = lua_objlen( L, 3 );
-    if (table_len <= 0 || table_len>DELAY_TABLE_MAX_LEN)
-      return luaL_error( L, "wrong arg range" );
-    int i;
-    for( i = 0; i < table_len; i ++ )
-    {
-      lua_rawgeti( L, 3, i + 1 );
-      delay_table[i] = ( int )luaL_checkinteger( L, -1 );
-      lua_pop( L, 1 );
-      if( delay_table[i] < 0 || delay_table[i] > 1000000 )    // can not delay more than 1000000 us
-        return luaL_error( L, "delay must < 1000000 us" );
-    }
-  } else {
-    return luaL_error( L, "wrong arg range" );
-  } 
-
-  if(lua_isnumber(L, 4))
-    repeat = lua_tointeger( L, 4 );
-  if( repeat < 0 || repeat > DELAY_TABLE_MAX_LEN )
-    return luaL_error( L, "delay must < 256" );
-
-  if(repeat==0)
-    repeat = 1;
-  int j;
-  bool skip_loop = true;
-  do
-  {
-    if(skip_loop){    // skip the first loop.
-      skip_loop = false;
+  unsigned clocks_per_us = system_get_cpu_freq();
+  unsigned pin = luaL_checkinteger( L, 1 );
+  unsigned level = luaL_checkinteger( L, 2 );
+  unsigned repeats = luaL_optint( L, 4, 1 );
+  unsigned table_len, i, j;
+
+  luaL_argcheck(L, platform_gpio_exists(pin), 1, "Invalid pin");
+  luaL_argcheck(L, level==HIGH || level==LOW, 2, "Wrong arg type" );
+  luaL_argcheck(L, lua_istable( L, 3 ) &&
+                   ((table_len = lua_objlen( L, 3 )<DELAY_TABLE_MAX_LEN)), 3, "Invalid table" );
+  luaL_argcheck(L, repeats<256, 4, "repeats >= 256" );
+
+  uint32 *delay_table = luaM_newvector(L, table_len*repeats, uint32);
+  for( i = 1; i <= table_len; i++ )  {
+    lua_rawgeti( L, 3, i + 1 );
+    unsigned delay = (unsigned) luaL_checkinteger( L, -1 );
+    if (delay > 1000000) return luaL_error( L, "delay %u must be < 1,000,000 us", i );
+    delay_table[i-1] = delay;
+    lua_pop( L, 1 );
+  }
+
+  for( i = 0; i <= repeats; i++ )  {
+    if (!i)    // skip the first loop (presumably this is some form of icache priming??).
       continue;
-    }
-    for(j=0;j<table_len;j++){
-      noInterrupts();
-      // platform_gpio_write(pin, level);
-      DIRECT_WRITE(pin, level);
-      interrupts();
+
+    for( j = 0;j < table_len; j++ ){
+      /* Direct Write is a ROM function which already disables interrupts for the atomic bit */
+      GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), level);
       delayMicroseconds(delay_table[j]);
-      level=!level;
+      level = level==LOW ? HIGH : LOW;
     }
-    repeat--;
-  } while (repeat>0); 
+  }
 
-  return 0;  
+  luaM_freearray(L, delay_table, table_len, uint32);
+  return 0;
 }
 #undef DELAY_TABLE_MAX_LEN
 
@@ -229,12 +220,13 @@ static const LUA_REG_TYPE gpio_map[] = {
   { LSTRKEY( "trig" ),   LFUNCVAL( lgpio_trig ) },
   { LSTRKEY( "INT" ),    LNUMVAL( INTERRUPT ) },
 #endif
-  { LSTRKEY( "OUTPUT" ), LNUMVAL( OUTPUT ) },
-  { LSTRKEY( "INPUT" ),  LNUMVAL( INPUT ) },
-  { LSTRKEY( "HIGH" ),   LNUMVAL( HIGH ) },
-  { LSTRKEY( "LOW" ),    LNUMVAL( LOW ) },
-  { LSTRKEY( "FLOAT" ),  LNUMVAL( FLOAT ) },
-  { LSTRKEY( "PULLUP" ), LNUMVAL( PULLUP ) },
+  { LSTRKEY( "OUTPUT" ),    LNUMVAL( OUTPUT ) },
+  { LSTRKEY( "OPENDRAIN" ), LNUMVAL( OPENDRAIN ) },
+  { LSTRKEY( "INPUT" ),     LNUMVAL( INPUT ) },
+  { LSTRKEY( "HIGH" ),      LNUMVAL( HIGH ) },
+  { LSTRKEY( "LOW" ),       LNUMVAL( LOW ) },
+  { LSTRKEY( "FLOAT" ),     LNUMVAL( FLOAT ) },
+  { LSTRKEY( "PULLUP" ),    LNUMVAL( PULLUP ) },
   { LNILKEY, LNILVAL }
 };
 
@@ -244,7 +236,7 @@ int luaopen_gpio( lua_State *L ) {
   for(i=0;i<GPIO_PIN_NUM;i++){
     gpio_cb_ref[i] = LUA_NOREF;
   }
-  platform_gpio_init(gpio_intr_callback);
+  platform_gpio_init(task_get_id(gpio_intr_callback_task));
 #endif
   return 0;
 }

+ 228 - 0
app/modules/http.c

@@ -0,0 +1,228 @@
+/******************************************************************************
+ * HTTP module for NodeMCU
+ * vowstar@gmail.com
+ * 2015-12-29
+*******************************************************************************/
+#include "module.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "cpu_esp8266.h"
+#include "httpclient.h"
+
+static int http_callback_registry  = LUA_NOREF;
+
+static void http_callback( char * response, int http_status, char * full_response )
+{
+#if defined(HTTPCLIENT_DEBUG_ON)
+  c_printf( "http_status=%d\n", http_status );
+  if ( http_status != HTTP_STATUS_GENERIC_ERROR )
+  {
+    if (full_response != NULL) {
+      c_printf( "strlen(full_response)=%d\n", strlen( full_response ) );
+    }
+    c_printf( "response=%s<EOF>\n", response );
+  }
+#endif
+  if (http_callback_registry != LUA_NOREF)
+  {
+    lua_State *L = lua_getstate();
+
+    lua_rawgeti(L, LUA_REGISTRYINDEX, http_callback_registry);
+
+    lua_pushnumber(L, http_status);
+    if ( http_status != HTTP_STATUS_GENERIC_ERROR && response)
+    {
+      lua_pushstring(L, response);
+    }
+    else
+    {
+      lua_pushnil(L);
+    }
+    lua_call(L, 2, 0); // With 2 arguments and 0 result
+
+    luaL_unref(L, LUA_REGISTRYINDEX, http_callback_registry);
+    http_callback_registry = LUA_NOREF;
+  }
+}
+
+// Lua: http.request( url, method, header, body, function(status, reponse) end )
+static int http_lapi_request( lua_State *L )
+{
+  int length;
+  const char * url     = luaL_checklstring(L, 1, &length);
+  const char * method  = luaL_checklstring(L, 2, &length);
+  const char * headers = NULL;
+  const char * body    = NULL;
+
+  // Check parameter
+  if ((url == NULL) || (method == NULL))
+  {
+    return luaL_error( L, "wrong arg type" );
+  }
+
+  if (lua_isstring(L, 3))
+  {
+    headers = luaL_checklstring(L, 3, &length);
+  }
+  if (lua_isstring(L, 4))
+  {
+    body = luaL_checklstring(L, 4, &length);
+  }
+
+  if (lua_type(L, 5) == LUA_TFUNCTION || lua_type(L, 5) == LUA_TLIGHTFUNCTION) {
+    lua_pushvalue(L, 5);  // copy argument (func) to the top of stack
+    if (http_callback_registry != LUA_NOREF)
+      luaL_unref(L, LUA_REGISTRYINDEX, http_callback_registry);
+    http_callback_registry = luaL_ref(L, LUA_REGISTRYINDEX);
+  }
+
+  http_request(url, method, headers, body, http_callback);
+  return 0;
+}
+
+// Lua: http.post( url, header, body, function(status, reponse) end )
+static int http_lapi_post( lua_State *L )
+{
+  int length;
+  const char * url     = luaL_checklstring(L, 1, &length);
+  const char * headers = NULL;
+  const char * body    = NULL;
+
+  // Check parameter
+  if ((url == NULL))
+  {
+    return luaL_error( L, "wrong arg type" );
+  }
+
+  if (lua_isstring(L, 2))
+  {
+    headers = luaL_checklstring(L, 2, &length);
+  }
+  if (lua_isstring(L, 3))
+  {
+    body = luaL_checklstring(L, 3, &length);
+  }
+
+  if (lua_type(L, 4) == LUA_TFUNCTION || lua_type(L, 4) == LUA_TLIGHTFUNCTION) {
+    lua_pushvalue(L, 4);  // copy argument (func) to the top of stack
+    if (http_callback_registry != LUA_NOREF)
+      luaL_unref(L, LUA_REGISTRYINDEX, http_callback_registry);
+    http_callback_registry = luaL_ref(L, LUA_REGISTRYINDEX);
+  }
+
+  http_post(url, headers, body, http_callback);
+  return 0;
+}
+
+// Lua: http.put( url, header, body, function(status, reponse) end )
+static int http_lapi_put( lua_State *L )
+{
+  int length;
+  const char * url     = luaL_checklstring(L, 1, &length);
+  const char * headers = NULL;
+  const char * body    = NULL;
+
+  // Check parameter
+  if ((url == NULL))
+  {
+    return luaL_error( L, "wrong arg type" );
+  }
+
+  if (lua_isstring(L, 2))
+  {
+    headers = luaL_checklstring(L, 2, &length);
+  }
+  if (lua_isstring(L, 3))
+  {
+    body = luaL_checklstring(L, 3, &length);
+  }
+
+  if (lua_type(L, 4) == LUA_TFUNCTION || lua_type(L, 4) == LUA_TLIGHTFUNCTION) {
+    lua_pushvalue(L, 4);  // copy argument (func) to the top of stack
+    if (http_callback_registry != LUA_NOREF)
+      luaL_unref(L, LUA_REGISTRYINDEX, http_callback_registry);
+    http_callback_registry = luaL_ref(L, LUA_REGISTRYINDEX);
+  }
+
+  http_put(url, headers, body, http_callback);
+  return 0;
+}
+
+// Lua: http.delete( url, header, body, function(status, reponse) end )
+static int http_lapi_delete( lua_State *L )
+{
+  int length;
+  const char * url     = luaL_checklstring(L, 1, &length);
+  const char * headers = NULL;
+  const char * body    = NULL;
+
+  // Check parameter
+  if ((url == NULL))
+  {
+    return luaL_error( L, "wrong arg type" );
+  }
+
+  if (lua_isstring(L, 2))
+  {
+    headers = luaL_checklstring(L, 2, &length);
+  }
+  if (lua_isstring(L, 3))
+  {
+    body = luaL_checklstring(L, 3, &length);
+  }
+
+  if (lua_type(L, 4) == LUA_TFUNCTION || lua_type(L, 4) == LUA_TLIGHTFUNCTION) {
+    lua_pushvalue(L, 4);  // copy argument (func) to the top of stack
+    if (http_callback_registry != LUA_NOREF)
+      luaL_unref(L, LUA_REGISTRYINDEX, http_callback_registry);
+    http_callback_registry = luaL_ref(L, LUA_REGISTRYINDEX);
+  }
+
+  http_delete(url, headers, body, http_callback);
+  return 0;
+}
+
+// Lua: http.get( url, header, function(status, reponse) end )
+static int http_lapi_get( lua_State *L )
+{
+  int length;
+  const char * url     = luaL_checklstring(L, 1, &length);
+  const char * headers = NULL;
+
+  // Check parameter
+  if ((url == NULL))
+  {
+    return luaL_error( L, "wrong arg type" );
+  }
+
+  if (lua_isstring(L, 2))
+  {
+    headers = luaL_checklstring(L, 2, &length);
+  }
+
+  if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION) {
+    lua_pushvalue(L, 3);  // copy argument (func) to the top of stack
+    if (http_callback_registry != LUA_NOREF)
+      luaL_unref(L, LUA_REGISTRYINDEX, http_callback_registry);
+    http_callback_registry = luaL_ref(L, LUA_REGISTRYINDEX);
+  }
+
+  http_get(url, headers, http_callback);
+  return 0;
+}
+
+// Module function map
+static const LUA_REG_TYPE http_map[] = {
+  { LSTRKEY( "request" ),         LFUNCVAL( http_lapi_request ) },
+  { LSTRKEY( "post" ),            LFUNCVAL( http_lapi_post ) },
+  { LSTRKEY( "put" ),             LFUNCVAL( http_lapi_put ) },
+  { LSTRKEY( "delete" ),          LFUNCVAL( http_lapi_delete ) },
+  { LSTRKEY( "get" ),             LFUNCVAL( http_lapi_get ) },
+
+  { LSTRKEY( "OK" ),              LNUMVAL( 0 ) },
+  { LSTRKEY( "ERROR" ),           LNUMVAL( HTTP_STATUS_GENERIC_ERROR ) },
+  
+  { LNILKEY, LNILVAL }
+};
+
+NODEMCU_MODULE(HTTP, "http", http_map, NULL);

+ 5 - 0
app/modules/linit.c

@@ -57,7 +57,12 @@ extern const luaR_entry math_map[];
 BUILTIN_LIB(      MATH,      LUA_MATHLIBNAME,   math_map);
 #endif
 
+#ifdef LUA_CROSS_COMPILER
+const luaL_Reg lua_libs[] = {{NULL, NULL}};
+const luaR_table lua_rotable[] = {{NULL, NULL}};
+#else 
 extern const luaL_Reg lua_libs[];
+#endif
 
 void luaL_openlibs (lua_State *L) {
   const luaL_Reg *lib = lua_libs;

+ 97 - 0
app/modules/mdns.c

@@ -0,0 +1,97 @@
+// Module for access to the nodemcu_mdns functions
+
+#include "module.h"
+#include "lauxlib.h"
+
+#include "c_string.h"
+#include "c_stdlib.h"
+
+#include "c_types.h"
+#include "mem.h"
+#include "lwip/ip_addr.h"
+#include "nodemcu_mdns.h"
+#include "user_interface.h"
+
+//
+// mdns.close()
+// 
+static int mdns_close(lua_State *L)
+{
+  nodemcu_mdns_close();
+  return 0;
+}
+
+//
+// mdns.register(hostname [, { attributes} ])
+//
+static int mdns_register(lua_State *L)
+{
+  struct nodemcu_mdns_info info;
+
+  memset(&info, 0, sizeof(info));
+  
+  info.host_name = luaL_checkstring(L, 1);
+  info.service_name = "http";
+  info.service_port = 80;
+  info.host_desc = info.host_name;
+
+  if (lua_gettop(L) >= 2) {
+    luaL_checktype(L, 2, LUA_TTABLE);
+    lua_pushnil(L); // first key
+    int slot = 0;
+    while (lua_next(L, 2) != 0 && slot < sizeof(info.txt_data) / sizeof(info.txt_data[0])) {
+      luaL_checktype(L, -2, LUA_TSTRING);
+      const char *key = luaL_checkstring(L, -2);
+
+      if (c_strcmp(key, "port") == 0) {
+	info.service_port = luaL_checknumber(L, -1);
+      } else if (c_strcmp(key, "service") == 0) {
+	info.service_name = luaL_checkstring(L, -1);
+      } else if (c_strcmp(key, "description") == 0) {
+	info.host_desc = luaL_checkstring(L, -1);
+      } else {
+	int len = c_strlen(key) + 1;
+	const char *value = luaL_checkstring(L, -1);
+	char *p = alloca(len + c_strlen(value) + 1);
+	strcpy(p, key);
+	strcat(p, "=");
+	strcat(p, value);
+	info.txt_data[slot++] = p;
+      }
+      lua_pop(L, 1);
+    }
+  }
+
+
+  struct ip_info ipconfig;
+
+  uint8_t mode = wifi_get_opmode();
+
+  if (!wifi_get_ip_info((mode == 2) ? SOFTAP_IF : STATION_IF, &ipconfig) || !ipconfig.ip.addr) {
+    return luaL_error(L, "No network connection");
+  }
+
+  // Close up the old session (if any). This cannot fail
+  // so no chance of losing the memory in 'result'
+  
+  mdns_close(L);
+
+  // Save the result as it appears that nodemcu_mdns_init needs
+  // to have the data valid while it is running.
+
+  if (!nodemcu_mdns_init(&info)) {
+    mdns_close(L);
+    return luaL_error(L, "Unable to start mDns daemon");
+  }
+
+  return 0;
+}
+
+// Module function map
+static const LUA_REG_TYPE mdns_map[] = {
+  { LSTRKEY("register"),  LFUNCVAL(mdns_register)  },
+  { LSTRKEY("close"),     LFUNCVAL(mdns_close)     },
+  { LNILKEY, LNILVAL }
+};
+
+NODEMCU_MODULE(MDNS, "mdns", mdns_map, NULL);

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 410 - 203
app/modules/mqtt.c


+ 217 - 8
app/modules/net.c

@@ -3,6 +3,7 @@
 #include "module.h"
 #include "lauxlib.h"
 #include "platform.h"
+#include "lmem.h"
 
 #include "c_string.h"
 #include "c_stdlib.h"
@@ -13,18 +14,19 @@
 #include "espconn.h"
 #include "lwip/dns.h" 
 
-#ifdef CLIENT_SSL_ENABLE
-unsigned char *default_certificate;
-unsigned int default_certificate_len = 0;
-unsigned char *default_private_key;
-unsigned int default_private_key_len = 0;
-#endif
-
 #define TCP ESPCONN_TCP
 #define UDP ESPCONN_UDP
 
 static ip_addr_t host_ip; // for dns
 
+#ifdef HAVE_SSL_SERVER_CRT
+#include HAVE_SSL_SERVER_CRT
+#else
+__attribute__((section(".servercert.flash"))) unsigned char net_server_cert_area[INTERNAL_FLASH_SECTOR_SIZE];
+#endif
+
+__attribute__((section(".clientcert.flash"))) unsigned char net_client_cert_area[INTERNAL_FLASH_SECTOR_SIZE];
+
 #if 0
 static int expose_array(lua_State* L, char *array, unsigned short len);
 #endif
@@ -594,6 +596,7 @@ static void socket_connect(struct espconn *pesp_conn)
   {
 #ifdef CLIENT_SSL_ENABLE
     if(nud->secure){
+      espconn_secure_set_size(ESPCONN_CLIENT, 5120); /* set SSL buffer size */
       espconn_secure_connect(pesp_conn);
     }
     else
@@ -610,7 +613,7 @@ static void socket_connect(struct espconn *pesp_conn)
 }
 
 static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg);
-static dns_reconn_count = 0;
+static int dns_reconn_count = 0;
 static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
 {
   NODE_DBG("socket_dns_found is called.\n");
@@ -1431,6 +1434,201 @@ static int net_multicastLeave( lua_State* L )
 	return net_multicastJoinLeave(L,0);
 }
 
+// Returns NULL on success, error message otherwise
+static const char *append_pem_blob(const char *pem, const char *type, uint8_t **buffer_p, uint8_t *buffer_limit, const char *name) {
+  char unb64[256];
+  memset(unb64, 0xff, sizeof(unb64));
+  int i;
+  for (i = 0; i < 64; i++) {
+    unb64["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
+  }
+
+  if (!pem) {
+    return "No PEM blob";
+  }
+
+  // Scan for -----BEGIN CERT
+  pem = strstr(pem, "-----BEGIN ");
+  if (!pem) {
+    return "No PEM header";
+  }
+
+  if (strncmp(pem + 11, type, strlen(type))) {
+    return "Wrong PEM type";
+  }
+
+  pem = strchr(pem, '\n');
+  if (!pem) {
+    return "Incorrect PEM format";
+  }
+  //
+  // Base64 encoded data starts here
+  // Get all the base64 data into a single buffer....
+  // We will use the back end of the buffer....
+  //
+
+  uint8_t *buffer = *buffer_p;
+
+  uint8_t *dest = buffer + 32 + 2;  // Leave space for name and length
+  int bitcount = 0;
+  int accumulator = 0;
+  for (; *pem && dest < buffer_limit; pem++) {
+    int val = unb64[*(uint8_t*) pem];
+    if (val & 0xC0) {
+      // not a base64 character
+      if (isspace(*(uint8_t*) pem)) {
+	continue;
+      }
+      if (*pem == '=') {
+	// just ignore -- at the end
+	bitcount = 0;
+	continue;
+      }
+      if (*pem == '-') {
+	break;
+      }
+      return "Invalid character in PEM";
+    } else {
+      bitcount += 6;
+      accumulator = (accumulator << 6) + val;
+      if (bitcount >= 8) {
+	bitcount -= 8;
+	*dest++ = accumulator >> bitcount;
+      }
+    }
+  }
+  if (dest >= buffer_limit || strncmp(pem, "-----END ", 9) || strncmp(pem + 9, type, strlen(type)) || bitcount) {
+    return "Invalid PEM format data";
+  }
+  size_t len = dest - (buffer + 32 + 2);
+
+  memset(buffer, 0, 32);
+  strcpy(buffer, name);
+  buffer[32] = len & 0xff;
+  buffer[33] = (len >> 8) & 0xff;
+  *buffer_p = dest;
+  return NULL;
+}
+
+static const char *fill_page_with_pem(lua_State *L, const unsigned char *flash_memory, int flash_offset, const char **types, const char **names) 
+{
+  uint8_t  *buffer = luaM_malloc(L, INTERNAL_FLASH_SECTOR_SIZE);
+  uint8_t  *buffer_base = buffer;
+  uint8_t  *buffer_limit = buffer + INTERNAL_FLASH_SECTOR_SIZE;
+
+  int argno;
+
+  for (argno = 1; argno <= lua_gettop(L) && types[argno - 1]; argno++) {
+    const char *pem = lua_tostring(L, argno);
+
+    const char *error = append_pem_blob(pem, types[argno - 1], &buffer, buffer_limit, names[argno - 1]);
+    if (error) {
+      luaM_free(L, buffer_base);
+      return error;
+    }
+  }
+
+  memset(buffer, 0xff, buffer_limit - buffer);
+
+  // Lets see if it matches what is already there....
+  if (c_memcmp(buffer_base, flash_memory, INTERNAL_FLASH_SECTOR_SIZE) != 0) {
+    // Starts being dangerous
+    if (platform_flash_erase_sector(flash_offset / INTERNAL_FLASH_SECTOR_SIZE) != PLATFORM_OK) {
+      luaM_free(L, buffer_base);
+      return "Failed to erase sector";
+    }
+    if (platform_s_flash_write(buffer_base, flash_offset, INTERNAL_FLASH_SECTOR_SIZE) != INTERNAL_FLASH_SECTOR_SIZE) {
+      luaM_free(L, buffer_base);
+      return "Failed to write sector";
+    }
+    // ends being dangerous
+  }
+
+  luaM_free(L, buffer_base);
+
+  return NULL;
+}
+
+// Lua: net.cert.auth(true / false | PEM data [, PEM data] )
+static int net_cert_auth(lua_State *L)
+{
+  int enable;
+
+  uint32_t flash_offset = platform_flash_mapped2phys((uint32_t) &net_client_cert_area[0]);
+  if ((flash_offset & 0xfff) || flash_offset > 0xff000 || INTERNAL_FLASH_SECTOR_SIZE != 0x1000) {
+    // THis should never happen
+    return luaL_error( L, "bad offset" );
+  }
+
+  if (lua_type(L, 1) == LUA_TSTRING) {
+    const char *types[3] = { "CERTIFICATE", "RSA PRIVATE KEY", NULL };
+    const char *names[2] = { "certificate", "private_key" };
+    const char *error = fill_page_with_pem(L, &net_client_cert_area[0], flash_offset, types, names);
+    if (error) {
+      return luaL_error(L, error);
+    }
+
+    enable = 1;
+  } else {
+    enable = lua_toboolean(L, 1);
+  }
+
+  bool rc;
+
+  if (enable) {
+    // See if there is a cert there
+    if (net_client_cert_area[0] == 0x00 || net_client_cert_area[0] == 0xff) {
+      return luaL_error( L, "no certificates found" );
+    }
+    rc = espconn_secure_cert_req_enable(1, flash_offset / INTERNAL_FLASH_SECTOR_SIZE);
+  } else {
+    rc = espconn_secure_cert_req_disable(1);
+  }
+
+  lua_pushboolean(L, rc);
+  return 1;
+}
+
+// Lua: net.cert.verify(true / false | PEM data [, PEM data] )
+static int net_cert_verify(lua_State *L)
+{
+  int enable;
+
+  uint32_t flash_offset = platform_flash_mapped2phys((uint32_t) &net_server_cert_area[0]);
+  if ((flash_offset & 0xfff) || flash_offset > 0xff000 || INTERNAL_FLASH_SECTOR_SIZE != 0x1000) {
+    // THis should never happen
+    return luaL_error( L, "bad offset" );
+  }
+
+  if (lua_type(L, 1) == LUA_TSTRING) {
+    const char *types[2] = { "CERTIFICATE", NULL };
+    const char *names[1] = { "certificate" };
+
+    const char *error = fill_page_with_pem(L, &net_server_cert_area[0], flash_offset, types, names);
+    if (error) {
+      return luaL_error(L, error);
+    }
+
+    enable = 1;
+  } else {
+    enable = lua_toboolean(L, 1);
+  }
+
+  bool rc;
+
+  if (enable) {
+    // See if there is a cert there
+    if (net_server_cert_area[0] == 0x00 || net_server_cert_area[0] == 0xff) {
+      return luaL_error( L, "no certificates found" );
+    }
+    rc = espconn_secure_ca_enable(1, flash_offset / INTERNAL_FLASH_SECTOR_SIZE);
+  } else {
+    rc = espconn_secure_ca_disable(1);
+  }
+
+  lua_pushboolean(L, rc);
+  return 1;
+}
 
 // Lua: s = net.dns.setdnsserver(ip_addr, [index])
 static int net_setdnsserver( lua_State* L )
@@ -1538,6 +1736,14 @@ static const LUA_REG_TYPE net_array_map[] = {
 };
 #endif
 
+static const LUA_REG_TYPE net_cert_map[] = {
+  { LSTRKEY( "verify" ), 	LFUNCVAL( net_cert_verify ) },  
+#ifdef CLIENT_SSL_CERT_AUTH_ENABLE
+  { LSTRKEY( "auth" ),		LFUNCVAL( net_cert_auth ) }, 
+#endif
+  { LNILKEY, LNILVAL }
+};
+
 static const LUA_REG_TYPE net_dns_map[] = {
   { LSTRKEY( "setdnsserver" ), LFUNCVAL( net_setdnsserver ) },  
   { LSTRKEY( "getdnsserver" ), LFUNCVAL( net_getdnsserver ) }, 
@@ -1551,6 +1757,9 @@ static const LUA_REG_TYPE net_map[] = {
   { LSTRKEY( "multicastJoin"),     LFUNCVAL( net_multicastJoin ) },
   { LSTRKEY( "multicastLeave"),    LFUNCVAL( net_multicastLeave ) },
   { LSTRKEY( "dns" ),              LROVAL( net_dns_map ) },
+#ifdef CLIENT_SSL_ENABLE
+  { LSTRKEY( "cert" ),             LROVAL(net_cert_map) },
+#endif
   { LSTRKEY( "TCP" ),              LNUMVAL( TCP ) },
   { LSTRKEY( "UDP" ),              LNUMVAL( UDP ) },
   { LSTRKEY( "__metatable" ),      LROVAL( net_map ) },

+ 117 - 26
app/modules/node.c

@@ -9,6 +9,7 @@
 #include "lmem.h"
 #include "lobject.h"
 #include "lstate.h"
+#include "legc.h"
 
 #include "lopcodes.h"
 #include "lstring.h"
@@ -18,13 +19,14 @@
 #include "lrodefs.h"
 
 #include "c_types.h"
-#include "romfs.h"
 #include "c_string.h"
 #include "driver/uart.h"
 #include "user_interface.h"
 #include "flash_api.h"
 #include "flash_fs.h"
 #include "user_version.h"
+#include "rom.h"
+#include "task/task.h"
 
 #define CPU80MHZ 80
 #define CPU160MHZ 160
@@ -39,7 +41,8 @@ static int node_restart( lua_State* L )
 // Lua: dsleep( us, option )
 static int node_deepsleep( lua_State* L )
 {
-  s32 us, option;
+  uint32 us;
+  uint8 option;
   //us = luaL_checkinteger( L, 1 );
   // Set deleep option, skip if nil
   if ( lua_isnumber(L, 2) )
@@ -48,12 +51,12 @@ static int node_deepsleep( lua_State* L )
     if ( option < 0 || option > 4)
       return luaL_error( L, "wrong arg range" );
     else
-      deep_sleep_set_option( option );
+      system_deep_sleep_set_option( option );
   }
   // Set deleep time, skip if nil
   if ( lua_isnumber(L, 1) )
   {
-    us = lua_tointeger(L, 1);
+    us = luaL_checknumber(L, 1);
     // if ( us <= 0 )
     if ( us < 0 )
       return luaL_error( L, "wrong arg range" );
@@ -143,8 +146,6 @@ static int node_heap( lua_State* L )
   return 1;
 }
 
-static lua_State *gL = NULL;
-
 #ifdef DEVKIT_VERSION_0_9
 static int led_high_count = LED_HIGH_COUNT_DEFAULT;
 static int led_low_count = LED_LOW_COUNT_DEFAULT;
@@ -172,27 +173,25 @@ static void default_short_press(void *arg) {
 }
 
 static void key_long_press(void *arg) {
+  lua_State *L = lua_getstate();
   NODE_DBG("key_long_press is called.\n");
   if (long_key_ref == LUA_NOREF) {
     default_long_press(arg);
     return;
   }
-  if (!gL)
-    return;
-  lua_rawgeti(gL, LUA_REGISTRYINDEX, long_key_ref);
-  lua_call(gL, 0, 0);
+  lua_rawgeti(L, LUA_REGISTRYINDEX, long_key_ref);
+  lua_call(L, 0, 0);
 }
 
 static void key_short_press(void *arg) {
+  lua_State *L = lua_getstate();
   NODE_DBG("key_short_press is called.\n");
   if (short_key_ref == LUA_NOREF) {
     default_short_press(arg);
     return;
   }
-  if (!gL)
-    return;
-  lua_rawgeti(gL, LUA_REGISTRYINDEX, short_key_ref);
-  lua_call(gL, 0, 0);
+  lua_rawgeti(L, LUA_REGISTRYINDEX, short_key_ref);
+  lua_call(L, 0, 0);
 }
 
 static void update_key_led (void *p)
@@ -284,7 +283,6 @@ static int node_key( lua_State* L )
   } else {
     ref = &short_key_ref;
   }
-  gL = L;
   // luaL_checkanyfunction(L, 2);
   if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION) {
     lua_pushvalue(L, 2);  // copy argument (func) to the top of stack
@@ -303,6 +301,7 @@ static int node_key( lua_State* L )
 #endif
 
 extern lua_Load gLoad;
+extern bool user_process_input(bool force);
 // Lua: input("string")
 static int node_input( lua_State* L )
 {
@@ -319,7 +318,7 @@ static int node_input( lua_State* L )
       NODE_DBG("Get command:\n");
       NODE_DBG(load->line); // buggy here
       NODE_DBG("\nResult(if any):\n");
-      system_os_post (LUA_TASK_PRIO, LUA_PROCESS_LINE_SIG, 0);
+      user_process_input(true);
     }
   }
   return 0;
@@ -328,12 +327,13 @@ static int node_input( lua_State* L )
 static int output_redir_ref = LUA_NOREF;
 static int serial_debug = 1;
 void output_redirect(const char *str) {
+  lua_State *L = lua_getstate();
   // if(c_strlen(str)>=TX_BUFF_SIZE){
   //   NODE_ERR("output too long.\n");
   //   return;
   // }
 
-  if (output_redir_ref == LUA_NOREF || !gL) {
+  if (output_redir_ref == LUA_NOREF || !L) {
     uart0_sendStr(str);
     return;
   }
@@ -342,15 +342,14 @@ void output_redirect(const char *str) {
     uart0_sendStr(str);
   }
 
-  lua_rawgeti(gL, LUA_REGISTRYINDEX, output_redir_ref);
-  lua_pushstring(gL, str);
-  lua_call(gL, 1, 0);   // this call back function should never user output.
+  lua_rawgeti(L, LUA_REGISTRYINDEX, output_redir_ref);
+  lua_pushstring(L, str);
+  lua_call(L, 1, 0);   // this call back function should never user output.
 }
 
 // Lua: output(function(c), debug)
 static int node_output( lua_State* L )
 {
-  gL = L;
   // luaL_checkanyfunction(L, 1);
   if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION) {
     lua_pushvalue(L, 1);  // copy argument (func) to the top of stack
@@ -450,6 +449,42 @@ static int node_compile( lua_State* L )
   return 0;
 }
 
+// Task callback handler for node.task.post()
+static task_handle_t do_node_task_handle;
+static void do_node_task (task_param_t task_fn_ref, uint8_t prio)
+{
+  lua_State* L = lua_getstate();
+  lua_rawgeti(L, LUA_REGISTRYINDEX, (int)task_fn_ref);
+  luaL_unref(L, LUA_REGISTRYINDEX, (int)task_fn_ref);
+  lua_pushinteger(L, prio);
+  lua_call(L, 1, 0);
+}
+
+// Lua: node.task.post([priority],task_cb) -- schedule a task for execution next
+static int node_task_post( lua_State* L )
+{
+  int n = 1, Ltype = lua_type(L, 1);
+  unsigned priority = TASK_PRIORITY_MEDIUM;
+  if (Ltype == LUA_TNUMBER) {
+    priority = (unsigned) luaL_checkint(L, 1);
+    luaL_argcheck(L, priority <= TASK_PRIORITY_HIGH, 1, "invalid  priority");
+    Ltype = lua_type(L, ++n);
+  }
+  luaL_argcheck(L, Ltype == LUA_TFUNCTION || Ltype == LUA_TLIGHTFUNCTION, n, "invalid function");
+  lua_pushvalue(L, n);
+
+  int task_fn_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+  if (!do_node_task_handle)  // bind the task handle to do_node_task on 1st call
+    do_node_task_handle = task_get_id(do_node_task);
+
+  if(!task_post(priority, do_node_task_handle, (task_param_t)task_fn_ref)) {
+    luaL_unref(L, LUA_REGISTRYINDEX, task_fn_ref);
+    luaL_error(L, "Task queue overflow. Task not posted");
+  }
+  return 0;
+}
+
 // Lua: setcpufreq(mhz)
 // mhz is either CPU80MHZ od CPU160MHZ
 static int node_setcpufreq(lua_State* L)
@@ -468,18 +503,24 @@ static int node_setcpufreq(lua_State* L)
   return 1;
 }
 
-// Lua: code = bootreason()
+// Lua: code, reason [, exccause, epc1, epc2, epc3, excvaddr, depc ] = bootreason()
 static int node_bootreason (lua_State *L)
 {
-  lua_pushnumber (L, rtc_get_reset_reason ());
-  return 1;
+  const struct rst_info *ri = system_get_rst_info ();
+  uint32_t arr[8] = {
+    rtc_get_reset_reason(),
+    ri->reason,
+    ri->exccause, ri->epc1, ri->epc2, ri->epc3, ri->excvaddr, ri->depc
+  };
+  int i, n = ((ri->reason != REASON_EXCEPTION_RST) ? 2 : 8);
+  for (i = 0; i < n; ++i)
+    lua_pushinteger (L, arr[i]);
+  return n;
 }
 
 // Lua: restore()
 static int node_restore (lua_State *L)
 {
-  flash_init_data_default();
-  flash_init_data_blank();
   system_restore();
   return 0;
 }
@@ -547,7 +588,52 @@ static int node_stripdebug (lua_State *L) {
 }
 #endif
 
+// Lua: node.egc.setmode( mode, [param])
+// where the mode is one of the node.egc constants  NOT_ACTIVE , ON_ALLOC_FAILURE,
+// ON_MEM_LIMIT, ALWAYS.  In the case of ON_MEM_LIMIT an integer parameter is reqired
+// See legc.h and lecg.c.
+static int node_egc_setmode(lua_State* L) {
+  unsigned mode  = luaL_checkinteger(L, 1);
+  unsigned limit = luaL_optinteger (L, 2, 0);
+
+  luaL_argcheck(L, mode <= (EGC_ON_ALLOC_FAILURE | EGC_ON_MEM_LIMIT | EGC_ALWAYS), 1, "invalid mode");
+  luaL_argcheck(L, !(mode & EGC_ON_MEM_LIMIT) || limit>0, 1, "limit must be non-zero");
+
+  legc_set_mode( L, mode, limit );
+  return 0;
+}
+//
+// Lua: osprint(true/false)
+// Allows you to turn on the native Espressif SDK printing
+static int node_osprint( lua_State* L )
+{
+  if (lua_toboolean(L, 1)) {
+    system_set_os_print(1);
+  } else {
+    system_set_os_print(0);
+  }
+
+  return 0;  
+}
+
 // Module function map
+
+static const LUA_REG_TYPE node_egc_map[] = {
+  { LSTRKEY( "setmode" ),           LFUNCVAL( node_egc_setmode ) },
+  { LSTRKEY( "NOT_ACTIVE" ),        LNUMVAL( EGC_NOT_ACTIVE ) },
+  { LSTRKEY( "ON_ALLOC_FAILURE" ),  LNUMVAL( EGC_ON_ALLOC_FAILURE ) },
+  { LSTRKEY( "ON_MEM_LIMIT" ),      LNUMVAL( EGC_ON_MEM_LIMIT ) },
+  { LSTRKEY( "ALWAYS" ),            LNUMVAL( EGC_ALWAYS ) },
+  { LNILKEY, LNILVAL }
+};
+static const LUA_REG_TYPE node_task_map[] = {
+  { LSTRKEY( "post" ),            LFUNCVAL( node_task_post ) },
+  { LSTRKEY( "LOW_PRIORITY" ),    LNUMVAL( TASK_PRIORITY_LOW ) },
+  { LSTRKEY( "MEDIUM_PRIORITY" ), LNUMVAL( TASK_PRIORITY_MEDIUM ) },
+  { LSTRKEY( "HIGH_PRIORITY" ),   LNUMVAL( TASK_PRIORITY_HIGH ) },
+  { LNILKEY, LNILVAL }
+};
+
 static const LUA_REG_TYPE node_map[] =
 {
   { LSTRKEY( "restart" ), LFUNCVAL( node_restart ) },
@@ -574,6 +660,11 @@ static const LUA_REG_TYPE node_map[] =
 #ifdef LUA_OPTIMIZE_DEBUG
   { LSTRKEY( "stripdebug" ), LFUNCVAL( node_stripdebug ) },
 #endif
+  { LSTRKEY( "egc" ),  LROVAL( node_egc_map ) },
+  { LSTRKEY( "task" ), LROVAL( node_task_map ) },
+#ifdef DEVELOPMENT_TOOLS
+  { LSTRKEY( "osprint" ), LFUNCVAL( node_osprint ) },
+#endif
 
 // Combined to dsleep(us, option)
 // { LSTRKEY( "dsleepsetoption" ), LFUNCVAL( node_deepsleep_setoption) },

+ 147 - 0
app/modules/perf.c

@@ -0,0 +1,147 @@
+//
+// This module allows performance monitoring by looking at
+// the PC at regular intervals and building a histogram
+// 
+// perf.start(start, end, nbins[, pc offset on stack])
+// perf.stop()  -> total sample, samples outside range, table { addr -> count , .. }
+
+
+#include "ets_sys.h"
+#include "os_type.h"
+#include "osapi.h"
+#include "c_stdlib.h"
+
+#include "module.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "hw_timer.h"
+#include "cpu_esp8266.h"
+
+typedef struct {
+  int ref;
+  uint32_t start;
+  uint32_t bucket_shift;
+  uint32_t bucket_count;
+  uint32_t total_samples;
+  uint32_t outside_samples;
+  uint32_t pc_offset;
+  uint32_t bucket[1];
+} DATA;
+
+static DATA *data;
+extern char _flash_used_end[];
+
+#define TIMER_OWNER ((os_param_t) 'p')
+
+static void ICACHE_RAM_ATTR hw_timer_cb(os_param_t p)
+{
+  (void) p;
+  uint32_t stackaddr;
+
+  if (data) {
+    uint32_t pc = *(&stackaddr + data->pc_offset);
+
+    uint32_t bucket_number = (pc - data->start) >> data->bucket_shift;
+    if (bucket_number < data->bucket_count) {
+      data->bucket[bucket_number]++;
+    } else {
+      data->outside_samples++;
+    }
+    data->total_samples++;
+  }
+}
+
+static int perf_start(lua_State *L)
+{
+  uint32_t start = luaL_optinteger(L, 1, 0x40000000);
+  uint32_t end = luaL_optinteger(L, 2, (uint32_t) _flash_used_end);
+  uint32_t bins = luaL_optinteger(L, 3, 1024);
+
+  if (end <= start) {
+    luaL_error(L, "end must be larger than start");
+  }
+
+  uint32_t binsize = (end - start + bins - 1) / bins;
+
+  // Round up to a power of two
+  int shift;
+  binsize = binsize - 1;
+  for (shift = 0; binsize > 0; shift++) {
+    binsize >>= 1;
+  }
+
+  bins = (end - start + (1 << shift) - 1) / (1 << shift);
+
+  int pc_offset = 20;  // This appears to be correct
+  if (lua_gettop(L) >= 4) {
+    pc_offset = luaL_checkinteger(L, 4);
+  }
+
+  size_t data_size = sizeof(DATA) + bins * sizeof(uint32_t);
+  DATA *d = (DATA *) lua_newuserdata(L, data_size);
+  memset(d, 0, data_size);
+  d->ref = luaL_ref(L, LUA_REGISTRYINDEX);
+  d->start = start;
+  d->bucket_shift = shift;
+  d->bucket_count = bins;
+  d->pc_offset = pc_offset;	
+
+  if (data) {
+    lua_unref(L, data->ref);
+  }
+
+  data = d;
+
+  // Start the timer
+  if (!platform_hw_timer_init(TIMER_OWNER, NMI_SOURCE, TRUE)) {
+    // Failed to init the timer
+    data = NULL;
+    lua_unref(L, d->ref);
+    luaL_error(L, "Unable to initialize timer");
+  }
+
+  platform_hw_timer_set_func(TIMER_OWNER, hw_timer_cb, 0);
+  platform_hw_timer_arm_us(TIMER_OWNER, 50);
+
+  return 0;
+}
+
+static int perf_stop(lua_State *L)
+{
+  if (!data) {
+    return 0;
+  }
+
+  // stop the timer
+  platform_hw_timer_close(TIMER_OWNER);
+
+  DATA *d = data;
+  data = NULL;
+
+  lua_pushnumber(L, d->total_samples);
+  lua_pushnumber(L, d->outside_samples);
+  lua_newtable(L);
+  int i;
+  uint32_t addr = d->start;
+  for (i = 0; i < d->bucket_count; i++, addr += (1 << d->bucket_shift)) {
+    if (d->bucket[i]) {
+      lua_pushnumber(L, addr);
+      lua_pushnumber(L, d->bucket[i]);
+      lua_settable(L, -3);
+    }
+  }
+
+  lua_pushnumber(L, 1 << d->bucket_shift);
+
+  lua_unref(L, d->ref);
+
+  return 4;
+}
+
+static const LUA_REG_TYPE perf_map[] = {
+  { LSTRKEY( "start" ),   LFUNCVAL( perf_start ) },
+  { LSTRKEY( "stop" ),    LFUNCVAL( perf_stop ) },
+  { LNILKEY, LNILVAL }
+};
+
+NODEMCU_MODULE(PERF, "perf", perf_map, NULL);

+ 9 - 2
app/modules/pwm.c

@@ -47,7 +47,9 @@ static int lpwm_start( lua_State* L )
   unsigned id;
   id = luaL_checkinteger( L, 1 );
   MOD_CHECK_ID( pwm, id );
-  platform_pwm_start( id );
+  if (!platform_pwm_start( id )) {
+    return luaL_error(L, "Unable to start PWM output");
+  }
   return 0;  
 }
 
@@ -120,6 +122,11 @@ static int lpwm_getduty( lua_State* L )
   return 1;
 }
 
+int lpwm_open( lua_State *L ) {
+  platform_pwm_init();
+  return 0;
+}
+
 // Module function map
 static const LUA_REG_TYPE pwm_map[] = {
   { LSTRKEY( "setup" ),    LFUNCVAL( lpwm_setup ) },
@@ -133,4 +140,4 @@ static const LUA_REG_TYPE pwm_map[] = {
   { LNILKEY, LNILVAL }
 };
 
-NODEMCU_MODULE(PWM, "pwm", pwm_map, NULL);
+NODEMCU_MODULE(PWM, "pwm", pwm_map, lpwm_open);

+ 1 - 0
app/modules/rc.c

@@ -1,6 +1,7 @@
 #include "module.h"
 #include "lauxlib.h"
 #include "platform.h"
+#include "rom.h"
 //#include "driver/easygpio.h"
 //static Ping_Data pingA;
 #define defPulseLen 185

+ 405 - 0
app/modules/rotary.c

@@ -0,0 +1,405 @@
+/*
+ * Module for interfacing with cheap rotary switches that
+ * are much used in the automtive industry as the cntrols for 
+ * CD players and the like.
+ *
+ * Philip Gladstone, N1DQ
+ */
+
+#include "module.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "c_types.h"
+#include "user_interface.h"
+#include "driver/rotary.h"
+#include "../libc/c_stdlib.h"
+
+#define MASK(x)		(1 << ROTARY_ ## x ## _INDEX)
+
+#define ROTARY_PRESS_INDEX	0
+#define ROTARY_LONGPRESS_INDEX	1
+#define ROTARY_RELEASE_INDEX	2
+#define ROTARY_TURN_INDEX	3
+#define ROTARY_CLICK_INDEX	4
+#define ROTARY_DBLCLICK_INDEX	5
+
+#define ROTARY_ALL		0x3f
+
+#define LONGPRESS_DELAY_US 	500000
+#define CLICK_DELAY_US 		500000
+
+#define CALLBACK_COUNT	6
+
+#ifdef LUA_USE_MODULES_ROTARY
+#if !defined(GPIO_INTERRUPT_ENABLE) || !defined(GPIO_INTERRUPT_HOOK_ENABLE)
+#error Must have GPIO_INTERRUPT and GPIO_INTERRUPT_HOOK if using ROTARY module
+#endif
+#endif
+
+typedef struct {
+  int lastpos;
+  int last_recent_event_was_press : 1;
+  int last_recent_event_was_release : 1;
+  int timer_running : 1;
+  int possible_dbl_click : 1;
+  uint8_t id;
+  int click_delay_us;
+  int longpress_delay_us;
+  uint32_t last_event_time;
+  int callback[CALLBACK_COUNT];
+  ETSTimer timer;
+} DATA;
+
+static DATA *data[ROTARY_CHANNEL_COUNT];
+static task_handle_t tasknumber;
+static void lrotary_timer_done(void *param);
+static void lrotary_check_timer(DATA *d, uint32_t time_us, bool dotimer);
+
+static void callback_free_one(lua_State *L, int *cb_ptr) 
+{
+  if (*cb_ptr != LUA_NOREF) {
+    luaL_unref(L, LUA_REGISTRYINDEX, *cb_ptr);
+    *cb_ptr = LUA_NOREF;
+  }
+}
+
+static void callback_free(lua_State* L, unsigned int id, int mask) 
+{
+  DATA *d = data[id];
+
+  if (d) {
+    int i;
+    for (i = 0; i < CALLBACK_COUNT; i++) {
+      if (mask & (1 << i)) {
+	callback_free_one(L, &d->callback[i]);
+      }
+    }
+  }
+}
+
+static int callback_setOne(lua_State* L, int *cb_ptr, int arg_number) 
+{
+  if (lua_type(L, arg_number) == LUA_TFUNCTION || lua_type(L, arg_number) == LUA_TLIGHTFUNCTION) {
+    lua_pushvalue(L, arg_number);  // copy argument (func) to the top of stack
+    callback_free_one(L, cb_ptr);
+    *cb_ptr = luaL_ref(L, LUA_REGISTRYINDEX);
+    return 0;
+  }
+
+  return -1;
+}
+
+static int callback_set(lua_State* L, int id, int mask, int arg_number) 
+{
+  DATA *d = data[id];
+  int result = 0;
+
+  int i;
+  for (i = 0; i < CALLBACK_COUNT; i++) {
+    if (mask & (1 << i)) {
+      result |= callback_setOne(L, &d->callback[i], arg_number);
+    }
+  }
+
+  return result;
+}
+
+static void callback_callOne(lua_State* L, int cb, int mask, int arg, uint32_t time) 
+{
+  if (cb != LUA_NOREF) {
+    lua_rawgeti(L, LUA_REGISTRYINDEX, cb);
+
+    lua_pushinteger(L, mask);
+    lua_pushinteger(L, arg);
+    lua_pushinteger(L, time);
+
+    lua_call(L, 3, 0);
+  }
+}
+
+static void callback_call(lua_State* L, DATA *d, int cbnum, int arg, uint32_t time) 
+{
+  if (d) {
+    callback_callOne(L, d->callback[cbnum], 1 << cbnum, arg, time);
+  }
+}
+
+int platform_rotary_exists( unsigned int id )
+{
+  return (id < ROTARY_CHANNEL_COUNT);
+}
+
+// Lua: setup(id, phase_a, phase_b [, press])
+static int lrotary_setup( lua_State* L )
+{
+  unsigned int id;
+  
+  id = luaL_checkinteger( L, 1 );
+  MOD_CHECK_ID( rotary, id );
+
+  if (rotary_close(id)) {
+    return luaL_error( L, "Unable to close switch." );
+  }
+  callback_free(L, id, ROTARY_ALL);
+
+  if (!data[id]) {
+    data[id] = (DATA *) c_zalloc(sizeof(DATA));
+    if (!data[id]) {
+      return -1;
+    } 
+  }
+
+  DATA *d = data[id];
+  memset(d, 0, sizeof(*d));
+
+  os_timer_setfn(&d->timer, lrotary_timer_done, (void *) d);
+  
+  int i;
+  for (i = 0; i < CALLBACK_COUNT; i++) {
+    d->callback[i] = LUA_NOREF;
+  }
+
+  d->click_delay_us = CLICK_DELAY_US;
+  d->longpress_delay_us = LONGPRESS_DELAY_US;
+
+  int phase_a = luaL_checkinteger(L, 2);
+  luaL_argcheck(L, platform_gpio_exists(phase_a) && phase_a > 0, 2, "Invalid pin");
+  int phase_b = luaL_checkinteger(L, 3);
+  luaL_argcheck(L, platform_gpio_exists(phase_b) && phase_b > 0, 3, "Invalid pin");
+  int press;
+  if (lua_gettop(L) >= 4) {
+    press = luaL_checkinteger(L, 4);
+    luaL_argcheck(L, platform_gpio_exists(press) && press > 0, 4, "Invalid pin");
+  } else {
+    press = -1;
+  }
+
+  if (lua_gettop(L) >= 5) {
+    d->longpress_delay_us = 1000 * luaL_checkinteger(L, 5);
+    luaL_argcheck(L, d->longpress_delay_us > 0, 5, "Invalid timeout");
+  }
+
+  if (lua_gettop(L) >= 6) {
+    d->click_delay_us = 1000 * luaL_checkinteger(L, 6);
+    luaL_argcheck(L, d->click_delay_us > 0, 6, "Invalid timeout");
+  }
+
+  if (rotary_setup(id, phase_a, phase_b, press, tasknumber)) {
+    return luaL_error(L, "Unable to setup rotary switch.");
+  }
+  return 0;  
+}
+
+// Lua: close( id )
+static int lrotary_close( lua_State* L )
+{
+  unsigned int id;
+  
+  id = luaL_checkinteger( L, 1 );
+  MOD_CHECK_ID( rotary, id );
+  callback_free(L, id, ROTARY_ALL);
+
+  DATA *d = data[id];
+  if (d) {
+    data[id] = NULL;
+    c_free(d);
+  }
+
+  if (rotary_close( id )) {
+    return luaL_error( L, "Unable to close switch." );
+  }
+  return 0;  
+}
+
+// Lua: on( id, mask[, cb] )
+static int lrotary_on( lua_State* L )
+{
+  unsigned int id;
+  id = luaL_checkinteger( L, 1 );
+  MOD_CHECK_ID( rotary, id );
+
+  int mask = luaL_checkinteger(L, 2);
+
+  if (lua_gettop(L) >= 3) {
+    if (callback_set(L, id, mask, 3)) {
+      return luaL_error( L, "Unable to set callback." );
+    }
+  } else {
+    callback_free(L, id, mask);
+  }
+
+  return 0;  
+}
+
+// Lua: getpos( id ) -> pos, PRESS/RELEASE
+static int lrotary_getpos( lua_State* L )
+{
+  unsigned int id;
+  id = luaL_checkinteger( L, 1 );
+  MOD_CHECK_ID( rotary, id );
+
+  int pos = rotary_getpos(id);
+
+  if (pos == -1) {
+    return 0;
+  }
+
+  lua_pushnumber(L, (pos << 1) >> 1);
+  lua_pushnumber(L, (pos & 0x80000000) ? MASK(PRESS) : MASK(RELEASE));
+
+  return 2;  
+}
+
+// Returns TRUE if there maybe/is more stuff to do
+static bool lrotary_dequeue_single(lua_State* L, DATA *d)
+{
+  bool something_pending = FALSE;
+
+  if (d) {
+    // This chnnel is open
+    rotary_event_t result;
+
+    if (rotary_getevent(d->id, &result)) {
+      int pos = result.pos;
+
+      lrotary_check_timer(d, result.time_us, 0);
+
+      if (pos != d->lastpos) {
+	// We have something to enqueue
+	if ((pos ^ d->lastpos) & 0x7fffffff) {
+	  // Some turning has happened
+	  callback_call(L, d, ROTARY_TURN_INDEX, (pos << 1) >> 1, result.time_us);
+	}
+	if ((pos ^ d->lastpos) & 0x80000000) {
+	  // pressing or releasing has happened
+	  callback_call(L, d, (pos & 0x80000000) ? ROTARY_PRESS_INDEX : ROTARY_RELEASE_INDEX, (pos << 1) >> 1, result.time_us);
+	  if (pos & 0x80000000) {
+	    // Press
+	    if (d->last_recent_event_was_release && result.time_us - d->last_event_time < d->click_delay_us) {
+	      d->possible_dbl_click = 1;
+	    }
+	    d->last_recent_event_was_press = 1;
+	    d->last_recent_event_was_release = 0;
+	  } else {
+	    // Release
+	    d->last_recent_event_was_press = 0;
+	    if (d->possible_dbl_click) {
+	      callback_call(L, d, ROTARY_DBLCLICK_INDEX, (pos << 1) >> 1, result.time_us);
+	      d->possible_dbl_click = 0;
+	      // Do this to suppress the CLICK event
+	      d->last_recent_event_was_release = 0;
+	    } else {
+	      d->last_recent_event_was_release = 1;
+	    }
+	  }
+	  d->last_event_time = result.time_us;
+	}
+
+	d->lastpos = pos;
+      }
+
+      something_pending = rotary_has_queued_event(d->id);
+    }
+
+    lrotary_check_timer(d, system_get_time(), 1);
+  }
+
+  return something_pending;
+}
+
+static void lrotary_timer_done(void *param)
+{
+  DATA *d = (DATA *) param;
+
+  d->timer_running = 0;
+
+  lrotary_check_timer(d, system_get_time(), 1);
+}
+
+static void lrotary_check_timer(DATA *d, uint32_t time_us, bool dotimer)
+{
+  uint32_t delay = time_us - d->last_event_time;
+  if (d->timer_running) {
+    os_timer_disarm(&d->timer);
+    d->timer_running = 0;
+  }
+
+  int timeout = -1;
+
+  if (d->last_recent_event_was_press) {
+    if (delay > d->longpress_delay_us) {
+      callback_call(lua_getstate(), d, ROTARY_LONGPRESS_INDEX, (d->lastpos << 1) >> 1, d->last_event_time + d->longpress_delay_us);
+      d->last_recent_event_was_press = 0;
+    } else {
+      timeout = (d->longpress_delay_us - delay) / 1000;
+    }
+  }
+  if (d->last_recent_event_was_release) {
+    if (delay > d->click_delay_us) {
+      callback_call(lua_getstate(), d, ROTARY_CLICK_INDEX, (d->lastpos << 1) >> 1, d->last_event_time + d->click_delay_us);
+      d->last_recent_event_was_release = 0;
+    } else {
+      timeout = (d->click_delay_us - delay) / 1000;
+    }
+  }
+
+  if (dotimer && timeout >= 0) {
+    d->timer_running = 1;
+    os_timer_arm(&d->timer, timeout + 1, 0);
+  }
+}
+
+static void lrotary_task(os_param_t param, uint8_t prio) 
+{
+  (void) param;
+  (void) prio;
+
+  uint8_t *task_queue_ptr = (uint8_t*) param;
+  if (task_queue_ptr) {
+    // Signal that new events may need another task post
+    *task_queue_ptr = 0;
+  }
+
+  int id;
+  bool need_to_post = FALSE;
+  lua_State *L = lua_getstate();
+
+  for (id = 0; id < ROTARY_CHANNEL_COUNT; id++) {
+    DATA *d = data[id];
+    if (d) {
+      if (lrotary_dequeue_single(L, d)) {
+	need_to_post = TRUE;
+      }
+    }
+  }
+  
+  if (need_to_post) {
+    // If there is pending stuff, queue another task
+    task_post_medium(tasknumber, 0);
+  }
+}
+
+static int rotary_open(lua_State *L) 
+{
+  tasknumber = task_get_id(lrotary_task);
+  return 0;
+}
+
+// Module function map
+static const LUA_REG_TYPE rotary_map[] = {
+  { LSTRKEY( "setup" ),    LFUNCVAL( lrotary_setup ) },
+  { LSTRKEY( "close" ),    LFUNCVAL( lrotary_close ) },
+  { LSTRKEY( "on" ),       LFUNCVAL( lrotary_on    ) },
+  { LSTRKEY( "getpos" ),   LFUNCVAL( lrotary_getpos) },
+  { LSTRKEY( "TURN" ),     LNUMVAL( MASK(TURN)    ) },
+  { LSTRKEY( "PRESS" ),    LNUMVAL( MASK(PRESS)   ) },
+  { LSTRKEY( "RELEASE" ),  LNUMVAL( MASK(RELEASE) ) },
+  { LSTRKEY( "LONGPRESS" ),LNUMVAL( MASK(LONGPRESS) ) },
+  { LSTRKEY( "CLICK" ),    LNUMVAL( MASK(CLICK)   ) },
+  { LSTRKEY( "DBLCLICK" ), LNUMVAL( MASK(DBLCLICK)) },
+  { LSTRKEY( "ALL" ),      LNUMVAL( ROTARY_ALL     ) },
+
+  { LNILKEY, LNILVAL }
+};
+
+NODEMCU_MODULE(ROTARY, "rotary", rotary_map, rotary_open);

+ 1 - 1
app/modules/rtctime.c

@@ -87,7 +87,7 @@ static void do_sleep_opt (lua_State *L, int idx)
     uint32_t opt = lua_tonumber (L, idx);
     if (opt < 0 || opt > 4)
       luaL_error (L, "unknown sleep option");
-    deep_sleep_set_option (opt);
+    system_deep_sleep_set_option (opt);
   }
 }
 

+ 87 - 0
app/modules/sigma_delta.c

@@ -0,0 +1,87 @@
+// Module for interfacing with sigma-delta hardware
+
+#include "module.h"
+#include "lauxlib.h"
+#include "platform.h"
+
+
+// Lua: setup( pin )
+static int sigma_delta_setup( lua_State *L )
+{
+    int pin = luaL_checkinteger( L, 1 );
+
+    MOD_CHECK_ID(sigma_delta, pin);
+
+    platform_sigma_delta_setup( pin );
+
+    return 0;
+}
+
+// Lua: close( pin )
+static int sigma_delta_close( lua_State *L )
+{
+    int pin = luaL_checkinteger( L, 1 );
+
+    MOD_CHECK_ID(sigma_delta, pin);
+
+    platform_sigma_delta_close( pin );
+
+    return 0;
+}
+
+
+// Lua: setpwmduty( duty_cycle )
+static int sigma_delta_setpwmduty( lua_State *L )
+{
+    int duty = luaL_checkinteger( L, 1 );
+
+    if (duty < 0 || duty > 255) {
+        return luaL_error( L, "wrong arg range" );
+    }
+
+    platform_sigma_delta_set_pwmduty( duty );
+
+    return 0;
+}
+
+// Lua: setprescale( value )
+static int sigma_delta_setprescale( lua_State *L )
+{
+    int prescale = luaL_checkinteger( L, 1 );
+
+    if (prescale < 0 || prescale > 255) {
+        return luaL_error( L, "wrong arg range" );
+    }
+
+    platform_sigma_delta_set_prescale( prescale );
+
+    return 0;
+}
+
+// Lua: settarget( value )
+static int sigma_delta_settarget( lua_State *L )
+{
+    int target = luaL_checkinteger( L, 1 );
+
+    if (target < 0 || target > 255) {
+        return luaL_error( L, "wrong arg range" );
+    }
+
+    platform_sigma_delta_set_target( target );
+
+    return 0;
+}
+
+
+// Module function map
+static const LUA_REG_TYPE sigma_delta_map[] =
+{
+    { LSTRKEY( "setup" ),       LFUNCVAL( sigma_delta_setup ) },
+    { LSTRKEY( "close" ),       LFUNCVAL( sigma_delta_close ) },
+    { LSTRKEY( "setpwmduty" ),  LFUNCVAL( sigma_delta_setpwmduty ) },
+    { LSTRKEY( "setprescale" ), LFUNCVAL( sigma_delta_setprescale ) },
+    { LSTRKEY( "settarget" ),   LFUNCVAL( sigma_delta_settarget ) },
+    { LNILKEY, LNILVAL }
+};
+
+NODEMCU_MODULE(SIGMA_DELTA, "sigma_delta", sigma_delta_map, NULL);

+ 34 - 16
app/modules/sntp.c

@@ -41,6 +41,7 @@
 #include "c_stdlib.h"
 #include "user_modules.h"
 #include "lwip/dns.h"
+#include "user_interface.h"
 
 #ifdef LUA_USE_MODULES_RTCTIME
 #include "rtc/rtctime.h"
@@ -57,6 +58,14 @@
 # define sntp_dbg(...)
 #endif
 
+typedef enum {
+  NTP_NO_ERR = 0,
+  NTP_DNS_ERR,
+  NTP_MEM_ERR,
+  NTP_SEND_ERR,
+  NTP_TIMEOUT_ERR
+} ntp_err_t;
+
 typedef struct
 {
   uint32_t sec;
@@ -93,6 +102,8 @@ typedef struct
 static sntp_state_t *state;
 static ip_addr_t server;
 
+static void on_timeout (void *arg);
+
 static void cleanup (lua_State *L)
 {
   os_timer_disarm (&state->timer);
@@ -104,14 +115,15 @@ static void cleanup (lua_State *L)
 }
 
 
-static void handle_error (lua_State *L)
+static void handle_error (lua_State *L, ntp_err_t err)
 {
   sntp_dbg("sntp: handle_error\n");
   if (state->err_cb_ref != LUA_NOREF)
   {
     lua_rawgeti (L, LUA_REGISTRYINDEX, state->err_cb_ref);
+    lua_pushinteger (L, err);
     cleanup (L);
-    lua_call (L, 0, 0);
+    lua_call (L, 1, 0);
   }
   else
     cleanup (L);
@@ -120,12 +132,19 @@ static void handle_error (lua_State *L)
 
 static void sntp_dosend (lua_State *L)
 {
+  if (state->attempts == 0)
+  {
+    os_timer_disarm (&state->timer);
+    os_timer_setfn (&state->timer, on_timeout, NULL);
+    os_timer_arm (&state->timer, 1000, 1);
+  }
+
   ++state->attempts;
   sntp_dbg("sntp: attempt %d\n", state->attempts);
 
   struct pbuf *p = pbuf_alloc (PBUF_TRANSPORT, sizeof (ntp_frame_t), PBUF_RAM);
   if (!p)
-    handle_error (L);
+    handle_error (L, NTP_MEM_ERR);
 
   ntp_frame_t req;
   os_memset (&req, 0, sizeof (req));
@@ -146,17 +165,19 @@ static void sntp_dosend (lua_State *L)
   sntp_dbg("sntp: send: %d\n", ret);
   pbuf_free (p);
   if (ret != ERR_OK)
-    handle_error (L);
+    handle_error (L, NTP_SEND_ERR);
 }
 
 
 static void sntp_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
 {
-  lua_State *L = arg;
+  (void)arg;
+
+  lua_State *L = lua_getstate ();
   if (ipaddr == NULL)
   {
-    NODE_ERR("DNS Fail!\n");
-    handle_error(L);
+    sntp_dbg("DNS Fail!\n");
+    handle_error(L, NTP_DNS_ERR);
   }
   else
   {
@@ -168,10 +189,11 @@ static void sntp_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
 
 static void on_timeout (void *arg)
 {
+  (void)arg;
   sntp_dbg("sntp: timer\n");
-  lua_State *L = arg;
+  lua_State *L = lua_getstate ();
   if (state->attempts >= MAX_ATTEMPTS)
-    handle_error (L);
+    handle_error (L, NTP_TIMEOUT_ERR);
   else
     sntp_dosend (L);
 }
@@ -319,8 +341,8 @@ static int sntp_sync (lua_State *L)
   if (!state->pcb)
     sync_err ("out of memory");
 
-  if (udp_bind (state->pcb, IP_ADDR_ANY, NTP_PORT) != ERR_OK)
-    sync_err ("ntp port in use");
+  if (udp_bind (state->pcb, IP_ADDR_ANY, 0) != ERR_OK)
+    sync_err ("no port available");
 
   udp_recv (state->pcb, on_recv, L);
 
@@ -340,10 +362,6 @@ static int sntp_sync (lua_State *L)
   else
     state->err_cb_ref = LUA_NOREF;
 
-  os_timer_disarm (&state->timer);
-  os_timer_setfn (&state->timer, on_timeout, L);
-  os_timer_arm (&state->timer, 1000, 1);
-
   state->attempts = 0;
 
   // use last server, unless new one specified
@@ -353,7 +371,7 @@ static int sntp_sync (lua_State *L)
     const char *hostname = luaL_checklstring(L, 1, &l);
     if (l>128 || hostname == NULL)
       sync_err("need <128 hostname");
-    err_t err = dns_gethostbyname(hostname, &server, sntp_dns_found, &L);
+    err_t err = dns_gethostbyname(hostname, &server, sntp_dns_found, state);
     if (err == ERR_INPROGRESS)
       return 0;  // Callback function sntp_dns_found will handle sntp_dosend for us
     else if (err == ERR_ARG)

+ 1 - 5
app/modules/spi.c

@@ -30,10 +30,6 @@ static int spi_setup( lua_State *L )
   if (cpol != PLATFORM_SPI_CPOL_LOW && cpol != PLATFORM_SPI_CPOL_HIGH) {
     return luaL_error( L, "wrong arg type" );
   }
-  // CPOL_HIGH is not implemented, see app/driver/spi.c spi_master_init()
-  if (cpol == PLATFORM_SPI_CPOL_HIGH) {
-    return luaL_error( L, "cpol=high is not implemented" );
-  }
 
   if (cpha != PLATFORM_SPI_CPHA_LOW && cpha != PLATFORM_SPI_CPHA_HIGH) {
     return luaL_error( L, "wrong arg type" );
@@ -296,7 +292,7 @@ static int spi_transaction( lua_State *L )
     return luaL_error( L, "dummy_bitlen out of range" );
   }
 
-  if (miso_bitlen < -512 || miso_bitlen > 511) {
+  if (miso_bitlen < -512 || miso_bitlen > 512) {
     return luaL_error( L, "miso_bitlen out of range" );
   }
 

+ 425 - 0
app/modules/struct.c

@@ -0,0 +1,425 @@
+/*
+** {======================================================
+** Library for packing/unpacking structures.
+** $Id: struct.c,v 1.4 2012/07/04 18:54:29 roberto Exp $
+** See Copyright Notice at the end of this file
+** =======================================================
+*/
+// Original: http://www.inf.puc-rio.br/~roberto/struct/
+// This was ported to NodeMCU by Philip Gladstone, N1DQ
+/*
+** Valid formats:
+** > - big endian
+** < - little endian
+** ![num] - alignment
+** x - pading
+** b/B - signed/unsigned byte
+** h/H - signed/unsigned short
+** l/L - signed/unsigned long
+** T   - size_t
+** i/In - signed/unsigned integer with size `n' (default is size of int)
+** cn - sequence of `n' chars (from/to a string); when packing, n==0 means
+        the whole string; when unpacking, n==0 means use the previous
+        read number as the string length
+** s - zero-terminated string
+** f - float
+** d - double
+** ' ' - ignored
+*/
+
+
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+#include "module.h"
+
+
+#include "lua.h"
+#include "lauxlib.h"
+
+
+/* basic integer type */
+#if !defined(STRUCT_INT)
+#define STRUCT_INT	long
+#endif
+
+typedef STRUCT_INT Inttype;
+
+/* corresponding unsigned version */
+typedef unsigned STRUCT_INT Uinttype;
+
+
+/* maximum size (in bytes) for integral types */
+#ifdef LUA_NUMBER_INTEGRAL
+#ifdef LUA_INTEGRAL_LONGLONG
+#define MAXINTSIZE	8
+#else
+#define MAXINTSIZE	4
+#endif
+#else
+#define MAXINTSIZE	32
+#endif
+
+/* is 'x' a power of 2? */
+#define isp2(x)		((x) > 0 && ((x) & ((x) - 1)) == 0)
+
+/* dummy structure to get alignment requirements */
+struct cD {
+  char c;
+  double d;
+};
+
+
+#define PADDING		(sizeof(struct cD) - sizeof(double))
+#define MAXALIGN  	(PADDING > sizeof(int) ? PADDING : sizeof(int))
+
+
+/* endian options */
+#define BIG	0
+#define LITTLE	1
+
+
+static union {
+  int dummy;
+  char endian;
+} const native = {1};
+
+
+typedef struct Header {
+  int endian;
+  int align;
+} Header;
+
+
+static int getnum (const char **fmt, int df) {
+  if (!isdigit(**fmt))  /* no number? */
+    return df;  /* return default value */
+  else {
+    int a = 0;
+    do {
+      a = a*10 + *((*fmt)++) - '0';
+    } while (isdigit(**fmt));
+    return a;
+  }
+}
+
+
+#define defaultoptions(h)	((h)->endian = native.endian, (h)->align = 1)
+
+
+
+static size_t optsize (lua_State *L, char opt, const char **fmt) {
+  switch (opt) {
+    case 'B': case 'b': return sizeof(char);
+    case 'H': case 'h': return sizeof(short);
+    case 'L': case 'l': return sizeof(long);
+    case 'T': return sizeof(size_t);
+#ifndef LUA_NUMBER_INTEGRAL
+    case 'f':  return sizeof(float);
+    case 'd':  return sizeof(double);
+#endif
+    case 'x': return 1;
+    case 'c': return getnum(fmt, 1);
+    case 'i': case 'I': {
+      int sz = getnum(fmt, sizeof(int));
+      if (sz > MAXINTSIZE)
+        luaL_error(L, "integral size %d is larger than limit of %d",
+                       sz, MAXINTSIZE);
+      return sz;
+    }
+    default: return 0;  /* other cases do not need alignment */
+  }
+}
+
+
+/*
+** return number of bytes needed to align an element of size 'size'
+** at current position 'len'
+*/
+static int gettoalign (size_t len, Header *h, int opt, size_t size) {
+  if (size == 0 || opt == 'c') return 0;
+  if (size > (size_t)h->align)
+    size = h->align;  /* respect max. alignment */
+  return (size - (len & (size - 1))) & (size - 1);
+}
+
+
+/*
+** options to control endianess and alignment
+*/
+static void controloptions (lua_State *L, int opt, const char **fmt,
+                            Header *h) {
+  switch (opt) {
+    case  ' ': return;  /* ignore white spaces */
+    case '>': h->endian = BIG; return;
+    case '<': h->endian = LITTLE; return;
+    case '!': {
+      int a = getnum(fmt, MAXALIGN);
+      if (!isp2(a))
+        luaL_error(L, "alignment %d is not a power of 2", a);
+      h->align = a;
+      return;
+    }
+    default: {
+      const char *msg = lua_pushfstring(L, "invalid format option '%c'", opt);
+      luaL_argerror(L, 1, msg);
+    }
+  }
+}
+
+
+static void putinteger (lua_State *L, luaL_Buffer *b, int arg, int endian,
+                        int size) {
+  lua_Number n = luaL_checknumber(L, arg);
+  Uinttype value;
+  char buff[MAXINTSIZE];
+  if (n < 0)
+    value = (Uinttype)(Inttype)n;
+  else
+    value = (Uinttype)n;
+  if (endian == LITTLE) {
+    int i;
+    for (i = 0; i < size; i++) {
+      buff[i] = (value & 0xff);
+      value >>= 8;
+    }
+  }
+  else {
+    int i;
+    for (i = size - 1; i >= 0; i--) {
+      buff[i] = (value & 0xff);
+      value >>= 8;
+    }
+  }
+  luaL_addlstring(b, buff, size);
+}
+
+
+static void correctbytes (char *b, int size, int endian) {
+  if (endian != native.endian) {
+    int i = 0;
+    while (i < --size) {
+      char temp = b[i];
+      b[i++] = b[size];
+      b[size] = temp;
+    }
+  }
+}
+
+
+static int b_pack (lua_State *L) {
+  luaL_Buffer b;
+  const char *fmt = luaL_checkstring(L, 1);
+  Header h;
+  int arg = 2;
+  size_t totalsize = 0;
+  defaultoptions(&h);
+  lua_pushnil(L);  /* mark to separate arguments from string buffer */
+  luaL_buffinit(L, &b);
+  while (*fmt != '\0') {
+    int opt = *fmt++;
+    size_t size = optsize(L, opt, &fmt);
+    int toalign = gettoalign(totalsize, &h, opt, size);
+    totalsize += toalign;
+    while (toalign-- > 0) luaL_addchar(&b, '\0');
+    switch (opt) {
+      case 'b': case 'B': case 'h': case 'H':
+      case 'l': case 'L': case 'T': case 'i': case 'I': {  /* integer types */
+        putinteger(L, &b, arg++, h.endian, size);
+        break;
+      }
+      case 'x': {
+        luaL_addchar(&b, '\0');
+        break;
+      }
+#ifndef LUA_NUMBER_INTEGRAL
+      case 'f': {
+        float f = (float)luaL_checknumber(L, arg++);
+        correctbytes((char *)&f, size, h.endian);
+        luaL_addlstring(&b, (char *)&f, size);
+        break;
+      }
+      case 'd': {
+        double d = luaL_checknumber(L, arg++);
+        correctbytes((char *)&d, size, h.endian);
+        luaL_addlstring(&b, (char *)&d, size);
+        break;
+      }
+#endif
+      case 'c': case 's': {
+        size_t l;
+        const char *s = luaL_checklstring(L, arg++, &l);
+        if (size == 0) size = l;
+        luaL_argcheck(L, l >= (size_t)size, arg, "string too short");
+        luaL_addlstring(&b, s, size);
+        if (opt == 's') {
+          luaL_addchar(&b, '\0');  /* add zero at the end */
+          size++;
+        }
+        break;
+      }
+      default: controloptions(L, opt, &fmt, &h);
+    }
+    totalsize += size;
+  }
+  luaL_pushresult(&b);
+  return 1;
+}
+
+
+static lua_Number getinteger (const char *buff, int endian,
+                        int issigned, int size) {
+  Uinttype l = 0;
+  int i;
+  if (endian == BIG) {
+    for (i = 0; i < size; i++) {
+      l <<= 8;
+      l |= (Uinttype)(unsigned char)buff[i];
+    }
+  }
+  else {
+    for (i = size - 1; i >= 0; i--) {
+      l <<= 8;
+      l |= (Uinttype)(unsigned char)buff[i];
+    }
+  }
+  if (!issigned)
+    return (lua_Number)l;
+  else {  /* signed format */
+    Uinttype mask = (Uinttype)(~((Uinttype)0)) << (size*8 - 1);
+    if (l & mask)  /* negative value? */
+      l |= mask;  /* signal extension */
+    return (lua_Number)(Inttype)l;
+  }
+}
+
+
+static int b_unpack (lua_State *L) {
+  Header h;
+  const char *fmt = luaL_checkstring(L, 1);
+  size_t ld;
+  const char *data = luaL_checklstring(L, 2, &ld);
+  size_t pos = luaL_optinteger(L, 3, 1) - 1;
+  defaultoptions(&h);
+  lua_settop(L, 2);
+  while (*fmt) {
+    int opt = *fmt++;
+    size_t size = optsize(L, opt, &fmt);
+    pos += gettoalign(pos, &h, opt, size);
+    luaL_argcheck(L, pos+size <= ld, 2, "data string too short");
+    luaL_checkstack(L, 1, "too many results");
+    switch (opt) {
+      case 'b': case 'B': case 'h': case 'H':
+      case 'l': case 'L': case 'T': case 'i':  case 'I': {  /* integer types */
+        int issigned = islower(opt);
+        lua_Number res = getinteger(data+pos, h.endian, issigned, size);
+        lua_pushnumber(L, res);
+        break;
+      }
+      case 'x': {
+        break;
+      }
+#ifndef LUA_NUMBER_INTEGRAL
+      case 'f': {
+        float f;
+        memcpy(&f, data+pos, size);
+        correctbytes((char *)&f, sizeof(f), h.endian);
+        lua_pushnumber(L, f);
+        break;
+      }
+      case 'd': {
+        double d;
+        memcpy(&d, data+pos, size);
+        correctbytes((char *)&d, sizeof(d), h.endian);
+        lua_pushnumber(L, d);
+        break;
+      }
+#endif
+      case 'c': {
+        if (size == 0) {
+          if (!lua_isnumber(L, -1))
+            luaL_error(L, "format `c0' needs a previous size");
+          size = lua_tonumber(L, -1);
+          lua_pop(L, 1);
+          luaL_argcheck(L, pos+size <= ld, 2, "data string too short");
+        }
+        lua_pushlstring(L, data+pos, size);
+        break;
+      }
+      case 's': {
+        const char *e = (const char *)memchr(data+pos, '\0', ld - pos);
+        if (e == NULL)
+          luaL_error(L, "unfinished string in data");
+        size = (e - (data+pos)) + 1;
+        lua_pushlstring(L, data+pos, size - 1);
+        break;
+      }
+      default: controloptions(L, opt, &fmt, &h);
+    }
+    pos += size;
+  }
+  lua_pushinteger(L, pos + 1);
+  return lua_gettop(L) - 2;
+}
+
+
+static int b_size (lua_State *L) {
+  Header h;
+  const char *fmt = luaL_checkstring(L, 1);
+  size_t pos = 0;
+  defaultoptions(&h);
+  while (*fmt) {
+    int opt = *fmt++;
+    size_t size = optsize(L, opt, &fmt);
+    pos += gettoalign(pos, &h, opt, size);
+    if (opt == 's')
+      luaL_argerror(L, 1, "option 's' has no fixed size");
+    else if (opt == 'c' && size == 0)
+      luaL_argerror(L, 1, "option 'c0' has no fixed size");
+    if (!isalnum(opt))
+      controloptions(L, opt, &fmt, &h);
+    pos += size;
+  }
+  lua_pushinteger(L, pos);
+  return 1;
+}
+
+/* }====================================================== */
+
+
+
+static const LUA_REG_TYPE thislib[] = {
+  {LSTRKEY("pack"), LFUNCVAL(b_pack)},
+  {LSTRKEY("unpack"), LFUNCVAL(b_unpack)},
+  {LSTRKEY("size"), LFUNCVAL(b_size)},
+  {LNILKEY, LNILVAL}
+};
+
+
+NODEMCU_MODULE(STRUCT, "struct", thislib, NULL);
+
+/******************************************************************************
+* Copyright (C) 2010-2012 Lua.org, PUC-Rio.  All rights reserved.
+*
+* 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.
+******************************************************************************/
+

+ 61 - 56
app/modules/tmr.c

@@ -52,6 +52,7 @@ tmr.softwd(int)
 #include "lauxlib.h"
 #include "platform.h"
 #include "c_types.h"
+#include "user_interface.h"
 
 #define TIMER_MODE_OFF 3
 #define TIMER_MODE_SINGLE 0
@@ -59,54 +60,55 @@ tmr.softwd(int)
 #define TIMER_MODE_AUTO 1
 #define TIMER_IDLE_FLAG (1<<7) 
 
-//well, the following are my assumptions
-//why, oh why is there no good documentation
-//chinese companies should learn from Atmel
-extern void ets_timer_arm_new(os_timer_t* t, uint32_t milliseconds, uint32_t repeat_flag, uint32_t isMstimer);
-extern void ets_timer_disarm(os_timer_t* t);
-extern void ets_timer_setfn(os_timer_t* t, os_timer_func_t *f, void *arg);
-extern void ets_delay_us(uint32_t us);
-extern uint32_t system_get_time();
-extern uint32_t platform_tmr_exists(uint32_t t);
-extern uint32_t system_rtc_clock_cali_proc();
-extern uint32_t system_get_rtc_time();
-extern void system_restart();
-extern void system_soft_wdt_feed();
-
-//in fact lua_State is constant, it's pointless to pass it around
-//but hey, whatever, I'll just pass it, still we waste 28B here
+#define STRINGIFY_VAL(x) #x
+#define STRINGIFY(x) STRINGIFY_VAL(x)
+
+// assuming system_timer_reinit() has *not* been called
+#define MAX_TIMEOUT_DEF 6870947  //SDK 1.5.3 limit (0x68D7A3)
+
+static const uint32 MAX_TIMEOUT=MAX_TIMEOUT_DEF;
+static const char* MAX_TIMEOUT_ERR_STR = "Range: 1-"STRINGIFY(MAX_TIMEOUT_DEF);
+
 typedef struct{
 	os_timer_t os;
-	lua_State* L;
 	sint32_t lua_ref;
 	uint32_t interval;
 	uint8_t mode;
 }timer_struct_t;
 typedef timer_struct_t* timer_t;
 
-//everybody just love unions! riiiiight?
-static union {
-	uint64_t block;
-	uint32_t part[2];
-} rtc_time;
+// The previous implementation extended the rtc counter to 64 bits, and then
+// applied rtc2sec with the current calibration value to that 64 bit value.
+// This means that *ALL* clock ticks since bootup are counted with the *current*
+// clock period. In extreme cases (long uptime, sudden temperature change), this
+// could result in tmr.time() going backwards....
+// This implementation instead applies rtc2usec to short time intervals only (the
+// longest being around 1 second), and then accumulates the resulting microseconds
+// in a 64 bit counter. That's guaranteed to be monotonic, and should be a lot closer
+// to representing an actual uptime.
+static uint32_t rtc_time_cali=0;
+static uint32_t last_rtc_time=0;
+static uint64_t last_rtc_time_us=0;
+
 static sint32_t soft_watchdog  = -1;
 static timer_struct_t alarm_timers[NUM_TMR];
 static os_timer_t rtc_timer;
 
 static void alarm_timer_common(void* arg){
 	timer_t tmr = &alarm_timers[(uint32_t)arg];
-	if(tmr->lua_ref == LUA_NOREF || tmr->L == NULL)
+	lua_State* L = lua_getstate();
+	if(tmr->lua_ref == LUA_NOREF)
 		return;
-	lua_rawgeti(tmr->L, LUA_REGISTRYINDEX, tmr->lua_ref);
+	lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->lua_ref);
 	//if the timer was set to single run we clean up after it
 	if(tmr->mode == TIMER_MODE_SINGLE){
-		luaL_unref(tmr->L, LUA_REGISTRYINDEX, tmr->lua_ref);
+		luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref);
 		tmr->lua_ref = LUA_NOREF;
 		tmr->mode = TIMER_MODE_OFF;
 	}else if(tmr->mode == TIMER_MODE_SEMI){
 		tmr->mode |= TIMER_IDLE_FLAG;
 	}
-	lua_call(tmr->L, 0, 0);
+	lua_call(L, 0, 0);
 }
 
 // Lua: tmr.delay( us )
@@ -136,15 +138,13 @@ static int tmr_now(lua_State* L){
 // Lua: tmr.register( id, interval, mode, function )
 static int tmr_register(lua_State* L){
 	uint32_t id = luaL_checkinteger(L, 1);
-	MOD_CHECK_ID(tmr, id);
-	sint32_t interval = luaL_checkinteger(L, 2);
+	uint32_t interval = luaL_checkinteger(L, 2);
 	uint8_t mode = luaL_checkinteger(L, 3);
-	//validate arguments
-	uint8_t args_valid = interval <= 0
-		|| (mode != TIMER_MODE_SINGLE && mode != TIMER_MODE_SEMI && mode != TIMER_MODE_AUTO)
-		|| (lua_type(L, 4) != LUA_TFUNCTION && lua_type(L, 4) != LUA_TLIGHTFUNCTION);
-	if(args_valid)
-		return luaL_error(L, "wrong arg range");
+	//Check if provided parameters are valid
+	MOD_CHECK_ID(tmr, id);
+	luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
+  luaL_argcheck(L, (mode == TIMER_MODE_SINGLE || mode == TIMER_MODE_SEMI || mode == TIMER_MODE_AUTO), 3, "Invalid mode");
+  luaL_argcheck(L, (lua_type(L, 4) == LUA_TFUNCTION || lua_type(L, 4) == LUA_TLIGHTFUNCTION), 4, "Must be function");
 	//get the lua function reference
 	lua_pushvalue(L, 4);
 	sint32_t ref = luaL_ref(L, LUA_REGISTRYINDEX);
@@ -157,7 +157,6 @@ static int tmr_register(lua_State* L){
 	tmr->lua_ref = ref;
 	tmr->mode = mode|TIMER_IDLE_FLAG;
 	tmr->interval = interval;
-	tmr->L = L; 
 	ets_timer_setfn(&tmr->os, alarm_timer_common, (void*)id);
 	return 0;  
 }
@@ -219,9 +218,8 @@ static int tmr_interval(lua_State* L){
 	uint8_t id = luaL_checkinteger(L, 1);
 	MOD_CHECK_ID(tmr,id);
 	timer_t tmr = &alarm_timers[id];
-	sint32_t interval = luaL_checkinteger(L, 2);
-	if(interval <= 0)
-		return luaL_error(L, "wrong arg range");
+	uint32_t interval = luaL_checkinteger(L, 2);
+  luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
 	if(tmr->mode != TIMER_MODE_OFF){	
 		tmr->interval = interval;
 		if(!(tmr->mode&TIMER_IDLE_FLAG)){
@@ -262,28 +260,33 @@ static int tmr_wdclr( lua_State* L ){
 //it tells how many rtc clock ticks represent 1us.
 //the high 64 bits of the uint64_t multiplication
 //are unnedded (I did the math)
-static uint32_t rtc2sec(uint64_t rtc){
-	uint64_t aku = system_rtc_clock_cali_proc();
-	aku *= rtc;
-	return (aku>>12)/1000000;
+static uint32_t rtc2usec(uint64_t rtc){
+	return (rtc*rtc_time_cali)>>12;
 }
 
-//the following function workes, I just wrote it and didn't use it.
-/*static uint64_t sec2rtc(uint32_t sec){
-	uint64_t aku = (1<<20)/system_rtc_clock_cali_proc();
-	aku *= sec;
-	return (aku>>8)*1000000;
-}*/
+// This returns the number of microseconds uptime. Note that it relies on the rtc clock,
+// which is notoriously temperature dependent
+inline static uint64_t rtc_timer_update(bool do_calibration){
+	if (do_calibration || rtc_time_cali==0)
+		rtc_time_cali=system_rtc_clock_cali_proc();
 
-inline static void rtc_timer_update(){
 	uint32_t current = system_get_rtc_time();
-	if(rtc_time.part[0] > current) //overflow check
-		rtc_time.part[1]++;
-	rtc_time.part[0] = current;
+	uint32_t since_last=current-last_rtc_time; // This will transparently deal with wraparound
+	uint32_t us_since_last=rtc2usec(since_last);
+	uint64_t now=last_rtc_time_us+us_since_last;
+
+	// Only update if at least 100ms has passed since we last updated.
+	// This prevents the rounding errors in rtc2usec from accumulating
+	if (us_since_last>=100000)
+	{
+		last_rtc_time=current;
+		last_rtc_time_us=now;
+	}
+	return now;
 }
 
 void rtc_callback(void *arg){
-	rtc_timer_update();
+	rtc_timer_update(true);
 	if(soft_watchdog > 0){
 		soft_watchdog--;
 		if(soft_watchdog == 0)
@@ -293,8 +296,8 @@ void rtc_callback(void *arg){
 
 // Lua: tmr.time() , return rtc time in second
 static int tmr_time( lua_State* L ){
-	rtc_timer_update();
-	lua_pushinteger(L, rtc2sec(rtc_time.block));
+	uint64_t us=rtc_timer_update(false);
+	lua_pushinteger(L, us/1000000);
 	return 1; 
 }
 
@@ -332,7 +335,9 @@ int luaopen_tmr( lua_State *L ){
 		alarm_timers[i].mode = TIMER_MODE_OFF;
 		ets_timer_disarm(&alarm_timers[i].os);
 	}
-	rtc_time.block = 0;
+	last_rtc_time=system_get_rtc_time(); // Right now is time 0
+	last_rtc_time_us=0;
+
 	ets_timer_disarm(&rtc_timer);
 	ets_timer_setfn(&rtc_timer, rtc_callback, NULL);
 	ets_timer_arm_new(&rtc_timer, 1000, 1, 1);

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels