adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.14k stars 1.22k forks source link

SSL not working when importing Adafruit-azureiot on a rpPico W #9174

Closed PanzerPandaNinja closed 6 months ago

PanzerPandaNinja commented 7 months ago

CircuitPython version

Adafruit CircuitPython 9.0.3 on 2024-04-04; Raspberry Pi Pico W with rp2040
Board ID:raspberry_pi_pico_w

Code/REPL

import os
import time
import adafruit_ntp
import rtc
import ipaddress
import ssl
import wifi
import socketpool
import adafruit_requests
import board
import busio
import terminalio
import displayio
from adafruit_display_text import label
from adafruit_st7735r import ST7735R
from adafruit_azureiot import IoTCentralDevice

#  ntp clock - update tz_offset to your timezone
pool = socketpool.SocketPool(wifi.radio)
ntp = adafruit_ntp.NTP(pool, tz_offset=-4)
rtc.RTC().datetime = ntp.datetime

if time.localtime().tm_year < 2023:
    print("Setting System Time in UTC")
    rtc.RTC().datetime = ntp.datetime

else:
    print("Year seems good, skipping set time.")

cal = ntp.datetime
year = cal[0]
mon = cal[1]
day = cal[2]
hour = cal[3]
minute = cal[4]

# Support both 8.x.x and 9.x.x. Change when 8.x.x is discontinued as a stable release.
try:
    from fourwire import FourWire
except ImportError:
    from displayio import FourWire

# *** The following code is for the WiFi radio ***
print(f"My MAC address: {[hex(i) for i in wifi.radio.mac_address]}")

print("Available WiFi networks:")
for network in wifi.radio.start_scanning_networks():
    print("\t%s\t\tRSSI: %d\tChannel: %d" % (str(network.ssid, "utf-8"),
                                             network.rssi, network.channel))
wifi.radio.stop_scanning_networks()

print(f"Connecting to {os.getenv('CIRCUITPY_WIFI_SSID')}")
wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD"))
print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}")
priv_ip = wifi.radio.ipv4_address
print(f"My IP address: {str(priv_ip)}")

print("-" * 40)

# *** The following code is for pinging ***
ping_ip = ipaddress.IPv4Address("8.8.8.8")
ping = wifi.radio.ping(ip=ping_ip)

if ping is None:
    print("Couldn't ping 'google.com' successfully")
else:
    # convert s to ms
    print(f"Pinging 'google.com' took: {ping * 1000} ms")

print("-" * 40)

# *** The following code is for fetching text from a URL ***
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())
TEXT_URL = "https://minip.no/"
print(f"Fetching text from {TEXT_URL}")
response = requests.get(TEXT_URL)
print("-" * 40)

# split the response text into lines and print the second line, it should be the external IP address
lines = response.text.split('\n')
if len(lines) > 1:
    #print(lines[1])
    parts = lines[1].split(" ")
    ext_ip_address = parts[5]  # The IP address is the 6th part when splitting by space
    print("External IP: " + ext_ip_address)
else:
    print("Response text does not have a second line.")

print("-" * 40)

# *** The following code is for the ST7735R display ***

# Release any resources currently in use for the displays
displayio.release_displays()

spi = busio.SPI(clock=board.GP10, MOSI=board.GP11, MISO=None)
tft_cs = board.GP9
tft_dc = board.GP8

display_bus = FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=board.GP12)

display = ST7735R(display_bus, width=128, height=130, colstart=2, rowstart=1, bgr=False)

# Draw Yggdrasil
bitmap = displayio.OnDiskBitmap("/yggdrasil.bmp")
tile_grid = displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader)
group = displayio.Group()
group.append(tile_grid)

# Draw a label for the private IP
text = str(priv_ip)
text_area = label.Label(terminalio.FONT, text=text, color=0xFFFFFF, x=35, y=80)
group.append(text_area)

# Draw a label for the external IP
text = ext_ip_address
text_area = label.Label(terminalio.FONT, text=text, color=0xFFFFFF, x=35, y=100)
group.append(text_area)

