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

Connection refused after some time/multiple requests on Wiznet5k Pico W5100S #41

Open
IakovlevAA opened this issue Mar 6, 2023 · 9 comments

Comments

@IakovlevAA
Copy link

IakovlevAA commented Mar 6, 2023

Adafruit CircuitPython 8.0.3 on 2023-02-23; Raspberry Pi Pico with rp2040
I am building HTTP server with W5100S and can't get this library to work properly because of adafruit_wiznet5k_socket, i think. I cut some code to demonstrate the problem, so if you need explanation in something, i'll explain
code.py

import board
import busio
import digitalio
import time

import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket
from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K
from adafruit_httpserver.request import HTTPRequest
from adafruit_httpserver.response import HTTPResponse
from adafruit_httpserver.server import HTTPServer
from adafruit_httpserver.methods import HTTPMethod

IP_ADDRESS = (192, 168, 1, 244)
SUBNET_MASK = (255, 255, 255, 0)
GATEWAY_ADDRESS = (192, 168, 1, 1)
DNS_SERVER = (8, 8, 8, 8)
W5x00_RSTn = board.GP20
ethernetRst = digitalio.DigitalInOut(W5x00_RSTn)
ethernetRst.direction = digitalio.Direction.OUTPUT
ethernetRst.value = False
time.sleep(1)
ethernetRst.value = True

SPI0_SCK = board.GP18
SPI0_TX = board.GP19
SPI0_RX = board.GP16
SPI0_CSn = board.GP17
spi_bus = busio.SPI(SPI0_SCK, MOSI=SPI0_TX, MISO=SPI0_RX)

cs = digitalio.DigitalInOut(SPI0_CSn)
eth = WIZNET5K(spi_bus, cs, is_dhcp=False)


def routes(server):
    @server.route("/", HTTPMethod.POST)
    def base(request: HTTPRequest):
        raw_text = request.body.decode("UTF-8")
        print(raw_text)
        with open('index.html', 'r') as f:
            html_string = f.read()
        f.close()
        with HTTPResponse(request) as response:
            response.send(html_string, content_type="text/html")

    @server.route("/", HTTPMethod.GET)
    def base(request: HTTPRequest):
        with open('index.html', 'r') as f:
            html_string = f.read()
        f.close()
        with HTTPResponse(request) as response:
            response.send(html_string, content_type="text/html")


def raise_serv():
    eth.ifconfig = (IP_ADDRESS, SUBNET_MASK, GATEWAY_ADDRESS, DNS_SERVER)

    socket.set_interface(eth)
    server_ip = eth.pretty_ip(eth.ip_address)

    print(f"Listening on http://{server_ip}:80")
    return HTTPServer(socket), server_ip


while True:
    print("Raising server..")
    server, server_ip = raise_serv()
    routes(server)
    server.start(str(server_ip))
    print("Server raised.")
    while True:
        try:
            server.poll()
        except OSError as error:
            print(error)
            continue

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style type="text/css">
    body {
      margin: 0;
    }
    .wrapper {
      background: #ffffff;
      box-sizing: border-box;
      margin: 0 auto;
      max-width: 1300px;
      min-height: 100vh;
    }
    .container {
      display: grid;
      grid-template-columns: 340px repeat(6, auto);
    }
    .el {
      padding: 2px;
      font-size: 17px;
    }
    input {
      text-align: right;
    }
    .btn-apply {
      text-align: right;
      padding: 15px 2px;
    }
    .row-border{
    border-top: 1px solid rgb(191, 252, 198);
    grid-column: 1 / 8;
    }
    .square {
      position: relative;
      padding-left: 17px;
      margin-left: 5px;
    }
    .square:after {
      position:absolute;
      content:"";
      width:17px;
      height:17px;
      top:2px;
      left:0;
    }
    .square-green {
      background-color:rgb(0, 190, 0);
    }
    .square-red {
      background-color:rgb(190, 0, 0);
    }
  </style>
