Asynchronous Websocket

All ESP8266 boards running MicroPython.
Official boards are the Adafruit Huzzah and Feather boards.
Target audience: MicroPython users with an ESP8266 board.
Post Reply
User avatar
Ventran
Posts: 24
Joined: Sun Jun 21, 2020 4:28 pm
Location: Poland, Europe

Asynchronous Websocket

Post by Ventran » Sat Sep 19, 2020 12:38 pm

Hello,
I want create two classes. One for controll hardware I/O and one for services HTTP Server on ESP8266. So, I create Two instance of class object. But when I run program websocket blocking system.

This is a code:
  • main.py

Code: Select all

from nphttpd import HttServ
from esphw import mySuperHWClass
import uasyncio

a = HttServ()
b = mySuperHWClass()

loop = uasyncio.get_event_loop()
loop.run_until_complete(a.run_socket())
loop.run_until_complete(b.my_hardware())
  • Hardware Controll Class

Code: Select all

from machine import Pin
import uasyncio

p2 = Pin(2, Pin.OUT)
p2.off()

class mySuperHWClass(object):
    def __init__(self):
        self.name = "LED_2"
    
    async def my_hardware(self):
        while True:
            print(self.name + "_ON")
            p2.on()
            await uasyncio.sleep_ms(100)
            p2.off()
            print(self.name + "_OFF")
            await uasyncio.sleep_ms(100)
  • HTTP WebServer

Code: Select all

import socket
from machine import Pin, reset 
from network import WLAN, AP_IF
from time import sleep
import uasyncio

class HttServ(object):
    def __init__(self):
        self.ip_address = '192.168.0.1'  # set your ip here
        self.ssid = 'MicroPython-AP-Ext'
        self.password = '123456789'
        ip_change = self.set_ip()
        if ip_change:
           self.port = 80  # set your port here
           self.conn = None
           self.s = None
           self.run_socket()

    def set_ip(self):
        ap_if = WLAN(AP_IF)
        ap_if.active(True)
        ap_if.config(essid=self.ssid, password=self.password)
        ap_if.ifconfig((self.ip_address,'255.255.255.0','192.168.0.1','192.168.0.1'))
        sleep(1)
        return True

    def connection(self, html):
        try:
            self.conn.sendall(html)
            sleep(0.2)
        except Exception as exc:
            print("Send Error", exc.args[0])
            pass
        finally:
            self.conn.close()

    def parse_request(self):

        self.request = b''

    async def run_socket(self):
        html = None
        try:
            with open("index.html", "r") as fhtml:
                html = fhtml.read()
        except OSError:
            pass
        try:
            self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            #self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.s.bind(("", self.port))
            self.s.listen(1)
        except Exception as exc:
            print("Address in use, restarting", exc.args[0])
            sleep(2)
            reset()
            pass
            
        while True:
            try:
                self.conn, addr = self.s.accept()
            except Exception as exc:
                print("Socket Accept Error ", exc.args[0])
                reset()
                pass
            print('Connected by', addr)

            self.conn.settimeout(1)           
            self.request = b''
            while True:
                try:
                    self.request_chunk = self.conn.recv(8)
                    self.request += self.request_chunk
                    if (len(self.request_chunk) < 8):
                        break
                except Exception as TimeoutException:
                  print("Timeout. Break Loop.", TimeoutException.args[0])
                  break
          
            self.connection(html)
How is appropriate solution for run asynchronous websocket?

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Asynchronous Websocket

Post by pythoncoder » Sat Sep 19, 2020 1:57 pm

Your use of uasyncio is incorrect: the first call to run_until_complete will block until a.run_socket() terminates, which I don't think is what you intended. A minor point is that run_until_complete is deprecated under uasyncio V3. Assuming you're running this version (if not, update your firmware), I suggest the following for main.py:

Code: Select all

from nphttpd import HttServ
from esphw import mySuperHWClass
import uasyncio

a = HttServ()
b = mySuperHWClass()

async def main():
    asyncio.create_task(a.run_socket())
    await b.my_hardware()

try:
    asyncio.run(main())
finally:
    asyncio.new_event_loop()  # Clear retained state
You may want to look at my tutorial for guidance on setting up a global exception handler which greatly simplifies debugging.
Peter Hinch
Index to my micropython libraries.

User avatar
Ventran
Posts: 24
Joined: Sun Jun 21, 2020 4:28 pm
Location: Poland, Europe

Re: Asynchronous Websocket

Post by Ventran » Sat Sep 19, 2020 7:25 pm

Ok, I tried this solution but LED still not blinking. So, I think in this situation is necessary use non-blocking socket. How I can do this?

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Asynchronous Websocket

Post by pythoncoder » Sun Sep 20, 2020 11:06 am

This is quite an involved topic requiring an essay rather than a forum post ;) You might like to look at this micropython-iot project which uses nonblocking sockets. In particular look at https://github.com/peterhinch/micropyth ... nt.py#L398 and https://github.com/peterhinch/micropyth ... nt.py#L364

There are also tutorials online.
Peter Hinch
Index to my micropython libraries.

User avatar
Ventran
Posts: 24
Joined: Sun Jun 21, 2020 4:28 pm
Location: Poland, Europe

Re: Asynchronous Websocket

Post by Ventran » Mon Sep 21, 2020 5:49 pm

Thank you for answer @pythoncoder. I found propably good solution using uselect

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Asynchronous Websocket

Post by pythoncoder » Wed Sep 23, 2020 6:00 am

Perhaps I should have pointed out this option. As you have discovered the alternative to nonblocking sockets is to use blocking sockets and delegate polling to uselect. Note that uselect.select is deprecated; .ipoll is the most efficient way to use the mechanism.

The poll mechanism is under review for release 1.14 but I would expect the API to retain compatibility with existing docs and code.
Peter Hinch
Index to my micropython libraries.

Post Reply