ESP8266 Support WIKI

User Tools

Site Tools


nodemcu-unofficial-faq

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
Last revision Both sides next revision
nodemcu-unofficial-faq [2015/06/17 03:56]
vowstar Add item `Why file writes fail all the time`
nodemcu-unofficial-faq [2016/05/22 13:57]
admin old revision restored
Line 1: Line 1:
-====== nodeMCU Unofficial FAQ ======+-====== nodeMCU Unofficial FAQ ======
  
 ** * * * Work in Progress * * * ** ** * * * Work in Progress * * * **
Line 6: Line 6:
 ===== What is this FAQ for? ===== ===== What is this FAQ for? =====
  
-This FAQ does not aim to help you to learn to program or even how to program in Lua.  There are plenty of resources on the Internet for this, some of which are listed in [[#Where to start|Where to start]] .  What this FAQ does is to answer some of the common questions that a competent Lua developer would ask in learning how to develop Lua applications for the ESP8266 based boards running the [[http://​nodemcu.com/​index_en.html|nodeMCU]] firmware.+This FAQ does not aim to help you to learn to program or even how to program in Lua.  There are plenty of resources on the Internet for this, some of which are listed in [[#Where to start|Where to start]]. ​ What this FAQ does is to answer some of the common questions that a competent Lua developer would ask in learning how to develop Lua applications for the ESP8266 based boards running the [[http://​nodemcu.com/​index_en.html|nodeMCU]] firmware.
  
 ===== Lua Language ===== ===== Lua Language =====
Line 14: Line 14:
 The nodeMCU firmware implements Lua 5.1 over the Espressif SDK for its ESP8266 SoC and the IoT modules based on this. The nodeMCU firmware implements Lua 5.1 over the Espressif SDK for its ESP8266 SoC and the IoT modules based on this.
  
-  * The official lua.org [[http://​www.lua.org/​manual/​5.1/​manual.html|Lua Language specification]] ​ gives a terse but complete language specification.+  * The official lua.org ​**[[http://​www.lua.org/​manual/​5.1/​manual.html|Lua Language specification]]**  gives a terse but complete language specification.
   * Its [[http://​www.lua.org/​faq.html|FAQ]] ​ provides information on Lua availability and licensing issues.   * Its [[http://​www.lua.org/​faq.html|FAQ]] ​ provides information on Lua availability and licensing issues.
-  * The [[http://​www.luafaq.org/​|unofficial Lua FAQ]]  provides a lot of useful Q and A content.+  * The **[[http://​www.luafaq.org/​|unofficial Lua FAQ]]** provides a lot of useful Q and A content, and is extremely useful for those learning Lua as a second language.
   * The [[http://​lua-users.org/​wiki/​|Lua User's Wiki]] ​ gives useful example source and relevant discussion. In particular, its [[http://​lua-users.org/​wiki/​Learning|Lua Learning Lua]]  section is a good place to start learning Lua.  ​   * The [[http://​lua-users.org/​wiki/​|Lua User's Wiki]] ​ gives useful example source and relevant discussion. In particular, its [[http://​lua-users.org/​wiki/​Learning|Lua Learning Lua]]  section is a good place to start learning Lua.  ​
-  * The best book to learn Lua is //​Programming in Lua// by Roberto Ierusalimschy,​ one of the creators of Lua. It's first edition is available free [[http://​www.lua.org/​pil/​contents.html|online]] . The second edition was aimed at Lua 5.1, but is out of print. The third edition is still in print and available in paperback. ​ It contains a lot more material and clearly identifies Lua 5.1 vs Lua 5.2 differences. This third edition is widely available for purchase and probably the best value for money. References of the format [PiL **n.m**] refer to section **n.m** in this edition.+  * The best book to learn Lua is //​Programming in Lua// by Roberto Ierusalimschy,​ one of the creators of Lua. It's first edition is available free [[http://​www.lua.org/​pil/​contents.html|online]] . The second edition was aimed at Lua 5.1, but is out of print. The third edition is still in print and available in paperback. ​ It contains a lot more material and clearly identifies Lua 5.1 vs Lua 5.2 differences. ​**This third edition is widely available for purchase and probably the best value for money**. References of the format [PiL **n.m**] refer to section **n.m** in this edition.
   * The Espressif ESP8266 architecture is closed source, but the Espressif SDK itself is continually being updated so the best way to get the documentation for this is to [[https://​www.google.co.uk/​search?​q=Espressif+IoT+SDK+Programming+Guide|google Espressif IoT SDK Programming Guide]] or to look at the Espressif [[http://​bbs.espressif.com/​viewforum.php?​f=5|downloads forum]] .   * The Espressif ESP8266 architecture is closed source, but the Espressif SDK itself is continually being updated so the best way to get the documentation for this is to [[https://​www.google.co.uk/​search?​q=Espressif+IoT+SDK+Programming+Guide|google Espressif IoT SDK Programming Guide]] or to look at the Espressif [[http://​bbs.espressif.com/​viewforum.php?​f=5|downloads forum]] .
-  * The [[http://​www.nodemcu.com/​docs/​|nodeMCU documentation]] ​ is available online. However, please remember that the development team are based in China, and English is a second language, so the documentation needs expanding and be could improved with technical proofing.+  * The **[[http://​www.nodemcu.com/​docs/​|nodeMCU documentation]]** is available online. However, please remember that the development team are based in China, and English is a second language, so the documentation needs expanding and be could improved with technical proofing.
   * As with all Open Source projects the source for the nodeMCU firmware is openly available on the [[https://​github.com/​nodemcu/​nodemcu-firmware|GitHub nodemcu-firmware]] repository.   * As with all Open Source projects the source for the nodeMCU firmware is openly available on the [[https://​github.com/​nodemcu/​nodemcu-firmware|GitHub nodemcu-firmware]] repository.
    
 ==== How is nodeMCU Lua different to standard Lua? ==== ==== How is nodeMCU Lua different to standard Lua? ====
  
-nodeMCU ​Lua in an implementation of [[http://www.eluaproject.net/overview|eLua]] ​ over the ESP8266 SDK. eLua is full-featured implementation of Lua 5.1 that is optimized for embedded ​system development and execution ​to provide a scripting ​framework that can be used to deliver useful applications ​within the limited RAM and Flash memory resources ​of embedded processors such as the ESP8266+Whilst the Lua standard distribution includes a host stand-alone Lua interpreter,​ Lua itself is primarily ​an //extension language// that makes no assumptions about "​main"​ program: ​Lua works embedded ​in a host application ​to provide a powerful, light-weight ​scripting ​language for use within the application. This host application can then invoke functions to execute a piece of Lua code, can write and read Lua variables, and can register C functions to be called by Lua code. Through ​the use of C functions, Lua can be augmented to cope with a wide range of different domains, thus creating customized programming languages sharing a syntactical framework.
  
-A key goal of eLua is to reduce the RAM requirements for the Lua runtime system. One of the key techniques used in this implementation is to use read-only tables and constants wherever practical for library modules.  ​On a typical build this approach reduces the RAM footprint by some 20-25KB ​and this makes Lua implementation ​for the ESP8266 ​feasibleThis technique ​is called LTR and this is documented ​in detail in an eLua technical paper: [[http://www.eluaproject.net/doc/master/en_arch_ltr.html|Lua Tiny RAM]] .+The ESP8266 was designed and is fabricated ​in China by [[http://​espressif.com/​new-sdk-release/​|Espressif Systems]].  ​Espressif have also developed ​and released ​companion software development kit (SDK) to enable developers to build practical [[wp>​Internet of Things|IoT]] applications ​for the ESP8266. ​ The SDK is made freely available to developers in the form of binary libraries ​and SDK documentation. ​ However ​this is in //closed format//, with no developer access to the source files, so ESP8266 applications ​//must// rely solely on the SDK API (and the somewhat Spartan SDK API documentation).
  
-The Espressif SDK is the interface that is freely available //albeit in closed format// ​to developers building applications for the ESP8266.  The nodeMCU eLua implementation must therefore use this SDK as its kernel layer and work within any design constraints ​that the SDK API imposes.  ​In particular, the SDK employs an event and task-oriented structurewhere individual events can trigger an associated task; this task then runs to completion uninterrupted,​ at which point the next event queued can be initiated.  ​(Note that the SDK contains device drivers which are interrupt driven. However, these are internal ​to the SDK, so it treats all event triggered //​application//​ tasks as atomic.)+The nodeMCU Lua firmware ​is an ESP8266 application and must therefore be layered over the ESP8266 SDK. However, the hooks and features of Lua enable it to be seamlessly integrated without loosing any of the standard Lua language features.  The firmware has replaced some standard Lua modules ​that don't align well with the SDK structure with ESP8266-specific versions.  ​For example, the standard ​ ''​io'' ​and ''​os''​ libraries don't workbut have been largely replaced by the nodeMCU ''​node''​ and ''​file''​ libraries.  ​The ''​debug''​ and ''​math''​ libraries have also been omitted ​to reduce ​the runtime footprint.
  
-The API calls for each type of event typically use a callback parameter to bind a C function implementing a given task to a given event. ​ In the case of the nodeMCU ​Lua implementation,​ this task is a wrapper around a developer-provided Lua functionThis event-driven model imposed by the SDK is very different to conventional procedural ​implementation of Lua.  Some standard Lua modules ​and eLua platform modules don't fit well within ​this structure, ​and so the nodeMCU implementation replaces these by ESP8266-specific versions.  ​For example, ​the standard ​ ''​io''​ and ''​os''​ libraries don't work, but have been largely replaced ​by the nodeMCU ''​node'' ​and ''​file''​ libraries+NodeMCU ​Lua is based on [[http://​www.eluaproject.net/​overview|eLua]], ​fully featured ​implementation of Lua 5.1 that has been optimized for embedded system development ​and execution to provide a scripting framework that can be used to deliver useful applications ​within ​the limited RAM and Flash memory resources of embedded processors such as the ESP8266. One of the main changes introduced in the eLua fork is to use read-only tables and constants wherever practical for library modules.  ​On a typical build this approach reduces ​the RAM footprint ​by some 20-25KB and this makes a Lua implementation for the ESP8266 feasible. This technique is called LTR and this is documented in detail in an eLua technical paper: [[http://​www.eluaproject.net/​doc/​master/​en_arch_ltr.html|Lua Tiny RAM]].
  
-The ''​debug'' ​and ''​math'' ​libraries ​have also been omitted ​to reduce ​the runtime footprint.  ​+The mains impacts of the ESP8266 SDK and together with its hardware resource limitations are not in the Lua language implementation itself, but in how //​application programmers must approach developing and structuring their applications//​. As discussed in detail below, the SDK is non-preemptive and event driven. ​ Tasks can be associated with given events by using the SDK API to registering callback functions to the corresponding events. ​ Events are queued internally within the SDK, and it then calls the associated tasks one at a time, with each task returning control to the SDK on completion. //The SDK states that if any tasks run for more than 10 mSec, then services such as Wifi can fail.// 
 + 
 +The nodeMCU ​libraries ​act as C wrappers around registered Lua callback functions ​to enable these to be used as SDK tasks. **//You must therefore use an [[wp>​Event-driven programming|Event-driven programming]] style in writing your ESP8266 Lua programs//​**. ​ Most programmers are used to writing in a procedural style where there is a clear single flow of execution, and the program interfaces to operating system services by a set of synchronous API calls to do network I/O, etc.  ​Whilst the logic of each individual task is procedural, this is not how you code up ESP8266 applications.
  
 ===== ESP8266 Specifics ===== ===== ESP8266 Specifics =====
Line 44: Line 46:
 ==== How is coding for the ESP8266 different to standard Lua? ==== ==== How is coding for the ESP8266 different to standard Lua? ====
  
-  *  The ESP8266 use onchip RAM and offchip Flash memory connected using a dedicated SPI interface. ​ Both of these are //very// limited (when compared to systems ​than most application programmer use).  The SDK and the Lua firmware already use the majority of this resource: the later build versions keep adding useful functionality,​ and unfortunately at an increased RAM and Flash cost, so depending on the build version and the number of modules installed the runtime can have as little as 17KB RAM and 40KB Flash available at an application level. ​ This Flash memory is formatted an made available as a **SPI Flash File System (SPIFFS)** through the ''​file''​ library.+  *  The ESP8266 use onchip RAM and offchip Flash memory connected using a dedicated SPI interface. ​ Both of these are //very// limited (when compared to systems ​that most application programmer use).  The SDK and the Lua firmware already use the majority of this resource: the later build versions keep adding useful functionality,​ and unfortunately at an increased RAM and Flash cost, so depending on the build version and the number of modules installed the runtime can have as little as 17KB RAM and 40KB Flash available at an application level. ​ This Flash memory is formatted an made available as a **SPI Flash File System (SPIFFS)** through the ''​file''​ library.
   *  However, if you choose to use a custom build, for example one which uses integer arithmetic instead of floating point, and which omits libraries that aren't needed for your application,​ then this can help a lot doubling these available resources. ​ (See Marcel Stör'​s excellent [[http://​frightanic.com/​nodemcu-custom-build/​|custom build tool]] that he discusses in [[http://​www.esp8266.com/​viewtopic.php?​f=23&​t=3001|this forum topic]]). ​ Even so, those developers who are used to dealing in MB or GB of RAM and file systems can easily run out of these resources. ​ Some of the techniques discussed below can go a long way to mitigate this issue.   *  However, if you choose to use a custom build, for example one which uses integer arithmetic instead of floating point, and which omits libraries that aren't needed for your application,​ then this can help a lot doubling these available resources. ​ (See Marcel Stör'​s excellent [[http://​frightanic.com/​nodemcu-custom-build/​|custom build tool]] that he discusses in [[http://​www.esp8266.com/​viewtopic.php?​f=23&​t=3001|this forum topic]]). ​ Even so, those developers who are used to dealing in MB or GB of RAM and file systems can easily run out of these resources. ​ Some of the techniques discussed below can go a long way to mitigate this issue.
   *  Current versions of the ESP8266 run the SDK over the native hardware so there is no underlying operating system to capture errors and to provide graceful failure modes, so system or application errors can easily "​PANIC"​ the system causing it to reboot. Error handling has been kept simple to save on the limited code space, and this exacerbates this tendency. Running out of a system resource such as RAM will invariably cause a messy failure and system reboot.   *  Current versions of the ESP8266 run the SDK over the native hardware so there is no underlying operating system to capture errors and to provide graceful failure modes, so system or application errors can easily "​PANIC"​ the system causing it to reboot. Error handling has been kept simple to save on the limited code space, and this exacerbates this tendency. Running out of a system resource such as RAM will invariably cause a messy failure and system reboot.
Line 56: Line 58:
  ​node.restart();​ for i = 1, 20 do print("​not quite yet -- ",i); end  ​node.restart();​ for i = 1, 20 do print("​not quite yet -- ",i); end
 </​code>​ </​code>​
-  * You therefore //have// to implement ESP8266 Lua applications using an [[wp>​Event-driven programming|event driven]] structure.  You have to understand which SDK API requests schedule asynchronous processing, and which define event actions through Lua callbacks. ​ Yes, such an event-driven approach makes it difficult to develop procedurally structured applications,​ but it is well suited to developing the sorts of application that you will typically want to implement on an [[wp>​Internet of Things|IoT]] device.+  * You therefore //have// to implement ESP8266 Lua applications using an event driven ​approach.  You have to understand which SDK API requests schedule asynchronous processing, and which define event actions through Lua callbacks. ​ Yes, such an event-driven approach makes it difficult to develop procedurally structured applications,​ but it is well suited to developing the sorts of application that you will typically want to implement on an [[wp>​Internet of Things|IoT]] device.
  
 ==== So how does the SDK event / tasking system work in Lua? ==== ==== So how does the SDK event / tasking system work in Lua? ====
  
-Any SDK-based application ​for the ESP8266 ​uses a startup hook ''​void user_init(void)''​ defined by convention in the C module ''​user_main.c''​.  The system ​invokes ​this hook on boot. The ''​user_init()''​ function can by used to do any initialisation required and to call the necessary timer alarms or system functions ​to bind and callback routines to implement the tasks needed in response to any system events. ​Individual task callbacks ​need to implement their actions ​and return control to the SDK as soon as practicalas the SDK framework ​is not pre-emptive so any further event tasks are queued on a pending ​list within the SDK kernel.  ​+  * The SDK employs an event-driven and task-oriented architecture ​for programming at an applications level.  
 +  * The SDK uses a startup hook ''​void user_init(void)''​defined by convention in the C module ''​user_main.c''​, which it invokes on boot. The ''​user_init()''​ function can be used to do any initialisation required and to call the necessary timer alarms or other SDK API calls to bind and callback routines to implement the tasks needed in response to any system events. 
 +  * The API provides a set of functions for declaring application functions (written in C) as callbacks to associate application tasks with specific hardware ​and timer events. ​ These are non-preemptive at an applications level. 
 +  * Whilst ​the SDK provides a number of interrupt driven device drivers, the hardware architecture severely limits the memory available for these drivers, so writing new device drivers ​is not a viable options for most developers 
 +  * The SDK interfaces internally with hardware and device drivers to queue pending ​events. 
 +  * The registered callback routines are invoked sequentially with the associated C task running to completion uninterrupted. 
 +  * In the case of Lua, these C tasks are typically functions ​within ​the Lua runtime library code and these typically act as C wrappers around the corresponding developer-provided Lua callback functions. ​ An example here is the Lua ''​tmr.alarm(id,​ interval, repeat, callback)''​ function. ​ The calls a function in the ''​tmr''​ library which registers a C function for this alarm using the SDK, and when this C function is called it then invokes the Lua callback.  ​
  
-Excessively long-running ​tasks can therefore cause other system functions and services to timeout, or allocate memory to buffer queued data, which can then trigger either the watchdog timer or memory exhaustion, both of which will ultimately cause the system to reboot. +The nodeMCU firmware simply mirrors this structure at a Lua scripting level: 
- +  * A startup module ''​init.lua''​ is invoked on boot. This function module can be used to do any initialisation required and to call the necessary timer alarms or libary calls to bind and callback routines to implement the tasks needed in response to any system events. 
-SDK Callbacks include: +  * The Lua libraries provide a set of functions for declaring application functions (written in Lua) as callbacks (which are stored in the [[#So how is the Lua Registry used and why is this important?​|Lua registry]]) to associate application tasks with specific hardware and timer events. ​ These are non-preemptive at an applications level. 
-  *  Timer alarm callbacks +  * The Lua libraries work in concert with the SDK to queue pending events and invoke any registered Lua callback routines, which then run to completion uninterrupted. 
-  *  Wifi scan callbacks +  * Excessively long-running ​Lua functions ​can therefore cause other system functions and services to timeout, or allocate memory to buffer queued data, which can then trigger either the watchdog timer or memory exhaustion, both of which will ultimately cause the system to reboot. 
-  *  Network (ESPCONN) callbacks for connection, disconnect, sendreceive, etc. (roughly equivalent to the ''​socket:​on()''​ callbacks ​in Lua+  * By default, the Lua runtime also 'listens' ​to UART 0, the serial port, in interactive mode and will execute any Lua commands input through this serial port.
-  *  GPIO and other hardware related interrupts.+
  
-The eLua implementation sits within this framework:​ +This event-driven approach is very different ​to a conventional procedural implementation of Lua.  ​
-  *  ''​app/​user/​user_main.c''​ contains the ''​user_init()''​ entry point.  ​This reinitialises the UART, the volatile sections of flash memory (if necessary), the RomFS and SPIFFS before calling ''​lua_main()''​ with the command-line ''​lua -i''​. +
-  *  The Lua RTS (see ''​app/​lua/​lua.c''​) then sets up a timer to poll the input UART every 80 mSec to assemble ​complete execution chunk which it then executes with a ''​lua_pcall()''​. +
-  *  The running ​Lua script can initialise one or more callbacks associated with events such as a timer.  ​The library code will typically store the link to this Lua callback function in the [[#So how is the Lua Registry used and why is this important?​|Lua registry]] .  When the callback hook is subsequently invoked, this hook code then retrieves this function reference from the registry and executes it with a ''​lua_call()''​. +
-  *  There are no concurrency or interlock issues with this approach as the SDK will only initiate a callback after the previously running task has completed, and in the case of Lua when the previous Lua chunk has completed -- Lua chunks are executed one-at-a-time.+
  
 Consider a simple telnet example given in ''​examples/​fragment.lua'':​ Consider a simple telnet example given in ''​examples/​fragment.lua'':​
Line 94: Line 97:
  end)  end)
 </​code>​ </​code>​
-This example ​doesn't use [[#Why is it importance to understand how upvalues are implemented when programming for the ESP8266?​|upvalues]] ​and all declarations ​are globalso we can reorder this code for clarity (though doing this adds a few extra globals)+ 
-<code Lua> +This example ​defines five Lua functions:​ 
- ​function c_receive(c,​l)  + 
-   node.input(l)  +^      Function ​       ^      Defined in      ^     ​Parameters ​       ^ Callback? ^ 
- end +| Main                 | Outer module ​        | ... (Not used)        |           | 
- ​function c_disconnection(c)  +| Connection listener ​ | Main                 | c (connection socket) |           | 
-   ​con_std = nil  +| s_output ​            | Connection listener ​ | str                   ​| ​   Yes    | 
-   ​node.output(nil)  +| On Receive ​          | Connection listener ​ | c, l (socket, input) ​ |    Yes    | 
- end +| On Disconnect ​       | Connection listener ​ | c (socket) ​           |    Yes    | 
- ​function s_output(str)  + 
-   ​if(con_std~=nil) then  +''s'',​ ''​con_std''​ and ''​s_output''​ are global, and no [[#Why is it importance to understand how upvalues are implemented when programming for the ESP8266?​|upvalues]] are used.  There is no "​correct"​ order to define these inbut we could reorder this code for clarity (though doing this adds a few extra globals) ​and define these functions separately one another.   However, let us consider how this is executed: 
-     ​con_std:​send(str)  + 
-   end  +  * The outer module is compiled including ​the four internal functions. 
- end +  * ''​Main'' ​is then assigning the created ​''​net.createServer()'' ​to the global ​''​s''​.  The ''​connection listener'' ​closure ​is created ​and bound to a temporary variable which is then passed to the ''​socket.listen()''​ as an argument ​The ​routine then exits returning ​control to the firmware
- ​function s_listen(c)  +  *  When another computer connects to port 23, the listener handler retrieves the reference to then connection listener ​and calls it with the socket parameter. ​ This function then binds the s_output closure to the global ​''​s_output''​, and registers this function with the ''​node.output''​ hook. Likewise ​the ''​on receive''​ and ''​on disconnection''​ are bound to temporary variables which are passed ​to the respective on handlers. ​ We now have four Lua functions ​registered in the Lua runtime libraries ​associated with four events.  This routine then exits returning control to the firmware
-   ​con_std = c  +  *  When a record is received, the on receive ​handler ​within the net library ​retrieves the reference to the ''​on receive'' ​Lua function ​and calls it passing it the record. This routine then passes this to the ''​node.input()''​ and exits returning control to the firmware
-   ​node.output(s_output0)  +  *  The ''​node.input'' ​handler ​polls on an 80 mSec timer alarm. If a compete Lua chunk is available ​(either via the serial port or node input function)then it executes it and any output is then passed to the ''​note.output'' ​handlerwhich calls ''​s_output'' ​function.  Any pending sends are then processed. 
-   ​c:​on("​receive",​c_receive)  +  *  This cycle repeats until the other computer disconnects,​ and ''​net'' ​library ​ disconnection ​handler ​then calls the Lua  ''​on disconnect'' ​handler. This Lua routine dereferences the connected socket and closes the ''​node.output''​ hook and exits returning control to the disconnect handler which garbage collects any associated sockets and registered on handlers. 
-   ​c:​on("​disconnection",​c_disconnection)  + 
- end +Whilst this is all going on, The SDK can (and often willschedule other event tasks in between these Lua executions (e.g. to do the actual TCP stack processing). ​ The longest individual Lua execution in this example is only 18 bytecode instructions (in the main routine).
- ​s=net.createServer(net.TCP)  +
- ​s:​listen(23,​s_listen) +
-</​code>​ +
-So let us consider how this is executed: +
-  *  The outer routine executes binding 4 functions to the global variables: ​''​c_receive''​''​c_disconnection''​''​s_output''​''​s_listen'';​ the server ''​s''​ is then created ​by the ''​net.createServer()''​ function and is bound to port 23 registering ''​s_listen''​ as the initialisation callbackThis outer routine then exits return ​control to the calling C libary. ​ As part of the call cleanup only these global variables retained and all of the outer routine local variable and code are garbage collected+
-  *  When another computer connects to port 23, the listener handler retrieves the reference to ''​s_listen''​ from the registry ​and calls it with the socket parameter. ​ This function then binds ''​s_output'' ​to the ''​node.output''​ hook registering it in the registry, and likewise ​the ''​c_receive''​ and ''​c_disconnection''​ are bound and registered ​to the respective on handlers. ​ We now have four routines ​registered in the registry ​associated with four events, and this routine then exits with only the routines execution frame garbage collected+
-  *  When a record is received, the ''​on_receive'' ​handler retrieves the reference to ''​c_receive'' ​from the registry ​and calls it passing it the record. This routine then passes this to the ''​node.input()''​ and exits. ​(The node input handler marshals these records into a complete Lua chunk.) +
-  *  The node input handler ​is polling ​on an 80 mSec alarm and if a compete Lua chunk is available, it executes it.  Any output is then passed to the note.output handler which retrieves and calls ''​s_output'' ​which exits on completion.  Any pending sends are then processed. +
-  *  This cycle repeats until the other computer disconnects,​ and this triggers the ''​on_disconnect()''​ handler This C function retrieves the ''​c_disconnection'' ​reference from the registry and executes it. This Lua routine dereferences the connected socket and closes the ''​node.output''​ hook and exits returning control to the disconnect handler which garbage collects any associated sockets and registered on handlers. +
-The SDK can and will often schedule other event tasks in between these Lua executions (e.g. to do the actual TCP stack processing). ​ The longest individual Lua execution in this example is only 20 bytecode instructions (in the main routine). The original version was a few instructions shorter in that temporary locals were used to hold the closure references instead of globals, but the runtime and memory footprint aren't materially different.+
   ​   ​
 Understanding how the system executes your code can help you structure it better and improve memory usage. Each event task is established by a callback in an API call in an earlier task.  Understanding how the system executes your code can help you structure it better and improve memory usage. Each event task is established by a callback in an API call in an earlier task. 
 +
 +==== So what Lua library functions enable the registration of Lua callbacks? ====
 +
 +SDK Callbacks include:
 +
 +^ Lua Module ^ Functions which define or remove callbacks ​                                  ^
 +| tmr        | ''​alarm(id,​ interval, repeat, function())'' ​                                 |
 +| node       | ''​key(type,​ function())'',​ ''​output(function(str),​ serial_debug)'' ​          ​| ​
 +| wifi       | ''​startsmart(chan,​ function())'',​ ''​sta.getap(function(table))'' ​            |
 +| net.server | ''​sk:​listen(port,​[ip],​function(socket))'' ​                                   |
 +| net        | ''​sk:​on(event,​ function(socket,​ [, data]))'',​ ''​sk:​send(string,​ function(sent))'',​ ''​sk:​dns(domain,​ function(socket,​ip))''​ |
 +| gpio       | ''​trig(pin,​ type, function(level))'' ​                                        |
 +| mqqt       | ''​client:​m:​on(event,​ function(conn[,​ topic, data])'' ​                        |
 +| uart       | ''​uart.on(event,​ cnt, [function(data)],​ [run_input])'' ​                      |
 +
  
 ==== So how is context passed between Lua event tasks? ==== ==== So how is context passed between Lua event tasks? ====
Line 176: Line 184:
 ''​tmr.delay()''​ is really intended to be used where you need to have more precise timing control on an external hardware I/O (e.g. lifting a GPIO pin high for 20  μSec). ​ It will achieve no functional purpose in pretty much every other usecase, as any other system code-based activity will be blocked from execution; at worst it will break your application and create hard-to-diagnose timeout errors. ​ ''​tmr.delay()''​ is really intended to be used where you need to have more precise timing control on an external hardware I/O (e.g. lifting a GPIO pin high for 20  μSec). ​ It will achieve no functional purpose in pretty much every other usecase, as any other system code-based activity will be blocked from execution; at worst it will break your application and create hard-to-diagnose timeout errors. ​
  
-The latest SDK includes a caution that if and (callback) task runs for more than 10 mSec, then the Wifi and TCP stacks might fail, so if you want a delay of more than 8 mSec or so, then //using ''​tmr.delay()''​ is the wrong approach//​. ​ You should be using a timer alarm or another library callback, to allow the other processing to take place. ​ As the nodeMCU documentation correctly advises (translating Chinese English into English): //''​tmr.delay()''​ will make the CPU work in non-interrupt mode, so other instructions and interrupts will be blocked. Take care in using this function.//+The latest SDK includes a caution that if any (callback) task runs for more than 10 mSec, then the Wifi and TCP stacks might fail, so if you want a delay of more than 8 mSec or so, then //using ''​tmr.delay()''​ is the wrong approach//​. ​ You should be using a timer alarm or another library callback, to allow the other processing to take place. ​ As the nodeMCU documentation correctly advises (translating Chinese English into English): //''​tmr.delay()''​ will make the CPU work in non-interrupt mode, so other instructions and interrupts will be blocked. Take care in using this function.//
  
  
Line 184: Line 192:
   * When this happens, the only robust solution is to reflash the firmware.   * When this happens, the only robust solution is to reflash the firmware.
   * The simplest way to avoid having to do this is to keep the ''​init.lua''​ as simple as possible -- say configure the wifi and then start your app using a one-time ''​tmr.alarm()''​ after a 2-3 sec delay. ​ This delay is long enough to issue a ''​file.remove("​init.lua"​)''​ through the serial port and recover control that way.   * The simplest way to avoid having to do this is to keep the ''​init.lua''​ as simple as possible -- say configure the wifi and then start your app using a one-time ''​tmr.alarm()''​ after a 2-3 sec delay. ​ This delay is long enough to issue a ''​file.remove("​init.lua"​)''​ through the serial port and recover control that way.
-  * Also always test any new ''​init.lua''​ by creating it as ''​init_test.lua'',​ say, and manually issuing a ''​dofile("​init_test.lua"​)''​ through the serial port, and then only rename it when you are certain it is working as you require.+  * Also it is always ​best to test any new ''​init.lua''​ by creating it as ''​init_test.lua'',​ say, and manually issuing a ''​dofile("​init_test.lua"​)''​ through the serial port, and then only rename it when you are certain it is working as you require.
  
 ===== Techniques for Reducing RAM and SPIFFS footprint ===== ===== Techniques for Reducing RAM and SPIFFS footprint =====
Line 238: Line 246:
  
 <code Lua> <code Lua>
-  local module ​ ...  -- this is a situation where using an upvalue is essential!+  local module ​...  -- this is a situation where using an upvalue is essential!
   return function (csocket)   return function (csocket)
     package.loaded[module]=nil     package.loaded[module]=nil
Line 312: Line 320:
   *  Install lua and luac on your development PC.  This is freely available for Windows, Mac and Linux distributions,​ but we strongly suggest that you use Lua 5.1 to maintain source compatibility with ESP8266 code.  This will allow you not only to unit test some modules on your PC in a rich development environment,​ but you can also use ''​luac''​ to generate a bytecode listing of your code and to validate new code syntactically before downloading to the ESP8266. ​ This will also allow you to develop server-side applications and embedded applications in a common language. ​   *  Install lua and luac on your development PC.  This is freely available for Windows, Mac and Linux distributions,​ but we strongly suggest that you use Lua 5.1 to maintain source compatibility with ESP8266 code.  This will allow you not only to unit test some modules on your PC in a rich development environment,​ but you can also use ''​luac''​ to generate a bytecode listing of your code and to validate new code syntactically before downloading to the ESP8266. ​ This will also allow you to develop server-side applications and embedded applications in a common language. ​
  
-===== DEVKIT V1.Specifics ===== +===== Firmware and Lua app development ===== 
-==== Why file writes fail all the time? ==== + 
-  * NodeMCU DEVKIT V1.0 uses ESP12-E-DIO(ESP-12-D) module, it runs in DIO(Dual IO SPI) mode. If uses old firmware (<=0.9.5) or old flashtool, the filesystem will not workTo solve this problemthe fastest way is updata all software ​to latest.  +==== How to save memory? ==== 
-  * 1Please use latest esptool.py ​and with DIO option to flash firmwarehttps://​github.com/​themadinventor/esptool +  * The NodeMCU development team recommends that you consider using a tailored firmware build, which only includes the modules that you plan to use before developing any Lua applicationOnce you have the ability to make and flash custom builds, the you also have the option of moving time sensitive or logic intensive code into your own custom module. Doing this can save a large amount of RAM as C code can be run directly from Flash memory. If you want an easy-to-use intermediate option then why note try the [[http://​frightanic.com/​nodemcu-custom-build|Cloud based nodeMCU custom build service]]?.  
-  * 2. Please use latest ​NodeMCU flasher with default option (must click '​restore to default'​ in advanced tab). https://​github.com/​nodemcu/​nodemcu-flasher + 
-  * 3. Please use latest Espressif'​s flash tool(without auto download support). If always have problem, please use latest flash download tool from espressif. ​http://​bbs.espressif.com/​viewtopic.php?​f=5&​t=433 ​Please use DIO mode and 32M flash size option, and flash latest firmware to 0x00000. Before flashing firmware, ​please ​hold FLASH button, and press RST button once. When our firmware download tool released, ​it will flash firmware automatically ​and needn'​t press any button.+===== Hardware ​Specifics ===== 
 + 
 +==== Why file writes fail all the time on DEVKIT V1.0? ==== 
 + 
 +  * NodeMCU DEVKIT V1.0 uses ESP12-E-DIO(ESP-12-D) module.  This module ​runs the Flash memory ​in [[#​What'​s the different between ​DIO and QIO mode?|Dual IO SPI]] (DIO) mode. This firmware will not be correctly loaded if you uses old flashtool version, and the filesystem will not work if you used a pre 0.9.6 firmware ​version ​(<0.9.5) or old. The easiest way to resolve ​this problem ​s update all the firmware and flash tool to  ​current version 
 +    - Use the latest ​[[https://​github.com/​themadinventor/​esptool|esptool.py]] with DIO support and command ​option to flash firmware, or  
 +    - Use the latest [[https://​github.com/​nodemcu/nodemcu-flasher|NodeMCU flasher]] with default option(You must select the ''​restore to default'​' option ​in advanced ​menu tab), or  
 +    - Use the latest Espressif'​s flash tool -- see [[http://​bbs.espressif.com/​viewtopic.php?​f=5&​t=433|this Espressif forum topic]] (without auto download support). Use DIO mode and 32M flash size option, and flash latest firmware to 0x00000. Before flashing firmware, ​remember to hold FLASH button, and press RST button once. Note that the new nodeMCU ​our firmware download tool, when released, will be capable of flashing ​firmware automatically ​without ​any button ​presses. 
 + 
 +==== What's the different between DIO and QIO mode? ==== 
 + 
 +Whether DIO or QIO modes are available depends on the physical connection between the ESP8266 CPU and its onboard flash chip. QIO connects to the flash using 6 pins as compared to DIO's 4, and QIO is twice as fast to read/write as DIO. 
 + 
 +==== How to use DEVKIT V0.9 on Mac OS X? ==== 
 +<​TODO>​ 
 + 
 +==== How does DEVKIT use DTR and RTS to enter download mode? ==== 
 +<​TODO>​
nodemcu-unofficial-faq.txt · Last modified: 2017/06/27 00:50 by terrye