Skip to content

Examples of using adafruit_httpserver with asyncio #70

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

Merged
merged 2 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ a running total of the last 10 samples.
:emphasize-lines: 29,38
:linenos:


If you need to perform some action periodically, or there are multiple tasks that need to be done,
it might be better to use ``asyncio`` module to handle them, which makes it really easy to add new tasks
without needing to manually manage the timing of each task.

``asyncio`` **is not included in CircuitPython by default, it has to be installed separately.**

.. literalinclude:: ../examples/httpserver_start_and_poll_asyncio.py
:caption: examples/httpserver_start_and_poll_asyncio.py
:emphasize-lines: 5,33,42,45,50,55-62
:linenos:

Server with MDNS
----------------

Expand Down Expand Up @@ -315,12 +327,15 @@ the client and the server.
Remember, that because Websockets also receive data, you have to explicitly call ``.receive()`` on the ``Websocket`` object to get the message.
This is anologous to calling ``.poll()`` on the ``Server`` object.

The following example uses ``asyncio``, which has to be installed separately. It is not necessary to use ``asyncio`` to use Websockets,
but it is recommended as it makes it easier to handle multiple tasks. It can be used in any of the examples, but here it is particularly useful.

**Because of the limited number of concurrently open sockets, it is not possible to process more than one Websocket response at the same time.
This might change in the future, but for now, it is recommended to use Websocket only with one client at a time.**

.. literalinclude:: ../examples/httpserver_websocket.py
:caption: examples/httpserver_websocket.py
:emphasize-lines: 12,21,67-73,83,90
:emphasize-lines: 12,20,65-72,88,99
:linenos:

Multiple servers
Expand Down
2 changes: 1 addition & 1 deletion examples/httpserver_sse.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023 Dan Halbert for Adafruit Industries
# SPDX-FileCopyrightText: 2023 Michał Pokusa
#
# SPDX-License-Identifier: Unlicense

Expand Down
62 changes: 62 additions & 0 deletions examples/httpserver_start_and_poll_asyncio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# SPDX-FileCopyrightText: 2023 Michał Pokusa
#
# SPDX-License-Identifier: Unlicense

from asyncio import create_task, gather, run, sleep as async_sleep
import socketpool
import wifi

from adafruit_httpserver import (
Server,
REQUEST_HANDLED_RESPONSE_SENT,
Request,
FileResponse,
)


pool = socketpool.SocketPool(wifi.radio)
server = Server(pool, "/static", debug=True)


@server.route("/")
def base(request: Request):
"""
Serve the default index.html file.
"""
return FileResponse(request, "index.html")


# Start the server.
server.start(str(wifi.radio.ipv4_address))


async def handle_http_requests():
while True:
# Process any waiting requests
pool_result = server.poll()

if pool_result == REQUEST_HANDLED_RESPONSE_SENT:
# Do something only after handling a request
pass

await async_sleep(0)


async def do_something_useful():
while True:
# Do something useful in this section,
# for example read a sensor and capture an average,
# or a running total of the last 10 samples
await async_sleep(1)

# If you want you can stop the server by calling server.stop() anywhere in your code


async def main():
await gather(
create_task(handle_http_requests()),
create_task(do_something_useful()),
)


run(main())
55 changes: 38 additions & 17 deletions examples/httpserver_websocket.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# SPDX-FileCopyrightText: 2023 Dan Halbert for Adafruit Industries
# SPDX-FileCopyrightText: 2023 Michał Pokusa
#
# SPDX-License-Identifier: Unlicense

from time import monotonic
from asyncio import create_task, gather, run, sleep as async_sleep
import board
import microcontroller
import neopixel
Expand All @@ -17,9 +17,7 @@

pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)


websocket: Websocket = None
next_message_time = monotonic()

HTML_TEMPLATE = """
<html lang="en">
Expand Down Expand Up @@ -75,17 +73,40 @@ def connect_client(request: Request):


server.start(str(wifi.radio.ipv4_address))
while True:
server.poll()

# Check for incoming messages from client
if websocket is not None:
if (data := websocket.receive(True)) is not None:
r, g, b = int(data[1:3], 16), int(data[3:5], 16), int(data[5:7], 16)
pixel.fill((r, g, b))

# Send a message every second
if websocket is not None and next_message_time < monotonic():
cpu_temp = round(microcontroller.cpu.temperature, 2)
websocket.send_message(str(cpu_temp))
next_message_time = monotonic() + 1

async def handle_http_requests():
while True:
server.poll()

await async_sleep(0)


async def handle_websocket_requests():
while True:
if websocket is not None:
if (data := websocket.receive(fail_silently=True)) is not None:
r, g, b = int(data[1:3], 16), int(data[3:5], 16), int(data[5:7], 16)
pixel.fill((r, g, b))

await async_sleep(0)


async def send_websocket_messages():
while True:
if websocket is not None:
cpu_temp = round(microcontroller.cpu.temperature, 2)
websocket.send_message(str(cpu_temp), fail_silently=True)

await async_sleep(1)


async def main():
await gather(
create_task(handle_http_requests()),
create_task(handle_websocket_requests()),
create_task(send_websocket_messages()),
)


run(main())