</head>
<body>
  <div class="wrapper">
  <form action="" method="post">
    <div class="container">
      <div class="el el-1"><b></b></div>
      <div class="el el-2" align="right"><b>1</b></div>
      <div class="el el-3" align="right"><b>2</b></div>
      <div class="el el-4" align="right"><b>3</b></div>
      <div class="el el-5" align="right"><b>4</b></div>
      <div class="el el-6" align="right"><b>5</b></div>
      <div class="el el-7" align="right"><b>6</b></div>
      
      <div class="row-border"></div><div class="el el-1"></div>
      <div class="el el-2"><input name="name1" type="text" value="1" style="width: 95%; color:rgb(0, 150, 17)"></div>
      <div class="el el-3"><input name="name2" type="text" value="2" style="width: 95%; color:rgb(0, 150, 17)"></div>
      <div class="el el-4"><input name="name3" type="text" value="3" style="width: 95%; color:rgb(0, 150, 17)"></div>
      <div class="el el-5"><input name="name4" type="text" value="4" style="width: 95%; color:rgb(0, 150, 17)"></div>
      <div class="el el-6"><input name="name5" type="text" value="5" style="width: 95%; color:rgb(0, 150, 17)"></div>
      <div class="el el-7"><input name="name6" type="text" value="6" style="width: 95%; color:rgb(0, 150, 17)"></div>
      <div class="row-border"></div>

      <div class="el el-1"></div>
      <div class="el el-2" align="right">1<span class="square square-$COLOR1"></span></div>
      <div class="el el-3" align="right">1<span class="square square-$COLOR2"></span></div>
      <div class="el el-4" align="right">1<span class="square square-$COLOR3"></span></div>
      <div class="el el-5" align="right">1<span class="square square-$COLOR4"></span></div>
      <div class="el el-6" align="right">1<span class="square square-$COLOR5"></span></div>
      <div class="el el-7" align="right">1<span class="square square-$COLOR6"></span></div>
      <div class="row-border"></div>

      <div class="el el-1"></div>
      <div class="el el-2"></div>
      <div class="el el-3"></div>
      <div class="el el-4"></div>
      <div class="el el-5"></div>
      <div class="el el-6"></div>
      <div class="el el-7"></div>
      <div class="row-border"></div>

      <div class="el el-1" style="padding-left: 10px;"></div>
      <div class="el el-2" align="right"><input type="radio" name="log_level1" value="1" id="" $CHECKEDTRUE1></div>
      <div class="el el-3" align="right"><input type="radio" name="log_level2" value="1" id="" $CHECKEDTRUE2></div>
      <div class="el el-4" align="right"><input type="radio" name="log_level3" value="1" id="" $CHECKEDTRUE3></div>
      <div class="el el-5" align="right"><input type="radio" name="log_level4" value="1" id="" $CHECKEDTRUE4></div>
      <div class="el el-6" align="right"><input type="radio" name="log_level5" value="1" id="" $CHECKEDTRUE5></div>
      <div class="el el-7" align="right"><input type="radio" name="log_level6" value="1" id="" $CHECKEDTRUE6></div>
      <div class="row-border"></div>

      <div class="el el-1" style="padding-left: 10px;"></div>
      <div class="el el-2" align="right"><input type="radio" name="log_level1" value="0" id="" $CHECKEDFALSE1></div>
      <div class="el el-3" align="right"><input type="radio" name="log_level2" value="0" id="" $CHECKEDFALSE2></div>
      <div class="el el-4" align="right"><input type="radio" name="log_level3" value="0" id="" $CHECKEDFALSE3></div>
      <div class="el el-5" align="right"><input type="radio" name="log_level4" value="0" id="" $CHECKEDFALSE4></div>
      <div class="el el-6" align="right"><input type="radio" name="log_level5" value="0" id="" $CHECKEDFALSE5></div>
      <div class="el el-7" align="right"><input type="radio" name="log_level6" value="0" id="" $CHECKEDFALSE6></div>
      <div class="row-border"></div>

      <div class="el el-1"></div>
      <div class="el el-2"></div>
      <div class="el el-3"></div>
      <div class="el el-4"></div>
      <div class="el el-5"></div>
      <div class="el el-6"></div>
      <div class="el el-7"></div>
      <div class="row-border"></div>
      <div class="el el-1" style="padding-left: 10px;"></div>
      <div class="el el-2"><input name="impulse_time1" type="text" style="width: 95%;" value="1"></div>
      <div class="el el-3"><input name="impulse_time2" type="text" style="width: 95%;" value="1"></div>
      <div class="el el-4"><input name="impulse_time3" type="text" style="width: 95%;" value="1"></div>
      <div class="el el-5"><input name="impulse_time4" type="text" style="width: 95%;" value="1"></div>
      <div class="el el-6"><input name="impulse_time5" type="text" style="width: 95%;" value="1"></div>
      <div class="el el-7"><input name="impulse_time6" type="text" style="width: 95%;" value="1"></div>
      <div class="row-border"></div>

      <div class="el el-1" style="padding-left: 10px;"></div>
      <div class="el el-2"><button name="impulse_line" value="1" style="width: 100%;">1</button></div>
      <div class="el el-3"><button name="impulse_line" value="2" style="width: 100%;">2</button></div>
      <div class="el el-4"><button name="impulse_line" value="3" style="width: 100%;">3</button></div>
      <div class="el el-5"><button name="impulse_line" value="4" style="width: 100%;">4</button></div>
      <div class="el el-6"><button name="impulse_line" value="5" style="width: 100%;">5</button></div>
      <div class="el el-7"><button name="impulse_line" value="6" style="width: 100%;">6</button></div>
      <div class="row-border"></div>
    </div>
    <div class="btn-apply">
      <button type="submit" name="apply_changes" value="1">a</button>
    </div>
    </form>
  </div>
</body>
</html>

After first request, server stops responding after some time, giving "ERR_CONNECTION_REFUSED" in browsers. Also, multiple POST requests(2 fast clicks on button) are doing the same, but faster. I've done some tests with different systems, that gave me no results. The only thing, that helped me is "Incognito" mode. Other versions of CircuitPython don't help.
When I interrupt programm with CTRL+C it gives.

  File "/lib/adafruit_httpserver/response.py", line 167, in send
  File "/lib/adafruit_httpserver/response.py", line 242, in _send_bytes
  File "/lib/adafruit_wiznet5k/adafruit_wiznet5k_socket.py", line 422, in send
  File "/lib/adafruit_wiznet5k/adafruit_wiznet5k.py", line 1034, in socket_write
  File "/lib/adafruit_wiznet5k/adafruit_wiznet5k.py", line 614, in write
@michalpokusa
Copy link
Contributor

Personally I do not own a W5100S, so I can't test it myself. What do you mean by Incognito helped, is it working fine when accessed via Incognito?

Also not connected, but it is not necessary to explicitly f.close() after using with open() statement, it is done automatically.

@IakovlevAA
Copy link
Author

I can't explain properly, but when I use Incognito socket doesn't block(or smth), and server is responding to requests.Maybe not Incognito sends several requests?
Recently, I was testing response.py function _send_bytes, and found out that it stucks in while loop.

7361 #bytes_to_sent
0 #bytes_sent

What helped me, is breaking this loop

        while bytes_sent < bytes_to_send:
            if bytes_sent == 0:
                i = i + 1
                if i == 10:
                    break

@michalpokusa
Copy link
Contributor

michalpokusa commented Mar 6, 2023

Please check two things:

  1. In your base function add print(request.path, request.connection), sometimes Chrome automatically requests favicon and if it can't find it, blocks the socket, that might be causing the stuck

  2. Try requesting your website using curl and REST Clients like Postman or If you are using VSCode, e.g. Thunder Client

