[risolto] Dubbio su ricezione pacchetti UDP

Sto facendo le prime prove con lo shield ethernet W5100 su ArduinoUNO.

Ho scritto un programma Python per inviare pacchetti UDP di prova, che crea un’interfaccia grafica con tre pulsanti: ‘accendi’ ‘spegni’ ‘spam’:

# Python 3.x
import socket
import random
import tkinter as tk

ARDU_ADDR = '192.168.1.30', 9000

#---------------------------------------------------------------------

def create_gui():
    gui = tk.Tk()
    gui.title('Ardu-UDP test')
    gui.resizable(False, False)
    button_on = tk.Button(gui, text='ACCENDI', width=12)
    button_on.grid(row=0, column=0)
    button_off = tk.Button(gui, text='SPEGNI', width=12)
    button_off.grid(row=0, column=1)
    button_spam = tk.Button(gui, text='SPAM', width=12)
    button_spam.grid(row=0, column=3)
    return gui, button_on, button_off, button_spam

#---------------------------------------------------------------------

def send_packet(n):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    if n == 2:
        for h in range(random.randint(1, 101)):
            len_data = random.randint(2, 1001)
            data = [random.randrange(256) for n in range(len_data)]
            sock.sendto(bytes(data), ARDU_ADDR)
    else:
        sock.sendto(bytes([n]), ARDU_ADDR)
    sock.close()

#---------------------------------------------------------------------

def main():
    gui, button_on, button_off, button_spam = create_gui()
    button_on.bind('<Button-1>', lambda evt, n=1: send_packet(n))
    button_off.bind('<Button-1>', lambda evt, n=0: send_packet(n))
    button_spam.bind('<Button-1>', lambda evt, n=2: send_packet(n))
    gui.mainloop()

#---------------------------------------------------------------------

main()

I pulsanti ‘accendi’ e ‘spegni’ inviano un pacchetto con un solo byte (rispettivamente di valore 1 e 0), mentre il pulsante ‘spam’ invia casualmente a raffica da 1 a 100 pacchetti, lunghi da 2 a 1000 byte, composti da byte casuali 0…255

Il programma ricevente su Arduino è il seguente:

#include <Ethernet.h>
#include <EthernetUdp.h>

//------------------------------------------------------------------

byte MAC[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress IP(192, 168, 1, 30);
char packetBuffer[UDP_TX_PACKET_MAX_SIZE];  // 24 bytes
EthernetUDP Udp;

//------------------------------------------------------------------

void setup() 
{
    pinMode(2, OUTPUT);   // LED debug
    digitalWrite(2, LOW); // spento
    
    Ethernet.init(10);
    Ethernet.begin(MAC, IP);
    Udp.begin(9000);
}

//------------------------------------------------------------------

void loop() 
{
    int packetSize = Udp.parsePacket();
    
    if (1 == packetSize) 
    {
        Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
        if      (1 == packetBuffer[0]) { digitalWrite(2, HIGH); }
        else if (0 == packetBuffer[0]) { digitalWrite(2, LOW);  }
        else                           {                        }
    }
    else if (packetSize > 1) // scartare intero pacchetto
    {
        while (Udp.available()) { Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE); }
    }
    else
    {
    }
    
    delay(10);
}

Lo scopo della prova era di verificare se la funzione ‘Udp.parsePacket’ riusciva a distinguere chiaramente tutti i pacchetti l’uno dall’altro e le loro esatte dimensioni.

I pulsanti ‘accendi’ e ‘spegni’ funzionano perfettamente, il LED collegato al pin 2 si comporta come previsto.

Cliccando su ‘spam’ invece di tanto in tanto il LED commuta, segno che qualche pacchetto o viene frammentato e diventa lungo un solo byte (ma strano su rete interna con solo uno switch di mezzo), o il buffer di ricezione del W5100 viene letto in un modo che non corrisponde a quanto penso, ovvero ‘Udp.parsePacket’ e/o ‘Udp.available’ e/o ‘Udp.read’ non si comportano come credevo.
Anche cambiando la strategia per scartare un pacchetto le cose non variano:

else if (packetSize > 1) // scartare intero pacchetto
{
    for (uint16_t n=0; n<packetSize; n++) { Udp.read(); }
}

In sostanza pensavo che controllando il valore riportato da parsePacket potessi distinguere esattamente i pacchetti lunghi 1 da tutti gli altri, ma non è così.
In realtà potrebbe non essere un problema, perché nell’uso reale non invierei singoli byte ma messaggi completi, e non ci sarebbero dati spam serrati in circolo, però mi piacerebbe capire.

Come non detto… solito errore di if con = al posto di == :roll_eyes: Ho corretto, ma lascio i codici se a qualcuno servono come spunto.