Pico-W Access Point static file webserver

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
Post Reply
HermannSW
Posts: 197
Joined: Wed Nov 01, 2017 7:46 am
Contact:

Pico-W Access Point static file webserver

Post by HermannSW » Wed Jul 06, 2022 10:20 am

I implemented a static file webserver for the Pico-W with Pico-W MicroPython firmware:
https://forums.raspberrypi.com/viewtopi ... 8#p2017461
https://github.com/Hermann-SW/pico-w

Main demo serves my planar_graph_playground (index.html, 7 .js files and new Pico-W favicon.ico):
pico_w_ap.planar_graph_playground.png
pico_w_ap.planar_graph_playground.png
pico_w_ap.planar_graph_playground.png (62.53 KiB) Viewed 2764 times

There is a weird MicroPython bug most likely related to resource usage of webserver.
Any help in debugging/analyzing this problem is greatly appreciated.
All files needed are in the repo (including htmlsvg.js).

Biggest .js file served was htmlsvg.js of size 8096 bytes.
When requesting that file via webserver
https://github.com/Hermann-SW/pico-w/bl ... ain.py#L41

Code: Select all

response = get_file(r)

only 6536 bytes get returned. Workaround in place in the repo is to use minimized JS file htmlsvg.min.js instead.

Surprisingly "get_file()" works if run directly in MicroPython REPL:

Code: Select all

...
KeyboardInterrupt: 
MicroPython v1.19.1 on 2022-07-05; Raspberry Pi Pico W with RP2040
Type "help()" for more information.
>>> def get_file(file_name):
...     with open(file_name, 'rb') as file:
...         return file.read()
... 
>>> print(len(get_file("htmlsvg.js")))
8096
>>> 
Pico-W Access Point static file webserver:
https://github.com/Hermann-SW/pico-w

Tiny MicroPython robots (the PCB IS the robot platform)
viewtopic.php?f=5&t=11454

webrepl_client.py
https://github.com/Hermann-SW/webrepl#webrepl-shell

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Pico-W Access Point static file webserver

Post by jimmo » Wed Jul 06, 2022 12:11 pm

HermannSW wrote:
Wed Jul 06, 2022 10:20 am
Biggest .js file served was htmlsvg.js of size 8096 bytes.
You're going to be much better off reading small chunks of the file and streaming it back over HTTP. Try and avoid ever loading entire files at once.

You can see how microdot does it here: https://github.com/miguelgrinberg/micro ... ot.py#L468
(When you use send_file, it opens the file and sets self.body to the file object, which it then iterates using body_iter, 1024 bytes at a time.

HermannSW
Posts: 197
Joined: Wed Nov 01, 2017 7:46 am
Contact:

Re: Pico-W Access Point static file webserver

Post by HermannSW » Thu Jul 07, 2022 8:06 am

Thanks for that hint !
I implemented it, and for those small files I host it works.
I did ampy put a 229283 bytes file and request that via "http://192.168.4.1/Peek_2022-07-07_02-56.gif" with wget.
Seldom I got the whole size, most times a little less.
That is because "cl.close()" seems not to wait until client has received all data.
I added "sleep_ms(100)", but that does not really help.
Commenting out "cl.close()" for letting garbage collection close the socket does not work at all.
So how can I "wait until all cl.send() data was received by client", before calling "cl.close()" ?
mp.socket.close.better.png
mp.socket.close.better.png
mp.socket.close.better.png (38.44 KiB) Viewed 2719 times
Current main.py:

Code: Select all

import rp2
import network
import machine
import socket
from time import sleep_ms

ap = network.WLAN(network.AP_IF)
ap.config(essid="pico_w_ap")
ap.active(True)

led = machine.Pin('LED', machine.Pin.OUT)
led.off()

# HTTP server with socket
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]

s = socket.socket()
s.bind(addr)
s.listen(10)

print('Listening on', addr)

# Listen for connections
while True:
    try:
        cl, addr = s.accept()
        led.on()
#        print('Client connected from', addr)
        r = str(cl.recv(1024))
#        print(r)
        r = r[r.find("/"):]
        r = r[1:r.find(" ")]
        if (r == ""):
            r = "index.html"

#        print(r)
        
        cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
        send_file_buffer_size = 1024
        with open(r, 'rb') as file:
            while True:
                buf = file.read(send_file_buffer_size)
                if len(buf):
                    cl.send(buf)
                if len(buf) < send_file_buffer_size:
                    break;
        sleep_ms(100)
        cl.close()
        led.off()
        
    except OSError as e:
        cl.close()
#        print('Connection closed')
        led.off()
Here are example wget calls demonstrating the different sizes received:
Peek_2022-07-07_02-56.gif

Code: Select all

$ wget http://192.168.4.1/Peek_2022-07-07_02-56.gif 2>&1 | grep saved
2022-07-07 10:05:31 (617 KB/s) - ‘Peek_2022-07-07_02-56.gif.8’ saved [227395]
$ wget http://192.168.4.1/Peek_2022-07-07_02-56.gif 2>&1 | grep saved
2022-07-07 10:05:33 (629 KB/s) - ‘Peek_2022-07-07_02-56.gif.9’ saved [227127]
$ wget http://192.168.4.1/Peek_2022-07-07_02-56.gif 2>&1 | grep saved
2022-07-07 10:05:36 (625 KB/s) - ‘Peek_2022-07-07_02-56.gif.10’ saved [229283]
$ wget http://192.168.4.1/Peek_2022-07-07_02-56.gif 2>&1 | grep saved
2022-07-07 10:05:41 (623 KB/s) - ‘Peek_2022-07-07_02-56.gif.11’ saved [229283]
$ wget http://192.168.4.1/Peek_2022-07-07_02-56.gif 2>&1 | grep saved
2022-07-07 10:05:47 (615 KB/s) - ‘Peek_2022-07-07_02-56.gif.13’ saved [229283]
$ wget http://192.168.4.1/Peek_2022-07-07_02-56.gif 2>&1 | grep saved
2022-07-07 10:05:49 (617 KB/s) - ‘Peek_2022-07-07_02-56.gif.14’ saved [228611]
$ 
Pico-W Access Point static file webserver:
https://github.com/Hermann-SW/pico-w

Tiny MicroPython robots (the PCB IS the robot platform)
viewtopic.php?f=5&t=11454

webrepl_client.py
https://github.com/Hermann-SW/webrepl#webrepl-shell

Post Reply