Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LFS documentation and example updates #2458

Merged
merged 2 commits into from
Aug 22, 2018
Merged

Conversation

TerryE
Copy link
Collaborator

@TerryE TerryE commented Aug 14, 2018

See #2431.

  • This PR is for the dev branch rather than for master.
  • This PR is compliant with the other contributing guidelines as well (if not, please describe why).
  • I have thoroughly tested my contribution.
  • The code changes are reflected in the documentation at docs/en/*.

Updates to LFS documentation, new LFS OTA example and backing out one unreviewed change to LFS _init file.

@TerryE
Copy link
Collaborator Author

TerryE commented Aug 14, 2018

@HHHartmann, Gregor @nwf, Nathaniel, you might want to review this as well.

docs/en/lfs.md Outdated

The LFS patch does this by adding two API new calls to the `node` module: one to reflash the LFS and restart the processor, and one to access the LFS store once loaded. Under the hood, it also addresses all of the technical issues to make this magic happen.
Now upload the generate LFS image file (`lfs.img` in this case) as a normal SPIFFS file. Several tools are available to upload the files from host to SPIFFS, including [ESPlorer](https://github.com/4refr0nt/ESPlorer). There is also a new example in `lua_examples` which can retrieve images from a standard web service.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Give its name, so people don't have to go fishing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

docs/en/lfs.md Outdated

Variable | Option
---------|------------
LFS size | (none, 32Kb, 64Kb, 94Kb) The default is none. Selecting a numeric value builds in the corresponding LFS.
SPIFFS size | (default or a multiple of 64Kb) The cloud build will base the SPIFFS at 1Mb if an explicit size is specified.
LFS size | (none, 32, 64, 96 or 128Kb) The default is none. Selecting a numeric value builds in the corresponding LFS.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than "builds in", maybe "leaves room for" ?

Copy link
Collaborator Author

@TerryE TerryE Aug 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per Johny's request, the LFS region is now statically allocated in lflash.c:

char flash_region_base[FLASH_SIZE] ICACHE_FLASH_RESERVED_ATTR;

so how about

The default is none, in which case LFS is disabled. Selecting a numeric value enables LFS with the LFS region sized at this value.

docs/en/lfs.md Outdated

You must choose an explicit (non-default) LFS size to enable the use of LFS. Whilst you can use a default (maximal) SPIFFS configuration, most developers find it more useful to work with a fixed SPIFFS that has been sized to match their application reqirements.
You must choose an explicit (non-default) LFS size to enable the use of LFS. Most developers find it more useful to work with a fixed SPIFFS size matched their application requirements.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either "size matched to their" or "size matching their".

docs/en/lfs.md Outdated

- Includes a 1 sec delay before connecting to the Wifi. This is a "just in case" when I am developing. This is enough to allow me to paste a `file.remove'init.lua'` into the UART if I want to do different development paths.
Under rare circumstances, for example a power fail during the flashing process, the flash can be left in a part-written state following a `flashreload()`. The Lua RTS start-up sequence will detect this and take the failsafe opton of resetting the LFS to empty, and if this happens then the LFS `_init` function will be unavailable. Your `init.lua` should therefore not assume that the LFS contains any modules (such as `_init`), and should contain logic to detect if LFS reset has occurred and if necessary reload the LFS again. Calling `node.flashindex("_init")()` directly will result in a panic loop in these circumstances. Therefore first check that `node.flashindex("_init")` returns a function or protect the call, `pcall(node.flashindex("_init"))`, and decode the error status to validate that initialisation was successful.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty far afield and NOT a blocker for the upcoming release, but I wanted to get the thought in your head: any possibility of a dual-slot LFS variant, where node.flashreload() can be told which slot to pave over and node.flashindex() either always uses the newest or always searches both? This would give a pretty nice "application LFS" vs. "failsafe LFS" setup and permit an OTA dance where only one of the two was ever potentially invalid at any moment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had dreamt of something like that too but also found it early to voice it. I would like to have a stable part with say telnet, ftp and network setup and another one with the application, which changes more often.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great minds ... In fact I also considered a dual slot LFS variant, but this was when I was still trying to get the in-ESP build version working. But you only have to fart when doing a lot of compiling and you run out of RAM.

The OTA code always spools the image to SPIFFS before even attempting to do the reload. Also have a look at my new compressed version; what this does is to do two passes over the deflated image. The first doesn't write to flash at all: it just does the inflate and generate the CRC of the inflated stream, plus extract the relocation flags array. If there are any errors found then it doesn't overwrite the flash, but instead returns an error message. If the image checks out, then it does an fseek back to the start of the image and this time does the flash for real. The decompress is fast, so two passes only take a couple of seconds and the main delay seems to be in the ROM restart entry point. Don't know why.

So IMO, the most probable reason for reload failure is a powerfail and there is nothing that we can do about that. But given that the image is still in SPIFFS, there is nothingo

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ha. So, I have just updated my LFS-flashing bit in init.lua to be

if node.flashindex() == nil then
  -- no LFS image.  Perhaps it failed to flash?
  if file.exists("luac.out.stage") and not file.exists("luac.out") then
    -- Looks like we tried once before, indeed.  Try again.
    node.flashreload("luac.out.stage")
  end
end
file.remove("luac.out.stage") -- remove stage file from last attempt
-- Do we have a new LFS blob?  If so, install it.
if file.exists("luac.out") then
  print("Updating LFS image.  Will reboot if things go well.")
  file.rename("luac.out", "luac.out.stage")
  node.flashreload("luac.out.stage")
  error("Failed to update LFS!")
end

If that seems sensible, it's perhaps worth including as an example in the docs?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, you should always use the -o option and name your images something.img and preferable embed a version number in the name. If you are doing OTA over the internet then also embed the some cypto signature for the file in the filename itself or pull the singature down separately using your App API.

I wouldn't over-complicate this with the current version as the compressed image version will go into dev as soon as we commit these changes and promote to master, and this has reasonable integrity checking in it.

If your images are called LFS_00001.img, etc, then you can easily track which you should be loading through a small SPIFFS config file or even put the LFS image version in RTC mem.

But yes, we should include some examples, but it might we worth using our scripts in anger for a month of so before raising them as PRs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather see problems in the content of the LFS image than in the flashing process. So there could be an early decision from which partition to load if used as alternatives or only to start the base LFS and not the app-LFS part.
This decisoin could be made based on the last run (or its failure) based on bootreason or some other startup markers (a file or RTC mem)

Copy link
Collaborator Author

@TerryE TerryE Aug 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather see problems in the content of the LFS image than in the flashing process. So there could be an early decision from which partition to load if used as alternatives or only to start the base LFS and not the app-LFS part.

The last two full words of an RFC 1951 deflate stream are the CRC and the unpacked len of the output stream. Having done the first pass which involves reading the entire SPIFFS file, and validated the CRC and the length, we can have confidence that we can read and unpack the image, and that it is as compiled.

As I said, the only material risk of failure during reflash is one on power-fail and this will be detected on restart, so whilst having two flash regions seems comfortable, it is just a lot easier to keep any LFS variants that you need as LFS image files on SPIFFS. As I said, the reflash takes about a second or two, though there is a further few seconds delay in the ROM restart routine.

Can you give me a scenario where having hot swap LFS regions would add any value at all?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TerryE Terry I did not mean any failure in the flashing process but false Lua code that fails to run for some reason.
I have a module setting a persistent flag at boot time and resetting it after some time.
If at boot it has not been reset (due to application failure) I would like to start a fallback or minimal system which is stored in a safe location (which is never destroyed by a faulty application LFS).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surely you can still do this by a little conditional logic in your init.lua and if necessary loading the fallback from SPIFFS?

docs/en/lfs.md Outdated

This code is also conveniently packaged in [lua_examples/lfs/_init.lua](../../lua_examples/lfs/_init.lua).
The first sets up an LFS table in `_G` with the `__index` and `__newindex` metamethods. The main purpose of the `__index()` is to resolve any names against the LFS using a `node.flashindex()` call, so that `LFS.someFunc(params)` does exactly what you would expect it to do: this will call `someFunc` with the specified parameters, if it exists in in the LFS. The LFS properties `_time`, `_config` and `_list` can be used to access the other LFS metadata that you need. See the code to understand what they do, but `LFS._list` is the array of all module names in the LFS.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"an LFS table in _G" ?

Copy link
Collaborator Author

@TerryE TerryE Aug 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would you describe

local G=getfenv()
G.LFS = setmetatable(lfs_t,lfs_t)

How about "The first sets up a table in the global variable LFS with the __index and __newindex metamethods"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry for being overly short. I just meant to put backticks around LFS: LFS. I think the prose is fine.


```Lua
do local pl = package.loaders; pl[2],pl[5] = pl[5],pl[2]; end
do local pl = package.loaders; pl[1],pl[3] = pl[3],pl[1]; end
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remain somewhat unhappy about exposing these particular details to programmers, but for the moment it's fine, I suppose.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To quote the LFS paper: "The second uses standard Lua functionality to add the LFS to the require package.loaders list". This is already openly documented, but we can refine it later.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, I don't think we should be hiding that package.loaders[1] is an interesting thing, I just would wish for a way to not encourage that particular thing to end up in people's lua, since we might change package.loaders in the future. But as you say, let's worry about refining it later, not now.

```

(The indexes 2 and 5 come from the value of the `loaders` array in [app/lua/loadlib.c](../../app/lua/loadlib.c)).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe keep this explanation around? Though the constants need updating.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hooked the detailed online documentation above. Either the apps programmers don't care or they can look up the master documentation.

docs/en/lfs.md Outdated
You can then create a file, say `LFS_dummy_strings.lua`, and insert these `local preload` lines into it. By including this file in your `luac.cross` compile, then the cross compiler will also include all strings referenced in this dummy module in the generated ROM string table. Note that you don''t need to call this module; it's inclusion in the LFS build is enough to add the strings to the ROM table. Once in the ROM table, then you can use them subsequently in your application without incurring any RAM or LGC overhead. A useful starting point may be found in [lua_examples/lfs/dummy_strings.lua](https://github.com/nodemcu/nodemcu-firmware/tree/dev/lua_examples/lfs/dummy_strings.lua); this saves about 4Kb of RAM by moving a lot of common compiler and Lua VM strings into ROM.

Another good use of this technique is when you have resources such as CSS, HTML and JS fragments that you want to output over the internet. Instead of having lots of small resource files, you can just use string assignments in an LFS module and this will keep these constants in LFS instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably these should not be local in LFS?

Copy link
Collaborator Author

@TerryE TerryE Aug 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure that you mean, Nathaniel.

The preload example just has one or more assignment of a single local to a list of strings. Do a luac.cross -l to see what this generates. The binary consists of a bunch of LOADK instructions, one per string and references a bunch of string constants which get put into the ROstrt.

If you are referring to a resources file then the best way is to avoid creating globals or any unnecessary RAM hit by just having a load of:

if arg == 'resourceA' then return [[
some long resource
]] end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking you meant that the developer should install into LFS a module that looked like this

resourceA = [[ ... ]]
resourceB = [[ ... ]]

rather than like this

local resourceA = [[ ... ]]
local resourceB = [[ ... ]]

since while the latter puts the strings in LFS, it doesn't make it easy to get at them.

Copy link
Collaborator Author

@TerryE TerryE Aug 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If your routine is LFS.getResource, then this approach would generate a load of globals whether you need them or not. If you really want to save RAM then in my approach you would do something like:

local resource =  LFS.getResource
-- ...
local js = resource('script1')

and you could avoid creating any globals. Or if you have logical pools of resources then each getResource() name could return a small table of resources.

But the important point is that Lua developers are free to do this in whatever way they feel comfortable and suits their applications.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Thanks for clarification.

docs/en/lfs.md Outdated
You can do this at the interactive prompt or call it as a debug function during a running application in order to generate this string list, (but note that calling this still creates the overhead of an array in RAM, so you do need to have enough "head room" to do the call).

You can then create a file, say `LFS_dummy_strings.lua`, and insert these `local preload` lines into it. By including this file in your `luac.cross` compile, then the cross compiler will also include all strings referenced in this dummy module in the generated ROM string table. Note that you don''t need to call this module; it's inclusion in the LFS build is enough to add the strings to the ROM table. Once in the ROM table, then you can use them subsequently in your application without incurring any RAM or LGC overhead. A useful starting point may be found in [lua_examples/lfs/dummy_strings.lua](https://github.com/nodemcu/nodemcu-firmware/tree/dev/lua_examples/lfs/dummy_strings.lua); this saves about 4Kb of RAM by moving a lot of common compiler and Lua VM strings into ROM.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth pointing out that the resulting LFS has a LFS_dummy_strings module in it that doesn't do anything.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is the magic that strings are in the rom table?
Every string that gets compiled by luac.cross is checked for duplicates and the ROM table and stored only once?
The node.compile also checks against the ROM table but cannot alter it (it's in ROM)
So it is only useful if I node.compile?
I would like to see some more explanation about that.

Copy link
Collaborator Author

@TerryE TerryE Aug 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it that doesn't do anything

Apart from freeing up about 4Kb RAM 😉

@HHHartmann Gregor read on in the second half of the document. Strings in Lua are interned. The RTS will take any new string and resolve it first against the ROstrt and then the RWstrt. If it already exists in one of those then this reference is used and so another copy is not created. A new string is only created in the RWstrt in the case of a miss. Also the Lua GC does mark and sweep on strings in the RWstrt so once dereferenced, they will be GCed again.

So if you compile or load an LC into RAM or even execute something like dir..name..'.lua', this will still be checked against the ROstrt and created in the RWstrt if needed. The strings in dummy_strings.lua are the ones that the Lua VM and the file and net modules create during startup. I did think of adding the ones used in the compiler, as this would allow you to compiler larger files locally.

As I suggest, have a play with debug.getstring('RAM') in your running app, to see what you might want to add. The reason that I say to use the "RAM" selector is that any strings already used in the LFS modules will already be included in the ROstrt.

You can also look at the source in app/lua/luac_cross/lflashimg.c/ addTS() is used to collected a table of all strings used in the LFS and createROstrt() builds the strings. app/lua/lsring.c:luaS_newlstr() does the lookup magic. The overhead of the ROstrt is less than the savings by reducing the size of the Lua GC mark and sweeps, so this is also a runtime upside.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. being used to GB of ram this seemed so crazy to me that I didn't even consider this.
Great job done!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's crazy what goes around comes around. The first minis that I used were the PDP8 and PDP11 and the first micro the Intel 8080, and I did Motorola 6502 work and these sorts of tricks were standard, but discarded when VAXs and virtual memory came into vogue; they were all hauled out again with Win3.0 and the Intel 80286 protected mode, but abandoned again as 32-bit then 64-bit variants took over and VM became universal -- until IoTs turned up and they get rolled out again!

In essence we've gone from Lua code running in 48Kb RAM to running in 256Kb ROM + 48 Kb RAM, so you can now build apps 4× the size -- so long as you can work within the RAM constraint.

Copy link
Member

@nwf nwf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some very minor comments. Thank you for updating the docs. :)

Copy link
Member

@HHHartmann HHHartmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great example to have. sure will help people to do OTA.
I only read the code, so there might be more.

if (size == s.size) then
wifi.setmode(wifi.NULLMODE)
collectgarbage();collectgarbage()
if s and s.size == size then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this check seems to be useless as it has been done 3 lines above already.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct the second should be removed and the first add the s and predicate. file.stat() returns nil if the file doesn't exist so evaluating s.size will throw an error.

end
uart.write(0,('%u of %u, '):format(total, size))
file.write(rec)
if total == size then finalise(sck) end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the server sends more bytes or closes the connection prematurely?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code assumes that the server will conform to HTTP 1.1, so it ignores byte overrun.
In terms of the early disconnect we would need to add a couple of extra sock:on() events to catch this, but the easiest way to handle this whole class of errors is to schedule a backstop alarm before calling LFS.HTTP_LUA(). Given that the normal path is to do a flashreload() which restarts the ESP, if this timer fires then something has timed out.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. After all it's an example. Maybe we can still improve it later on.

sck:hold()
node.task.post(0, function() sck:unhold() end)
end
uart.write(0,('%u of %u, '):format(total, size))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why write to uart here and print elsewhere?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Print adds a \n (and also uses\t as a field separator). uart.write() doesn't.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if I use node.output() I see only half of the show.
Besides this would print something like
0 of 3000, 1024 of 3000,
I would prefer separate lines, but tastes vary.

collectgarbage();collectgarbage()
if s and s.size == size then
-- run as separate task to maximise RAM available
node.task.post(function() node.flashreload(image) end)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if flashing fails and the call returns? Print some error message (I guess the new Version with specific error messages is not included yet)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I said, this was just a starting point. We can improve on it.

The main thing was to show as an example how to do the GET request and what to process in the response, and also using flow control to ensure that server doesn't overrun the ESP because writing to SPIFFS is slower than the capacity of many WAN and LAN links.

print(rec:sub(1, i+1))
if size > 0 then
sck:on("receive",subsRec)
file.open('lfs.img', 'w')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you happen to test this only with a file called "lfs.img"?
Here you open "lfs.img" but in finalize you use the variable image.
If they are not equal, finalize will fail (or burn some old stuff)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you happen to test this only with a file called "lfs.img"?

Oops 😱

@TerryE
Copy link
Collaborator Author

TerryE commented Aug 15, 2018

Thanks guys. Really appreciate.

@marcelstoer marcelstoer added this to the next-release milestone Aug 15, 2018
@TerryE
Copy link
Collaborator Author

TerryE commented Aug 15, 2018

@marcelstoer @vsky279, I was uncomfortable with the Windows quick start guide being the first section in the doc, as it just didn't fit well here. I have therefore dropped this down below the background. I have also trimmed the background section slightly and removed some of the duplication between the quick-start and the following section. I hope that you are both comfortable with the result. If not, then please add your comments.

@marcelstoer
Copy link
Member

marcelstoer commented Aug 15, 2018

I'll give this some though later but IMO the point of a quick start guide is that I don't need to study the background first before I can start using a new feature. I can always come back later to learn more. What is the shortest path to success? I felt that placing it at the top would be very much in line with what @jmd13391 had requested in his issue.
In that sense a good quick start in the right place can be seen as a "marketing measure".

@TerryE
Copy link
Collaborator Author

TerryE commented Aug 15, 2018

Marcel, the background is a minimal introduction that explains what LFS does and why it does it. If developers can't be bothered to read a page of text on this, then IMO they shouldn't be quick-starting anything to do with LFS.

The previous quick start text was written solely around WSL, but Win10 Pro is also a prerequisite for WSL. I suspect that only a minority of our Windows developers use this variant, so I don't know why we should be expecting what is possibly the majority of our developers to upgrade from Win7 and Home Editions to quick start anything.

If you want it at the lede to the document, then I suggest that you and the other contributors create a set of separate and standalone quick-starts, one for Win10Pro, one for other WinX, and one for Docker users. I can reference these in the LFS paper.

This sort of documentation takes a lot of effort to produce and I seem to the only contributor / committer willing to put this effort in. Doing this is also a difficult balancing act and I try to reflect all constructive comment and input, but I can't please everyone every time.

@TerryE
Copy link
Collaborator Author

TerryE commented Aug 22, 2018

Marcel, I have tried to precis the lede as much as possible. I've read it through top to bottom and done some more smoothing. I've also taken on board the above comments. Since this is documentation and example only, I will merge this in later tomorrow.

@TerryE TerryE merged commit add0938 into nodemcu:dev Aug 22, 2018
@TerryE TerryE deleted the dev-LFS-doc-updates branch August 22, 2018 10:09

doRequest = function(sk,hostIP)
if hostIP then
local con = net.createConnection(net.TCP,0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TerryE I recently tried modifying your HTTP_OTA example using tls.createConnection instead, so updated LFS images could be retrieved from a secure server such as AWS s3. It didn't work, however -- the connection was established and got a success back from the server, but it seems that the on("receive",..) callback was never called subsequently with the body of the file to download. I would have expected the tls submodule to be equivalent to the net module in retrieving a file.

Am I missing something obvious?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants