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

ENOMEM when running sample code from readme.md #19

Closed
Kortenbach opened this issue Dec 21, 2018 · 21 comments
Closed

ENOMEM when running sample code from readme.md #19

Kortenbach opened this issue Dec 21, 2018 · 21 comments

Comments

@Kortenbach
Copy link

Kortenbach commented Dec 21, 2018

Make sure you are running the latest version of TinyWeb before reporting an issue.

Device and/or platform:
ESP8266

Description of problem:

  • I downloaded the latest Tinyweb firmware
  • I loaded the sample code into the ESP and ran it
  • Error: ENOMEM

Expected:
Running example code

Traceback (if applicable):

>>> exec(open('./TinyWebTest.py').read(),globals())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 32, in <module>
  File "tinyweb/server.py", line 651, in run
  File "uasyncio/core.py", line 126, in run_forever
  File "uasyncio/core.py", line 87, in run_forever
  File "tinyweb/server.py", line 614, in _tcp_server
OSError: [Errno 12] ENOMEM
>>> scandone

>>> gc.mem_free()
13584

Additional info:
The code rus fine after a Hard-reset. Every re-run after that results in ENOMEM.
Line 613: sock.bind(addr)
Line 614: sock.listen(backlog)
It seems the socket is not freed properly or something like that?

@belyalov
Copy link
Owner

belyalov commented Dec 23, 2018

How big is TinyWebTest.py?

Since python is interpreted language - load/compile takes significant amount of memory and 13k of free memory is not enough to compile your code right on microcontroller.

There are 2 possible options:

  • Use frozen modules
  • Switch to esp32 which is successor of esp8266

Anyway - it is strongly recommended to use frozen modules, good reading at:

P.S. It is not clear why you have only 13k of free memory, in my setup using NodeMCU 1.0 it 35k / 30k after loading of tinyweb:

>>>
>>> import gc
>>> gc.mem_free()
36640

>>>
>>> import tinyweb
>>> gc.mem_free()
30400

@Kortenbach
Copy link
Author

Hi Konstantin. Thank you for your answer. The source code is straight from the readme.MD on github and so is the firmware. There is no other code involved.

@belyalov
Copy link
Owner

Can you share it?
Which version of esp8266 are you using?

BTW, now examples are included into firmware starting from release 1.3.3, so you can simply run Hello World example right after flash:

>>> import examples.hello_world as hello
>>> hello.run()

@belyalov
Copy link
Owner

Also, I just tried slightly updated code from README.md:

# Network config done prior to this line, then CTRL+E to enter paste mode
>>>
paste mode; Ctrl-C to cancel, Ctrl-D to finish
import tinyweb
===
===
=== # Create web server application
=== app = tinyweb.webserver()
===
===
=== # Index page
=== @app.route('/')
=== async def index(request, response):
===     # Start HTTP response with content-type text/html
===     await response.start_html()
===     # Send actual HTML page
===     await response.send('<html><body><h1>Hello, world! (<a href="/table">table</a>)</h1></html>\n')
===
===
=== # Another one, more complicated page
=== @app.route('/table')
=== async def table(request, response):
===     # Start HTTP response with content-type text/html
===     await response.start_html()
===     await response.send('<html><body><h1>Simple table</h1>'
===                         '<table border=1 width=400>'
===                         '<tr><td>Name</td><td>Some Value</td></tr>')
===     for i in range(10):
===         await response.send('<tr><td>Name{}</td><td>Value{}</td></tr>'.format(i, i))
===     await response.send('</table>'
===                         '</html>')
===
===
=== def run():
===     app.run(host='0.0.0.0', port=8081)
===
>>>
>>> run()

And from another terminal:

$ curl http://192.168.3.100:8081
<html><body><h1>Hello, world! (<a href="/table">table</a>)</h1></html>

$ curl http://192.168.3.100:8081/table
<html><body><h1>Simple table</h1><table border=1 width=400><tr><td>Name</td><td>Some Value</td></tr><tr><td>Name0</td><td>Value0</td></tr><tr><td>Name1</td><td>Value1</td></tr><tr><td>Name2</td><td>Value2</td></tr><tr><td>Name3</td><td>Value3</td></tr><tr><td>Name4</td><td>Value4</td></tr><tr><td>Name5</td><td>Value5</td></tr><tr><td>Name6</td><td>Value6</td></tr><tr><td>Name7</td><td>Value7</td></tr><tr><td>Name8</td><td>Value8</td></tr><tr><td>Name9</td><td>Value9</td></tr></table></html>

Hope it will help.. :)

@Kortenbach
Copy link
Author

Hi, Konstantin. I appreciate your effort, but I don't understand. Is the readme.md code not correct in some way? Why does the code run well after reboot? I am running it on a cheap NodeMCU V1.3 from Aliexpress. I've only flashed your firmware and I ran your code from readme.md. B.t.w. If I just import tinyweb I have about 22k of free memory to spend. After running the sample and ctrl-c, about 10k is lost and I cannot restart, even after garbage collection.

@Kortenbach
Copy link
Author

I'm still looking for a nice IDE that let's me browse code by symbol and has code completion. Without it I'm kinda lost...

