大概想法如下:
在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
标签:ESP32CAM,addr,ESP,dest,ip,AndroidStudio,TAG,IP地址,new From: https://www.cnblogs.com/lesterbor/p/18046382