# Draw a text label
text = "Yggdrasil"
text_area = label.Label(terminalio.FONT, text=text, color=0xFFFFFF, x=40, y=120)
group.append(text_area)

display.root_group = group

# *** The following code is for the Azure IoT Central device ***
esp = None
pool = socketpool.SocketPool(wifi.radio)
device = IoTCentralDevice(
    pool, esp, os.getenv("id_scope"), os.getenv("device_id"), os.getenv("device_primary_key")
)

print("Connecting to Azure IoT Central...")
device.connect()

print("Connected to Azure IoT Central!")

while True:
    pass

Behavior

Fetching text from https://minip.no/
x509-crt-bundle:PK verify failed with error FFFFBD70
x509-crt-bundle:Failed to verify certificate
Traceback (most recent call last):
  File "adafruit_connection_manager.py", line 271, in get_socket
OSError: (-12288, 'MBEDTLS_ERR_X509_FATAL_ERROR')

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "code.py", line 80, in <module>
  File "adafruit_requests.py", line 591, in get
  File "adafruit_requests.py", line 525, in request
  File "adafruit_connection_manager.py", line 282, in get_socket
RuntimeError: Error connecting socket: (-12288, 'MBEDTLS_ERR_X509_FATAL_ERROR')

Description

SSL not working when importing Adafruit-azureiot on a rpPico W, I get this error when I try to reach a HTTPS-site. When I comment out "from adafruit_azureiot import IoTCentralDevice" and the code running it, it is working. It is also working if I get HTTP-site.

Not a big problem for me since I can use HTTP, but might be a problem someone wants to look at, when I googled it I found very little.

Additional information

No response

santiest commented 7 months ago

I'm facing the same issue. After searching for long, it does look like this comes from a memory problem. I am not completely certain, but it looks like I need about 60k bytes of RAM free to be able to be able to make an HTTPS request. I've read it would be enough if you had 40k-ish, but that has not been my experience (maybe you need 40k of continuos, non-fragmented space?)

You can try this to check if this is your problem (put this code right before the HTTPS request):

import gc
print("Available memory:", gc.mem_free())

In your particular case, it looks like you are loading a bitmap into memory. There are ways to just read them from disk, so it is an easy fix, hopefully. You can try commenting it out to see if you have enough memory then.

bablokb commented 7 months ago

Remove the code that is not necessary, i.e. scanning networks and pinging google. I would also recommend to remove setting the time from ntp. Also, skip the call to minip.no. If you absolutely need that, query the http version instead of the https version of that service.

PanzerPandaNinja commented 7 months ago

Hi, I kind of gave up since this was for a workshop and I could do the workshop without it. This ticket was more meant as a heads up to developers or whoever cares enough to fix it :) I am pretty sure I checked the gc.mem_free and I think there was enough. I'm still interested in getting evrything to work, but I will have to revisit this later. I thought maybe it was a collision with the libraries and not a memory problem. But I will test this later at some point. :) Thank you for the replies.

anecdata commented 7 months ago

For whatever reasons, less free memory is showing at startup in CP 9 compared to CP 8:

Adafruit CircuitPython 8.2.10 on 2024-02-14; Raspberry Pi Pico W with rp2040
>>> import gc
>>> gc.mem_free()
142640
Adafruit CircuitPython 9.0.4 on 2024-04-16; Raspberry Pi Pico W with rp2040
>>> import gc
>>> gc.mem_free()
126160
dhalbert commented 7 months ago

This decrease in free memory was explored in https://github.com/adafruit/circuitpython/issues/9085. We turned off some new modules in 9.1.0-beta.1. Could you try that version and see if things improve?

anecdata commented 7 months ago

Thanks for the reminder about #9085

9.1.0-beta.1 is the same as 9.0.4 on Pico W:

Adafruit CircuitPython 9.1.0-beta.1-10-gc92bb9b3cc on 2024-04-25; Raspberry Pi Pico W with rp2040
>>> import gc
>>> gc.mem_free()
126160