_threads, sockets and interruption with ESP32

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
ghusson
Posts: 9
Joined: Tue Jan 23, 2018 4:41 pm

_threads, sockets and interruption with ESP32

Post by ghusson » Tue Jan 23, 2018 4:47 pm

Hello,

I am trying to stabilize a FTP server I found here : https://github.com/lemariva/uPyPortal
For that, I loop on client connection.
But, when doing Ctrl + C on the python console, the thread doesn't exit properly. It seems to be running in the background of the os (but in a bad state because FTP client connection gives nothing). And then when the ftp server starts again, it can't bind the socket.
Things I am trying to do thing right. Is there for example a way to cach an interrupt/exit signal and setting a callback in the FTP class (that will properly clothe the sockets) ?

Thank you

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: _threads, sockets and interruption with ESP32

Post by dhylands » Tue Jan 23, 2018 6:30 pm

Generally speaking (i'm not sure about the ESP32) you can use a try/except handler to grab the KeyboardInterrupt exception and use that to perform cleanup (i.e. close open sockets etc). Here's a simple example for the pyboard:

Code: Select all

import pyb

try:
  while 1:
        print('hello')
        pyb.delay(100)
except KeyboardInterrupt:
  print('Caught Control-C')

OutoftheBOTS_
Posts: 847
Joined: Mon Nov 20, 2017 10:18 am

Re: _threads, sockets and interruption with ESP32

Post by OutoftheBOTS_ » Tue Jan 23, 2018 9:32 pm

I normally find that not only do I need to catch keyboard interrupt and deint but I need to catch all errors to deint while debugging my programs. Because if your debugging a program and it crashes and ends then you fix the program and re-run it, all the previous thread, PWM, SPI and anything else created still are live and micropython creates another instance on top of the last one. I have become very good at having a a try and except catch and at the except making sure that I have deint everything or problems will arise.

ghusson
Posts: 9
Joined: Tue Jan 23, 2018 4:41 pm

Re: _threads, sockets and interruption with ESP32

Post by ghusson » Thu Jan 25, 2018 2:32 pm

I don't know if I am doing something wrong, but is seems that the except is not reached in a "background" running thread when I do a Ctrl+C on the Python command line...

ghusson
Posts: 9
Joined: Tue Jan 23, 2018 4:41 pm

Re: _threads, sockets and interruption with ESP32

Post by ghusson » Thu Jan 25, 2018 3:26 pm

Yes, understood.

I made a mistake, I wanted to speak of Ctrl + D.
Anyway, in a background thread, the Ctrl + C is not caught.
The Ctrl + D is not caught either.

In my example, the previously ran thread is not interrupted, and a next run of the thread fail in binding the sockets.

Do you have an idea ?

OutoftheBOTS_
Posts: 847
Joined: Mon Nov 20, 2017 10:18 am

Re: _threads, sockets and interruption with ESP32

Post by OutoftheBOTS_ » Thu Jan 25, 2018 7:32 pm

In the except you will need to have a line of code that stops the thread. I am using laboris firmware and the code to stop a thread is _thread.stop(th_id) see https://github.com/loboris/MicroPython_ ... dstopth_id

I also suggest that in the except you stop anything else that you have started like SPI, PWM, PIN states

Just remember control C doesn't free all memory it just stop main program but all other allocation is still active

Also FYI laboris also has a FTP server module that I use all the time to transfer/edit files on my ESP32 see https://github.com/loboris/MicroPython_ ... /ftpserver

ghusson
Posts: 9
Joined: Tue Jan 23, 2018 4:41 pm

Re: _threads, sockets and interruption with ESP32

Post by ghusson » Fri Jan 26, 2018 10:27 am

Ok, thank you for your answers. I am rather new to Python and totally new on Python.
I wrote some test code here under. The filename is "test_threaded_socket.py". I have some questions...

1) Why __name__ returns the python file less ".py" rather than main ?
2) I think it is normal but surprising that using "if __name__ == "test_threaded_socket": main()" trick, it works only one time when doing "import test_threaded_socket".
3) How do you do when developping some code ? Always quit your serial terminal, use ampy, return to your serial terminal and reboot the ESP32 ? Is there a simpler way (I am trying to increase dev speed using FTP server on the ESP32...).
4) The example code show that KeyboardInterrupt is not caught if using "all exception" catching (except Exception as err:), thus I have to declare it specially (except KeyboardInterrupt:).
5) This code should be rock solid and ever work. But with two connection reset (Ctrl + C at client side), it does not restart. I may be missing something. Maybe the "finally" statement is not reentrant, and I should do the restart after the try/catch ?
6) When using Ctrl + D, what sould be a method for that a FTP server thread either still continue to run background, either be stopped and re-run ?

Thank you for your help.

Code: Select all

import socket
import network
import uos
import gc
import _thread
import time