If the server works that means the sockets are being passed correctly, otherwise it wouldn't work even in incognito.

That is interesting that the loops repeats infinitely.
You can try printing the bytes_sent, bytes_to_send just under while in _send_bytes to see if it increases in each iteration, if not, there is something wrong with it.

@IakovlevAA
Copy link
Author

Made some tests to watch socket's behaviour. It looks like I could get it to work with 1-2 requests at time, but not with 3 or more.
Here is output for bytes.

Raising server..
Listening on http://192.168.1.244:80
Server raised.
0 #bytes_sent
85 #bytes_to_send
0
7362
2048
7362
4096
7362
6144
7362
/ <socket object at 0x2002aa50>
0
85
0
7362
2048
7362
4096
7362
6144
7362
/ <socket object at 0x20011c50>
0
85
0
7362
0
7362
0
7362
0
7362
0
7362
/ <socket object at 0x2000d1a0>
0
85
0
7362
2048
7362
4096
7362
6144
7362
/ <socket object at 0x200182b0>
0
85
0
7362
0
7362
0
7362
0
7362
0
7362

Apparently, this means main problem is with wiznet5k socket. I think it blocks, when perfoming too many write() functions, that are called in _send_bytes()

@michalpokusa
Copy link
Contributor

michalpokusa commented Mar 6, 2023

Seems like it, you might try to pinpoint the location of the block by placing prints inside write_socket method (after downloading .py version of wiznet5k module of course). Providing exact line that causes problem will probably speed up the process of fixing it.

Until the underlying problem in wiznet5k is solved it might be usuful to patch that in httpserver, i will try something similar to your i==10 method. Even after the fix in wiznet5k that might stay, as it would be a failsafe it socket is not sending data for any reason.

@IakovlevAA
Copy link
Author

Sorry, forgot to show new traceback

 File "/lib/adafruit_wiznet5k/adafruit_wiznet5k_socket.py", line 380, in accept
  File "/lib/adafruit_wiznet5k/adafruit_wiznet5k_socket.py", line 261, in _status
  File "/lib/adafruit_wiznet5k/adafruit_wiznet5k.py", line 668, in socket_status
  File "/lib/adafruit_wiznet5k/adafruit_wiznet5k.py", line 1125, in _read_snsr
  File "/lib/adafruit_wiznet5k/adafruit_wiznet5k.py", line 1168, in _read_socket
  File "/lib/adafruit_wiznet5k/adafruit_wiznet5k.py", line 578, in read

It's no longer problem with write_socket , now read_socket. Looks like it gets some status, that blocks socket, until restart

@michalpokusa
Copy link
Contributor

I did some testing and I back out from my previous statement about patching that in httpserver. Althought it might indeed make the code work with current wiznet5k, it is not a proper solution as the amount of time that the EAGAIN error is raised depends heavily on the connection speed and buffer size. It is possible that it is called even more than 10 times in a row during normal operation.

I think your best bet is creating an issue on wiznet5k repository and waiting for the fix there.

@IakovlevAA
Copy link
Author

I've noticed that, socket stucks in recv function in adafruit_wiznet5k_socket.py. Socket is blocking, after receiving empty bytes, so I did the same thing as in _send_bytes:

recv function in adafruit_wiznet5k_socket.py

i = 0
        while not self._available():
            if self._timeout and 0 < self._timeout < time.monotonic() - stamp:
            i = i + 1
            if (self._timeout and 0 < self._timeout < time.monotonic() - stamp) or (i == 20):
                break
            time.sleep(0.05)
        bytes_on_socket = self._available()

After that, my problem was particularly solved. Particularly, because it still blocking if doing too many requests, but not blocking after time

@foxy82
Copy link
Contributor

foxy82 commented Apr 3, 2023

Interestingly I'm just debugging with a view to raising an issue - what would seem to be a similar problem on the pico w - requesting 3 or 4 connections in quick succession locks the board up and polling stops for me with no apparent exception.

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

3 participants