Skip to content

CPY core module socketpool request add attributes to be able to receive udp multicast packets #7556

@PaulskPt

Description

@PaulskPt

Some years ago I wrote and used successfully a Python script for a Raspberry Pi to receive, unpack and display UDP multicast packets, broadcasted from an X-Plane flight simulator app running on a MS Windows PC.
In this moment I am trying to port this script to CircuitPython V8.0.0.-rc.2 on an Adafruit Feather ESP32-S2 with TFT,
however I am not able to proceed because, TMHO, the core module 'socketpool' lacks attributes I need for my project.

Ref: readthedocs_latest_socketpool
socketpool has (among some others) the following attributes:
'AF_INET', 'AF_INET6', 'EAI_NONAME', 'IPPROTO_TCP', 'SOCK_DGRAM', 'SOCK_RAW', 'SOCK_STREAM', 'TCP_NODELAY', 'gaierror', 'getaddrinfo', 'socket'

My 'wishlist' of attributes to add:
IPPROTO_UDP
SO_REUSEADDR
SOL_SOCKET
IPPROTO_IP
IP_ADD_MEMBERSHIP
INADDR_ANY
inet_aton()

Below an image of BECN packets captured using WireShark:
multicast_udp_packets_captured

The following is a test script to receive, unpack and print to REPL received multicast udp packets.
It fails because of socketpool.SocketPool is lacking the attributes listed above in my 'wishlist':

# code.py -- receive UDP multicast Beacon & Data packets sent from a desktop PC (MS Windows 11 Pro) 
# running X-Plane 12 flight simulator which broadcast UDP multicast BECN & Data packets.
# This is a test script for CircuitPython V8.0.0-rc.2 to receive, unpack and display these packets.
# Platform: Adafruit Feather ESP32-S2 with TFT
# 2023-02-08 @PaulskPt Github
#type:ignore

import time, wifi, socketpool, os, struct, binascii
my_debug = True

ip = wifi.radio.ipv4_address

if ip:
    s_ip = str(ip)
    le_s_ip = len(s_ip)

if s_ip is not None and len(s_ip) > 0 and s_ip != '0.0.0.0':
    print("\nWiFi already connected")
else:
    wifi.radio.connect(ssid=os.getenv("CIRCUITPY_WIFI_SSID"), password=os.getenv("CIRCUITPY_WIFI_PASSWORD"))

print("IP addr:", wifi.radio.ipv4_address)

try:
    pool = socketpool.SocketPool(wifi.radio)
    #udp_host = str(wifi.radio.ipv4_address) # LAN IP as a string
    MCAST_GRP = "239.255.1.1"
    MCAST_PORT = 49707  # a number of your choosing, should be 1024-65000
    udp_buffer = bytearray(61)  # stores our incoming packet

    sock = pool.socket(pool.AF_INET, pool.SOCK_DGRAM, pool.IPPROTO_UDP) # UDP socket
    sock.setsockopt(pool.SO_REUSEADDR, 1)
    sock.settimeout(5)
    sock.bind((MCAST_GRP, MCAST_PORT))
    mreq = struct.pack("4sl", sock.inet_aton(MCAST_GRP), pool.INADDR_ANY)
    sock.setsockopt(pool.IPPROTO_IP, pool.IP_ADD_MEMBERSHIP, mreq)

    print(f"waiting for UDP packets from remote host to multicast group: \'{MCAST_GRP}\', port: {MCAST_PORT}")
except Exception as e:
    raise 

BeaconData = {}
data = {}

while not BeaconData:
    try:
        size, sender = sock.recvfrom_into(udp_buffer)
        print(f"Received udp packet from {sender[0]}:", udp_buffer)
        header = udp_buffer[0:4]
        if header == b'BECN':
            print("Received a BECN packet")
        time.sleep(2)
        # * Data
        data = udp_buffer[5:21]
        # struct becn_struct
        # {
        # 	uchar beacon_major_version;		// 1 at the time of X-Plane 10.40
        # 	uchar beacon_minor_version;		// 1 at the time of X-Plane 10.40
        # 	xint application_host_id;		// 1 for X-Plane, 2 for PlaneMaker
        # 	xint version_number;			// 104014 for X-Plane 10.40b14 - 113201 for X-Plane 11.32
        # 	uint role;				        // 1 for master, 2 for extern visual, 3 for iOS
        # 	ushort port;				    // port number X-Plane is listening on
        # 	xchr	computer_name[strDIM];  // the hostname of the computer
        # };
        beacon_major_version = 0
        beacon_minor_version = 0
        application_host_id = 0
        xplane_version_number = 0
        role = 0
        port = 0
        (
          beacon_major_version,  # 1 at the time of X-Plane 10.40
          beacon_minor_version,  # 1 at the time of X-Plane 10.40, 2 at the time of X-Plane 11
          application_host_id,   # 1 for X-Plane, 2 for PlaneMaker
          xplane_version_number, # 104014 for X-Plane 10.40b14 - 113201 for X-Plane 11.32
          role,                  # 1 for master, 2 for extern visual, 3 for IOS
          port,                  # port number X-Plane is listening on
        ) = struct.unpack("<BBiiIH", data)

        if my_debug:
            print('beacon_major_version = ', beacon_major_version)
            print('beacon_minor_version = ', beacon_minor_version)
            print('application_host_id = ', application_host_id)

        # Originally beacon_minor_version was checked for a value of 1 but investigation by Paulsk revealed that X-Plane 11 returns a value of  2
        computer_name = udp_buffer[21:-1]
        if beacon_major_version == 1 \
           and beacon_minor_version == 2 \
           and application_host_id == 1:
            BeaconData["IP"] = sender[0]
            BeaconData["Port"] = port
            BeaconData["hostname"] = computer_name.decode()
            BeaconData["XPlaneVersion"] = xplane_version_number
            BeaconData["role"] = role

            if my_debug:
                print('\nBeacon UDP packet received:')
                print('Host IP         =', BeaconData["IP"])
                print('Port            =', BeaconData["Port"])
                print('Hostname        =', BeaconData["hostname"])
                print('X-Plane version =', BeaconData["XPlaneVersion"])
                print('Role            =', BeaconData["role"])
        else:
            print('Unknown packet from '+sender[0])
            print(str(len(udp_buffer)) + ' bytes')
            print(udp_buffer)
            print(binascii.hexlify(udp_buffer))

    except OSError as e:
        if e.errno == 116:  # ETIMEDOUT
            raise

print("Done!")

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions