最近在做一个功能,需要用到UDP组播的通讯方式,记录一下做UDP组播的过程,中间遇到了很多坑,GPT和DeepSECK已经问的崩溃了。

多的不少 少的不唠,先上程序

启用UDP和组播功能

lwipopts.h文件中启用组播支持:

#define LWIP_IGMP                   1
#define LWIP_UDP                    1
#define LWIP_SOCKET                 1
 
初始化UDP组播

在代码中初始化UDP并加入组播组:

#define MULTICAST_PORT 5000

static struct udp_pcb *multicast_pcb = NULL;
static ip_addr_t mutlcast_send_ip;
static ip_addr_t mutlcast_recv_ip;

static void udp_recv_multicast(void *arg, struct udp_pcb *pcb, struct pbuf *p,const ip_addr_t *addr, u16_t port);

static void udp_multicast_init(void)
{
    err_t err;

    if (multicast_pcb != NULL) return;

    multicast_pcb = udp_new();
    if (multicast_pcb == NULL)
    {
        LOGD("udp_pcb fail\r\n");
        return;
    }

    IP4_ADDR(&mutlcast_send_ip, 224, 0, 1, 0);  /* 设置发送多播地址 */

    IP4_ADDR(&mutlcast_recv_ip, 224, 0, 1, 0);  /* 设置加入的多播接收地址 */

     /* 加入多播组 */
    err = igmp_joingroup(IP_ADDR_ANY, &mutlcast_recv_ip);
    if (err != ERR_OK)
    {
        LOGD("add multicast group fail\r\n");
        goto free_udp_pcb;
    }

    err = udp_bind(multicast_pcb, IP_ADDR_ANY, MULTICAST_PORT);   /* 绑定端口 */
    if (err != ERR_OK)
    {
        LOGD("bind port fail\r\n");
        goto free_udp_pcb;
    }

    udp_recv(multicast_pcb, udp_recv_multicast, NULL);
    
    LOGD("multicast init ok\r\n");
    return;
free_udp_pcb:
    if (multicast_pcb != NULL) {
        udp_remove(multicast_pcb);  // 释放 UDP PCB
        multicast_pcb = NULL;       // 重要!指针置空,避免悬垂指针
    }
}

 
实现组播数据接收

定义UDP接收回调函数

#define UDP_RECV_BUF_SIZE   256      // 最大接收长度
#define UDP_RECV_QUEUE_LEN  8        // 队列深度(最大缓存8条)

typedef struct {
    uint16_t len;
    char data[UDP_RECV_BUF_SIZE];
    ip_addr_t src_ip;
    uint16_t src_port;
} udp_recv_msg_t;

static QueueHandle_t udp_recv_queue = NULL;
static struct udp_pcb *multicast_pcb = NULL;

void udp_recv_multicast(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
    if (p == NULL) return;

    struct pbuf *q;
    int recv_count = 0;
    udp_recv_msg_t msg;
    memset(&msg, 0, sizeof(msg));  // 清空结构体

    // 拷贝数据
    for (q = p; q != NULL && recv_count < UDP_RECV_BUF_SIZE; q = q->next)
    {
        uint16_t copy_len = q->len;
        if ((recv_count + copy_len) > UDP_RECV_BUF_SIZE) {
            copy_len = UDP_RECV_BUF_SIZE - recv_count;
            LOGD("multicast recv overflow, truncated\r\n");
        }

        memcpy(&msg.data[recv_count], q->payload, copy_len);
        recv_count += copy_len;
    }

    msg.len = recv_count;
    msg.src_ip = *addr;
    msg.src_port = port;

    // 入队
    if (udp_recv_queue != NULL)
    {
        if (xQueueSendFromISR(udp_recv_queue, &msg, NULL) != pdTRUE)
        {
            LOGD("udp_recv_queue send fail\r\n");
        }
    }

    pbuf_free(p);  // 释放 pbuf
}
发送组播数据

通过UDP PCB发送数据到组播地址:

int udp_multicast_send(const void *psrc, uint32_t src_len, uint16_t port)
{
    struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, src_len, PBUF_POOL);
    if (p == NULL)
    {
        LOGD("pbuf alloc fail\r\n");
        return -1;
    }
    pbuf_take(p, psrc, src_len);
    udp_sendto_if(multicast_pcb, p, &mutlcast_send_ip, port, netif_default);
    pbuf_free(p);  /* 释放pbuf */
    return 0;
}

创建任务处理接收的数据;

void udp_process_task(void *arg)
{
    udp_recv_msg_t msg;
    while (1)
    {
        if (xQueueReceive(udp_recv_queue, &msg, portMAX_DELAY) == pdTRUE)
        {
            uint32_t ip = msg.src_ip.addr;
            LOGD("from ip %d.%d.%d.%d:%d data, len:%d\r\n",
                ip & 0xFF, (ip >> 8) & 0xFF,
                (ip >> 16) & 0xFF, (ip >> 24) & 0xFF,
                msg.src_port, msg.len);

            parse_multicast_data(msg.data, msg.len);
        }
    }
}

经过上面的程序基本就可以实现UDP的组播了

注意如果你的设备能够就将组播包发出去但收不到,大概率是在ETH驱动将组播过滤了,设置为

全部接收

ETH_InitStruct->ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_None;
 

还要注意的是在以太网初始化时要加入组播标志

netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP;

并强制接收所有组播包;

ETH->MACFFR |= ETH_MACFFR_PAM;  // 强制接收所有组播包
 

就可以了

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