I am building a CircuitPython weather station based on Adafruit M4 board stacked with Adafruit ESP32 AirLift module. Both work fine together when it comes to making http calls (via Adafruit_requests library). However I would like to build a HTTP server serving weather measures instead. And here is a problem: all Adafuit examples are based on ESP32 or Raspberry Pico boards natively fitted with WIFI. Adafuit M4 board doesn't have the same core python packages since it doesn't sport WIFI. So example written for Pico doesn't work on M4 with ESP AirLift, there are a number of packages missing to do so (like wifi and socket), adafuit_esp package has to be used instead. This package however seems to be incompatible with Adafuit HTTPServer: https://docs.circuitpython.org/projects/httpserver/en/stable/index.html HttpServer requires socket object as a parameter to be initialized:

esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
server = HTTPServer(socket)

this code fails when I'm trying to start the server:

    print("Listening on http://%s:80" % esp.pretty_ip(esp.ip_address))
#  if the server fails to begin, restart the pico w
except OSError:

and the error is:

Creating display
Creating server socket
ESP32 found and in idle mode
Firmware vers. bytearray(b'1.2.2\x00')
MAC addr: ...
Connecting to AP...
Connected to jama   RSSI: -27
My IP address is
Creating HTTP Server...
starting server..
Traceback (most recent call last):
  File "<stdin>", line 175, in <module>
  File "adafruit_httpserver/server.py", line 91, in start
AttributeError: 'socket' object has no attribute 'bind'

So it looks like that's not the right socket ? it works for ESP and Requests libs but not for HTTPServer.

Here are imports:

from adafruit_esp32spi import adafruit_esp32spi
import adafruit_esp32spi.adafruit_esp32spi_socket as socket

My question then (or two) is if anyone know a workaround for this issue and/or a different socket or server packages I can use instead ?

Here is the full code for the reference, it is not finished and contains more stuff that described here but unrelated to the issue:

import os
import time
import busio
import board
import microcontroller
from digitalio import DigitalInOut, Direction
import adafruit_bh1750
# import adafruit_requests as requests
from adafruit_bme280 import basic as adafruit_bme280
from adafruit_httpserver.server import HTTPServer
from adafruit_httpserver.request import HTTPRequest
from adafruit_httpserver.response import HTTPResponse
from adafruit_httpserver.methods import HTTPMethod
from adafruit_httpserver.mime_type import MIMEType
from adafruit_epd.epd import Adafruit_EPD
from adafruit_epd.il0373 import Adafruit_IL0373
from adafruit_esp32spi import adafruit_esp32spi
import adafruit_esp32spi.adafruit_esp32spi_socket as socket

    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")

#  onboard LED setup
# led = DigitalInOut(board.LED)
# led.direction = Direction.OUTPUT
# led.value = False

i2c = board.I2C()  # uses board.SCL and board.SDA
bh1750 = adafruit_bh1750.BH1750(i2c)
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, 0x76)
bme280.sea_level_pressure = 1012.50

spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
ecs = DigitalInOut(board.D9)
dc = DigitalInOut(board.D10)
srcs = DigitalInOut(board.D6)  # can be None to use internal memory
rst = None #DigitalInOut(board.D1)  # can be None to not use this pin
busy = None #DigitalInOut(board.D5)  # can be None to not use this pin

esp32_cs = DigitalInOut(board.D13)
esp32_ready = DigitalInOut(board.D11)
esp32_reset = DigitalInOut(board.D12)

print("Creating display")
display = Adafruit_IL0373(
    296,  # 2.9" Tri-color display

display.set_black_buffer(1, True)
display.set_color_buffer(1, True)
display.rotation = 1
# display.display()
# display.text(splash, 12, 12, Adafruit_EPD.BLACK, size=1)

#  connect to network
print("Creating server socket")
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)

if esp.status == adafruit_esp32spi.WL_IDLE_STATUS:
    print("ESP32 found and in idle mode")
print("Firmware vers.", esp.firmware_version)
print("MAC addr:", [hex(i) for i in esp.MAC_address])

for ap in esp.scan_networks():
    print("\t%s\t\tRSSI: %d" % (str(ap["ssid"], "utf-8"), ap["rssi"]))

print("Connecting to AP...")
while not esp.is_connected:
        esp.connect_AP(secrets["ssid"], secrets["password"])
    except OSError as e:
        print("could not connect to AP, retrying: ", e)
print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)
print("My IP address is", esp.pretty_ip(esp.ip_address))

print("Creating HTTP Server...")
server = HTTPServer(socket)

#  font for HTML
font_family = "monospace"

