micropython / micropython

MicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems

Home Page:https://micropython.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ESP8266 - lwip state is not renitialized on soft-reset

wendlers opened this issue · comments

When using bind on a lwip socket, it looks like the binding is not removed after soft-reset. Binding again to the same port is only possible after hard-reset (when calling close before the soft reset, it is unbound, but I think the socket instances should be closed on soft reset automatically).

import os
>>> os.uname()
(sysname='ESP8266', nodename='ESP8266', release='1.5.2(80914727)', version='v1.6-217-g99fc0d1-dirty on 2016-03-13', machine='ESP module with ESP8266')
>>> import lwip
>>> so = lwip.socket()
>>> so.bind(("", 80))
>>> 
PYB: soft reboot
could not find module 'boot'
>>> 
MicroPython v1.6-217-g99fc0d1-dirty on 2016-03-13; ESP module with ESP8266
Type "help()" for more information.
>>> print(so)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name not defined
>>> import lwip
>>> so = lwip.socket()
>>> so.bind(("", 80))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: 128
>>> so.bind(("", 81))

Yes, proper soft-reset is not implemented. You'll need to use hard reset for now.

OK. I found kind of a "work-around" to avoid hard-reset (I mostly access the serial line of the ESP remotely):

esptool.py --port /dev/ttyUSB0 run

The esptool seams to restart whats programmed to the flash, and after that, the socket is unbound :-)

There is a slightly more severe problem caused by this bug: if you soft-reset and there is an open socket with a callback set (via setsockopt(socket.SOL_SOCKET, 20, cb)), the callback will be invalid after the soft reset and the interpreter may crash if it's triggered. Tested on an esp8266, but I think any port using lwip would be affected.

To reproduce, in micropython run:
import socket
l = socket.socket()
l.bind(socket.getaddrinfo('0.0.0.0', 1234)[0][4])
l.listen(1)
l.setsockopt(socket.SOL_SOCKET, 20, lambda x: x.accept())

Then, connect to port 1234 (with telnet or netcat or whatever) from another host.
Then, soft-reset.
Then, send data on or close the connection to port 1234.

At best, micropython will produce a bogus exception, e.g.:

TypeError: 'int' object is not callable
or
TypeError: 'tuple' object is not callable

At worst, micropython will cause an OS exception:

Fatal exception 9(LoadStoreAlignmentCause):
epc1=0x40211c62, epc2=0x00000000, epc3=0x00000000, excvaddr=0x00000002, depc=0x00000000
ets Jan 8 2013,rst cause:2, boot mode:(3,7)

In commit b2fa1b5 a call to gc_sweep_all() is added to close any remaining sockets upon soft reset. This should help to fix the issues here.