class threaded_socket():

    def __init__(self):
        self.mysocket = None
        self.myclient = None


    def start_socket_service(self):
        print("Starting socket service, use : \"nc <ESP32 IP address> 23\"")
        self.mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.mysocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.mysocket.bind(socket.getaddrinfo("0.0.0.0", 23)[0][4])
        self.mysocket.listen(1)
        self.mysocket.settimeout(None)
        
        self.wlan = network.WLAN(network.AP_IF)
        if self.wlan.active():
            ifconfig = self.wlan.ifconfig()
        else:
            self.wlan = network.WLAN(network.STA_IF)
            if self.wlan.active():
                ifconfig = self.wlan.ifconfig()
            else:
                dbg("No active connection")
                return
        
        self.myclient, remote_addr = self.mysocket.accept()
        self.myclient.settimeout(10)
        self.myclient.sendall("Hello, this is the ESP8266/ESP32.\r\n")
        line_count = 1
        while True:
            data = self.myclient.readline().decode("utf-8").rstrip("\r\n")
            print(data)
            self.myclient.sendall(str(line_count) + ": " + data + "\r\n" )
            line_count = line_count +1

    def loop_service(self):
        try:
            self.start_socket_service()
        except KeyboardInterrupt:
            print("Keyboard interruption caught")
            self.cleaning()
        except Exception as err:
            print("exception caught :" + str(err))
            self.cleaning()
        finally:
            print("Socket service will be restarted in 2 seconds")
            time.sleep(2)
            self.start_socket_service()
    
    def cleaning(self):
        print("Closing socket")
        if self.myclient is not None:
            self.myclient.close()
        if self.mysocket is not None:
            self.mysocket.close()


def main():
    import test_threaded_socket
    thsk = test_threaded_socket.threaded_socket()
    thsk.loop_service()

print("This lib name is: " + __name__)
if __name__ == "test_threaded_socket": main()



OutoftheBOTS_
Posts: 847
Joined: Mon Nov 20, 2017 10:18 am

Re: _threads, sockets and interruption with ESP32

Post by OutoftheBOTS_ » Fri Jan 26, 2018 10:55 am

ok I am using loboris firmware for fast development because it has telent and FTP builtin to the firmware.

I run a Telent repl over putty and use filezilla to edit transfer files. In filezilla you can specify what prograsm you want to open what files. I have specified note++ to open *.py . So I can open filezilla and log on to the ESP32 board then right click on any .py file to edit it with note++ but of course you can use what ever IDE you like.

Once I have edited the .py file and I want to run it I switch to the putty window running telnet to the ESP32 board and execute import file.

I have a function declared in my boot.py file called reload() and when I have executed import file and then edited the file with notye++ and want to re run it i execute reload(file)

This is what I have added to my boot.py, mind I do think that laboris has now added this standard to the boot.py now

Code: Select all

import sys
def reload(mod):
    mod_name = mod.__name__
    del sys.modules[mod_name]
    return __import__(mod_name)
    

ghusson
Posts: 9
Joined: Tue Jan 23, 2018 4:41 pm

Re: _threads, sockets and interruption with ESP32

Post by ghusson » Fri Jan 26, 2018 1:00 pm

Thanks for confirming that I will have less headaches with Loboris micropython :-) Telnet and FTP per default is what I am looking for.
Thank you for the reload code, it helps me !

In return, this is a scipt I use in linux command line with lftp. It uses size of files for syncing. But with loboris and NTP, you can remove "--ignore-time" option I think.

Code: Select all

#!/bin/bash
HOST='192.168.250.179'
USER='myuser'
PASS='myuser'
TARGETFOLDER='/'
SOURCEFOLDER='/home/ghusson/esp32_micropython/'

lftp -f "
open $HOST
user $USER $PASS
lcd $SOURCEFOLDER
mirror --no-recursion --reverse --ignore-time --delete --verbose --include="^.*\.py$" --parallel=1 $SOURCEFOLDER $TARGETFOLDER
bye
"

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: _threads, sockets and interruption with ESP32

Post by Roberthh » Fri Jan 26, 2018 1:57 pm

I'm using a different style and do a lot of editing on the device itself with the pye() editor. It is embedded in flash at the loboris port, and for the other devices a made my own image with the editor as frozen bytecode. You'll find it here: https://github.com/robert-hh/Micropython-Editor The files size you can edit is limited by the heap size. On a MP ESP32 port w/lo PSRAM it's at least 500 lines, with PSRAM it's like 20000 lines.
For file tranfer I use ftp. A server is embedded in the Pycom and loboris ports. For esp8266 and the MP esp32 port I use a server which I tailored for my needs: https://github.com/robert-hh/ESP8266-FTP-Server
uftpd.py runs on esp8266 only, while ftp.py runs on the esp32 too. Both variants support active mode, meaning that command line ftp on windows works. As client I use the Firftp extension of Firefox, which is anhow open most of the time. For me, it's easier to use than FileZilla. And you can start editing out of the client too. Pyboard is a little bit different. As long as you enable it's mass storag mode, you can access it with you file manager. But you have to take care when storing that the content has been synched. That's not always obvious. And it you disable mass storage mode to avoid file system corruption, than you're limited to ampy or rshell, or ...., unless you added a LAN/WiFi interface. Then you can use ftp again.
For restarting after code changes I simply press Ctrl-D or the reset button. It's fewer keystrokes, and brings the devices in a well defined state.

Post Reply