@belyalov
Copy link
Owner

I've updated readme code - to make it even simpler. I don't sure that previous version of sample code from readme worked well - I've added it only to show simplicity (it wasn't intended to copy-paste, I though that...)

But now it definitely works :)

Regarding IDE - I don't believe that there is some special IDE, personally I'm using Sublime Text 3, but it is not IDE.. just powerful editor.. )

@Kortenbach
Copy link
Author

Thank you Konstantin. I will try the new sample code tomorrow.

@Kortenbach
Copy link
Author

Kortenbach commented Dec 24, 2018

Hi Konstantin,

I tried the new firmware V1.3.3 and the build-in example. I'm afraid it didn't go too well either. I'll tell you exactly what I did:

  • I loaded the new firmware. AFter loading the blue LED started blinking erratically.
  • I pressed the "RST" button on the ESP:
connected with blabla, channel 13
dhcp client start...
ip:192.168.1.25,mask:255.255.255.0,gw:192.168.1.1 
  • No >>> prompt
  • I pressed Ctrl-C
  • and the REPL prompt showed up.
  • I checked free memory:
>>> import gc
>>> gc.mem_free()
32816
>>> 
  • I then proceeded as instructed in Readme.md:
>>> import network

# Connect to WiFi
>>> sta_if = network.WLAN(network.STA_IF)
>>> sta_if.active(True)
>>> sta_if.connect('<ssid>', '<password>')

# Run Hello World! :)
>>> import examples.hello_world as hello
>>> gc.mem_free()
25424
>>> hello.run()
  • The website runs fine
  • Then I press Ctrl-C to interrupt the running process and check free mem:
>>> hello.run()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "examples/hello_world.py", line 44, in run
  File "tinyweb/server.py", line 651, in run
  File "uasyncio/core.py", line 145, in run_forever
  File "uasyncio/__init__.py", line 49, in wait
KeyboardInterrupt: 
>>> gc.mem_free()
24752
>>> 
  • Now I try to re-run the sample:
>>> hello.run()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "examples/hello_world.py", line 44, in run
  File "tinyweb/server.py", line 651, in run
  File "uasyncio/core.py", line 126, in run_forever
  File "uasyncio/core.py", line 87, in run_forever
  File "tinyweb/server.py", line 614, in _tcp_server
OSError: [Errno 12] ENOMEM
>>> 
  • That's when I get the ENOMEM... Why?

A few extra questions that are not directly related to the ENOMEM problem:

  • Why do I get an erratically blinking LED after loading the new firmware?
  • Why don't I get a >>> prompt after restarting

@belyalov
Copy link
Owner

Hi,

Now I got your problem.
Actually due to nature of embedded programming "graceful" shutdowns are usually not supported due to:

  1. It increases code complexity
  2. As a result of ^^ flash / RAM usage increases too
  3. In "production" applications you mostly never restart your program (except full reset)

The same for tinyweb - it does not support graceful shutdown / release resources. It is doable, however I don't see any reason to add such support - it will increase bytecode size / RAM usage.

If you want to restart your program from REPL - terminate program by Ctrl + C followed by Ctrl + D "soft reboot" and you'll be able to rerun it :)

@belyalov
Copy link
Owner

Regarding blinking LED - I have no idea, sorry.. (

@Kortenbach
Copy link
Author

Hi Konstantin,
Thank you for looking into this. As you might have guessed I'm a PC software builder (Delphi). I'm not used to "not cleaning up", but I totally understand. I want to thank you for your help! Happy holidays!

@Kortenbach
Copy link
Author

Kortenbach commented Dec 30, 2018

Do you know if there is a way to free all RAM? If there is I could start my project by doing a "free all RAM". I then would'nt have to manually reset my ESP after each software upload...

@belyalov
Copy link
Owner

I'm not sure that I understood you right, but for NodeMCU whenever you upload new software through esptool.py it resets your controller automatically...

If you want to reset it programmatically (reset frees all RAM :)) you can do it by:

import machine

machine.reset()

@Kortenbach
Copy link
Author

If I start my program with machine.reset() it will be caught in an eternal reset loop. I use upycraft for uploading. It apparently doesn't reset the board on loading. What I need is ram.cleanup() or something similar...

@belyalov
Copy link
Owner

Try to use esptool.py (which is kinda industry standard for esp devices), it may help

@Kortenbach
Copy link
Author

Thanks for the tip, I'll try it tomorrow. One more thing... If I want to do some work other than http, what would be the best way to tie into the main loop when using your server?

@belyalov
Copy link
Owner

The best way is to start one more uasyncio "task", like that:

import uasyncio

# ....

async def task():
    # do something

# ...

loop = uasyncio.get_event_loop()
loop.create_task(task)

# run even loop through tinyweb http
http.run()

In order to understand this code better - it is good to invest in "async" programming, like this one

@Kortenbach
Copy link
Author

Thank you very much! That was what I was looking for. Thanks again for making this great library!

@belyalov
Copy link
Owner

Cool! :) Thank you and happy new year!

P.S. if you like library - star it :)

@Kortenbach
Copy link
Author

There you go! 1* cheers!

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

No branches or pull requests

2 participants