AndroidStudio扫描局域网下的ESP32CAM并获取IP地址

大概想法如下:

 

在ESP32CAM端直接下载示例代码udp_server这个历程,修改默认的WIFI和密码,启动之后会输出如下结果

 由此我们知道了UDP的地址和端口IP地址为192.168.2.3,端口为3333

此时我们使用小工具NetAssist.exe来测试,选择UDP协议之后向ESP32CAM的地址发送广播,如下图所示

 此时我们的ESP32CAM控制台则会输出如下相关信息

 说明设备已经收到了广播的UDP数据

之后我们需要自己在AndroidStudio上编写代码,以广播我们自己需要的UDP数据

首先在页面创建一个按钮StartStream,之后写入按钮的回调函数,执行以下代码即可做到广播UDP数据

至于为啥是192.168.2.255,是因为需要广播而不是单播,如果是单播,你需要指定IP地址,如果你需要扫描192.168.2.xxx下的所有设备,你写成192.168.2.255就可以广播了

//预定义变量
private static final String BROADCAST_IP = "192.168.2.255"; // 广播地址
private static final int BROADCAST_PORT = 3333; // 你的广播端口

//写在onCreate中
StartStream.setOnClickListener(v -> {
    sendBroadcastMessage("NBQS_IPCAM");
});

//定义一个发送函数
public void sendBroadcastMessage(String message) {
    new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    DatagramSocket socket = new DatagramSocket();
                    InetAddress inetAddress = InetAddress.getByName(BROADCAST_IP);

                    byte[] buffer = message.getBytes();
                    DatagramPacket packet = new DatagramPacket(buffer, buffer.length, inetAddress, BROADCAST_PORT);

                    socket.send(packet);
                    socket.setBroadcast(true);
                    socket.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

  这样就可以在该局域网内部广播UDP数据,当ESP32CAM收到特定的数据之后则会返回设备名称,ESPCAM端UDP的回复部分代码如下

static void udp_server_task(void *pvParameters)
{
    char rx_buffer[128];
    char addr_str[128];
    int addr_family = (int)pvParameters;
    int ip_protocol = 0;
    struct sockaddr_in6 dest_addr;
    static struct sockaddr_in udp_client_addr;

    ESP_ERROR_CHECK(esp_netif_init());

    while (1) {

        if (addr_family == AF_INET) {
            struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
            dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
            dest_addr_ip4->sin_family = AF_INET;
            dest_addr_ip4->sin_port = htons(PORT);
            ip_protocol = IPPROTO_IP;
        } else if (addr_family == AF_INET6) {
            bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
            dest_addr.sin6_family = AF_INET6;
            dest_addr.sin6_port = htons(PORT);
            ip_protocol = IPPROTO_IPV6;
        }

        int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Socket created");

        int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
        if (err < 0) {
            ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        }
        ESP_LOGI(TAG, "Socket bound, port %d", PORT);

        struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
        socklen_t socklen = sizeof(source_addr);

        while (1) {
            ESP_LOGI(TAG, "Waiting for data");
            int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);
            // Error occurred during receiving
            if (len < 0) {
                ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
                break;
            }
            // Data received
            else {
                // Get the sender's ip address as string
                if (source_addr.ss_family == PF_INET) {
                    inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
                } else if (source_addr.ss_family == PF_INET6) {
                    inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);
                }

                 // 设置目标地址和端口  
                memset(&udp_client_addr, 0, sizeof(udp_client_addr));  
                udp_client_addr.sin_family = AF_INET;  
                udp_client_addr.sin_port = htons(3333); // 替换为你想要发送到的端口  
                udp_client_addr.sin_addr.s_addr = inet_addr(addr_str); // 替换为你想要发送到的 IP 地址

                rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string...
                ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
                ESP_LOGI(TAG, "%s", rx_buffer);

                if(!strncmp(rx_buffer,"NBQS_IPCAM",10)){
                    ESP_LOGE(TAG, "From IPCAM UC");

                    char DeviceInfo[20] = "NBQS_IPCAM_00000001";

                    int err = sendto(sock, DeviceInfo, 20, 0, (struct sockaddr *)&udp_client_addr, sizeof(udp_client_addr));
                    if (err < 0) {
                        ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                        break;
                    }
                }else{
                    ESP_LOGE(TAG, "Dont send");
                }  
            }
        }

        if (sock != -1) {
            ESP_LOGE(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            close(sock);
        }
    }
    vTaskDelete(NULL);
}

之后在Android Studio中监听UDP返回的数据,在处理返回的数据的时候ESPCAM的IP地址也会同时被获取到其处理函数如下:

public void onBroadcastReceive() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d("UDP_Server", "Begin");
                while (true) {
                    try {
                        // 创建接收数据报套接字并将其绑定到本地主机上的指定端口
                        DatagramSocket datagramSocket = new DatagramSocket(BROADCAST_PORT);
                        byte[] buf = new byte[1024];
                        final DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length);
                        datagramSocket.receive(datagramPacket);

                        InetAddress ip = datagramPacket.getAddress();
                        String ipStr = ip + "";

                        String ip_new = ipStr.substring(1, ipStr.length());

                        //过滤掉自身IP
                        if(!(!localIp.equals("") && localIp.equals(ip_new))) {
                            // 获取内容
                            String Content = new String(datagramPacket.getData(),0, datagramPacket.getLength());
                            Log.d("UDP_Server", "From:" + ip_new + "  Content:" + Content);

                            if(Content.substring(0, 10).equals("NBQS_IPCAM")){
                                Log.d("UDP_Server", "符合");
                                //判断原有数组中是否有新的数组
                                if (!MainActivity.this.IpCameraIp.contains(ip_new)) {
                                    MainActivity.this.IpCameraName.add(Content);//将搜索到的蓝牙名称和地址添加到列表。
                                    MainActivity.this.IpCameraIp.add(ip_new);//将搜索到的蓝牙地址添加到列表。

                                    //构建数据格式更新到ListView
                                    Map<String, Object> map = new HashMap<>();
                                    map.put("ico", R.drawable.icon_video);
                                    map.put("name", Content);
                                    map.put("ssid", ip_new);
                                    MainActivity.this.mIpCameraListData.add(map);

                                    runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            MainActivity.this.mIpCameraSimpleAdapter.notifyDataSetChanged();
                                        }
                                    });
                                }
                            }
                        }
                        datagramSocket.close();

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

在上述代码中,我使用了Android Studio中的ListView来展示数据,相关的展示请自行查询数据更新代码,mIpCameraListData中存放的就是设备名称和IP地址

MainActivity.this.mIpCameraSimpleAdapter.notifyDataSetChanged();就是ListView的数据集,如果你不需要,删除即可。

执行sendBroadcastMessage之后,如果发送的数据是NBQS_IPCAM,则Android Studio会收到来自于ESP32CAM发送回的数据,并且保存到mIpCameraListData

posted @ 2024-03-05 19:53  千樊  阅读(561)  评论(0)    收藏  举报