#  the HTML script
#  setup as an f string
#  this way, can insert string variables from code.py directly
#  of note, use {{ and }} if something from html *actually* needs to be in brackets
#  i.e. CSS style formatting
def webpage():
    html = f"""
    <!DOCTYPE html>
    <meta http-equiv="Content-type" content="text/html;charset=utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    html{{font-family: {font_family}; background-color: lightgrey;
    display:inline-block; margin: 0px auto; text-align: center;}}
      h1{{color: deeppink; width: 200; word-wrap: break-word; padding: 2vh; font-size: 35px;}}
      p{{font-size: 1.5rem; width: 200; word-wrap: break-word;}}
      .button{{font-family: {font_family};display: inline-block;
      background-color: black; border: none;
      border-radius: 4px; color: white; padding: 16px 40px;
      text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}}
      p.dotted {{margin: auto;
      width: 75%; font-size: 25px; text-align: center;}}
    <title>Pico W HTTP Server</title>
    <h1>Pico W HTTP Server</h1>
    <p class="dotted">This is a Pico W running an HTTP server with CircuitPython.</p>
    <p class="dotted">The current ambient temperature near the Pico W is
    <span style="color: deeppink;">{temp_test}°{unit}</span></p><br>
    <h1>Control the LED on the Pico W with these buttons:</h1><br>
    <form accept-charset="utf-8" method="POST">
    <button class="button" name="LED ON" value="ON" type="submit">LED ON</button></a></p></form>
    <p><form accept-charset="utf-8" method="POST">
    <button class="button" name="LED OFF" value="OFF" type="submit">LED OFF</button></a></p></form>
    <p><form accept-charset="utf-8" method="POST">
    <button class="button" name="party" value="party" type="submit">PARTY!</button></a></p></form>
    return html

#  route default static IP
def base(request: HTTPRequest):  # pylint: disable=unused-argument
    #  serve the HTML f string
    #  with content type text/html
    with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response:

#  if a button is pressed on the site
@server.route("/", method=HTTPMethod.POST)
def buttonpress(request: HTTPRequest):
    #  get the raw text
    raw_text = request.raw_request.decode("utf8")
    #  if the led on button was pressed
    if "ON" in raw_text:
        #  turn on the onboard LED
        led.value = True
    #  if the led off button was pressed
    if "OFF" in raw_text:
        #  turn the onboard LED off
        led.value = False
    #  reload site
    with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response:

print("starting server..")
# startup the server
    print("Listening on http://%s:80" % esp.pretty_ip(esp.ip_address))
#  if the server fails to begin, restart the pico w
except OSError:

#  text objects for screen
#  connected to SSID text
connect_text_area.text = "Connected to:"
ssid_text = "%s" % secrets('ssid')
# ssid_text_area = label.Label(terminalio.FONT, text=ssid_text, color=0xFFFFFF, x=0, y=offset_y+15)
#  display ip address
ip_text = "IP: %s" % wifi.radio.ipv4_address
# ip_text_area = label.Label(terminalio.FONT, text=ip_text, color=0xFFFFFF, x=0, y=offset_y+30)
#  display temp reading
temp_text = "Temperature: %.02f F" % float(temp_test)
# temp_text_area = label.Label(terminalio.FONT, text=temp_text, color=0xFFFFFF, x=0, y=offset_y+45)

def get_weather_conditions():
    temp = bme280.temperature
    print("\nTemperature: %0.1f C" % temp)
    hum = bme280.relative_humidity
    print("Humidity: %0.1f %%" % hum)
    pres = bme280.pressure
    print("Pressure: %0.1f hPa" % pres)
    alt = bme280.altitude
    print("Altitude: %0.2f meters" % alt)
    light = bh1750.lux
    print("Light: %.2f Lux" % light)
    return [temp, hum, pres, alt, light]

clock = time.monotonic() #  time.monotonic() holder for server ping

while True:
        #  every 30 seconds, ping server & update temp reading
        if (clock + 30) < time.monotonic():
            if wifi.radio.ping(ping_address) is None:
                connect_text_area.text = "Disconnected!"
                ssid_text_area.text = None
                print("lost connection")
                connect_text_area.text = "Connected to:"
                ssid_text_area.text = "%s" % secrets('ssid')
            clock = time.monotonic()
            #  comment/uncomment for desired units
            #  temp_test = str(ds18.temperature)
            temp_test = str(c_to_f(19.45))
            temp_text_area.text = "Temperature: %s F" % temp_test

#         display.show(splash)
        #  poll the server for incoming/outgoing requests
    # pylint: disable=broad-except
    except Exception as e:

1 Answer 1


Official answer from Adafuit for this question is that HTTPServer lib is created for Raspeberry PICO and do not work on other boards like Feather M4 express indeed. Different set of libraries needs to be used instead, here is the relevant example: https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/tree/main/examples/server

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.