tcp服务端与ESP32Cam通信ESP32Cam通过串口控制ESP32

  1. ESP32
    #include 
    /*
    esp32cam_to_esp32:.
    
    esp32cam_to_esp32:.
    
    esp32cam_to_esp32:.
    
    esp32cam_to_esp32:.
    
    esp32cam_to_esp32:.
    
    esp32cam_to_esp32:.
    
    esp32cam_to_esp32:.
    
    esp32cam_to_esp32:.
    
    esp32cam_to_esp32:.
    
    esp32cam_to_esp32:.
    
    esp32cam_to_esp32:Connected
    
    esp32cam_to_esp32:Connected
    
    esp32cam_to_esp32:IP Address:
    
    esp32cam_to_esp32:IP Address:
    
    esp32cam_to_esp32:67152064
    
    esp32cam_to_esp32:67152064
    
    esp32cam_to_esp32:Camera Init OK!
    
    esp32cam_to_esp32:Camera Init OK!
    
    esp32cam_to_esp32:Connect To Tcp Server Failed!After 10 Seconds Try Again!
    
    esp32cam_to_esp32:Connect To Tcp Server Failed!After 10 Seconds Try Again!
    
    esp32cam_to_esp32:Connect To Tcp Server Failed!After 10 Seconds Try Again!
    
    esp32cam_to_esp32:Connect To Tcp Server Failed!After 10 Seconds Try Again!
    */
    String recv_data = ""; //接受串口数据的变量
    String esp32_head = "esp32_head:";  //esp32与服务器的通信标识头
    //esp32cam 串口向 esp32发送消息的标识头 主要是联网信息 摄像头初始化信息等 
    // 这些信息 esp32显示在串口或小屏幕上
    String esp32cam_to_esp32 = "esp32cam_to_esp32:";
    const int LED = 2;
    void setup() {
      Serial.begin(115200);
      Serial2.begin(115200);
      pinMode(LED,OUTPUT);
      digitalWrite(LED, LOW);
    }
    
    void loop() {
      if(Serial2.available()){
        recv_data = Serial2.readStringUntil('\n');
        Serial.println(recv_data);
        if(recv_data.substring(0,esp32_head.length()) == esp32_head){
          //消息来自服务器端 处理相关指令 必须这样判断 防止存在 \r\n等看不见的字符
          if(recv_data.substring(0,(esp32_head + "openLed").length()) == (esp32_head + "openLed") ){
            digitalWrite(LED, HIGH);
            Serial2.println( esp32_head + "Led ON!"); //串口发送给ESPCAM ESPCAM在发送给服务器
          }
          if(recv_data.substring(0,(esp32_head + "closeLed").length()) == (esp32_head + "closeLed")  ){
            digitalWrite(LED, LOW);
            Serial2.println( esp32_head + "Led OFF!");
          }
        }else if(recv_data.substring(0,esp32cam_to_esp32.length()) == esp32cam_to_esp32){
          //消息来自esp32cam的数据一般为一些提示信息 可以用在串口或屏幕起到提示作用
          Serial.println(recv_data);
        }else{
          Serial.println("Error Msg!");
        }
      }
    }
    

  2. ESP32CAM
    #include 
    #include 
    #include "esp_camera.h"
    #include 
    
    const char *ssid = "dsx_zj";
    const char *password = "dsxbs725";
    const IPAddress serverIP(192,168,0,2); //欲访问的地址
    uint16_t serverPort = 8080;         //服务器端口号
    String esp32_head = "esp32_head:";  //esp32与服务器的通信标识头
    String esp32_cam_head = "esp32_cam_head:"; //esp32cam与服务器的通信标识头
    //esp32cam 串口向 esp32发送消息的标识 发送的消息主要是联网信息 摄像头初始化信息等 
    // 这些信息 esp32用来显示在串口或小屏幕上 起到提醒作用
    String esp32cam_to_esp32 = "esp32cam_to_esp32:"; 
    bool cam_state = true; //打开或关闭摄像头标识
    const int LED = 4;//闪光灯
    const int ZHESHI_LED = 33; //指示灯 亮表示连接上服务器  灭表示没有连上服务器
    String esp32_data = "";//保存esp32串口发过来的数据 然后espcam转发给服务器
    
    #define maxcache 1430  //图像数据包的大小
    
    WiFiClient client; //声明一个客户端对象,用于与服务器进行连接
    
    //CAMERA_MODEL_AI_THINKER类型摄像头的引脚定义
    #define PWDN_GPIO_NUM     32
    #define RESET_GPIO_NUM    -1
    #define XCLK_GPIO_NUM      0
    #define SIOD_GPIO_NUM     26
    #define SIOC_GPIO_NUM     27
     
    #define Y9_GPIO_NUM       35
    #define Y8_GPIO_NUM       34
    #define Y7_GPIO_NUM       39
    #define Y6_GPIO_NUM       36
    #define Y5_GPIO_NUM       21
    #define Y4_GPIO_NUM       19
    #define Y3_GPIO_NUM       18
    #define Y2_GPIO_NUM        5
    #define VSYNC_GPIO_NUM    25
    #define HREF_GPIO_NUM     23
    #define PCLK_GPIO_NUM     22
    
    static camera_config_t camera_config = {
        .pin_pwdn = PWDN_GPIO_NUM,
        .pin_reset = RESET_GPIO_NUM,
        .pin_xclk = XCLK_GPIO_NUM,
        .pin_sscb_sda = SIOD_GPIO_NUM,
        .pin_sscb_scl = SIOC_GPIO_NUM,
        
        .pin_d7 = Y9_GPIO_NUM,
        .pin_d6 = Y8_GPIO_NUM,
        .pin_d5 = Y7_GPIO_NUM,
        .pin_d4 = Y6_GPIO_NUM,
        .pin_d3 = Y5_GPIO_NUM,
        .pin_d2 = Y4_GPIO_NUM,
        .pin_d1 = Y3_GPIO_NUM,
        .pin_d0 = Y2_GPIO_NUM,
        .pin_vsync = VSYNC_GPIO_NUM,
        .pin_href = HREF_GPIO_NUM,
        .pin_pclk = PCLK_GPIO_NUM,
        
        .xclk_freq_hz = 20000000,
        .ledc_timer = LEDC_TIMER_0,
        .ledc_channel = LEDC_CHANNEL_0,
        
        .pixel_format = PIXFORMAT_JPEG,
        .frame_size = FRAMESIZE_VGA,
        .jpeg_quality = 12,
        .fb_count = 1,
    };
    //初始化摄像头
    esp_err_t camera_init() {
        //initialize the camera
        esp_err_t err = esp_camera_init(&camera_config);
        if (err != ESP_OK) {
            Serial.println( esp32cam_to_esp32 + "Camera Init Failed");
            return err;
        }
        sensor_t * s = esp_camera_sensor_get();
        //initial sensors are flipped vertically and colors are a bit saturated
        if (s->id.PID == OV2640_PID) {
        //        s->set_vflip(s, 1);//flip it back
        //        s->set_brightness(s, 1);//up the blightness just a bit
        //        s->set_contrast(s, 1);
        }
        Serial.println(esp32cam_to_esp32 + "Camera Init OK!");
        return ESP_OK;
    }
    //初始化wifi
    void wifi_init()
    {
        WiFi.mode(WIFI_STA);
        WiFi.setSleep(false); //关闭STA模式下wifi休眠,提高响应速度
        WiFi.begin(ssid, password);
        while (WiFi.status() != WL_CONNECTED)
        {
            delay(500);
            Serial.println(esp32cam_to_esp32 + ".");
        }
        Serial.println(esp32cam_to_esp32 + "Connected");
        Serial.println(esp32cam_to_esp32 + "IP Address:");
        Serial.println(esp32cam_to_esp32 + WiFi.localIP());
    }
    
    void setup()
    {
      Serial.begin(115200);
      pinMode(ZHESHI_LED, OUTPUT);
      digitalWrite(ZHESHI_LED, HIGH);
      pinMode(LED, OUTPUT);
      digitalWrite(LED, LOW);
      wifi_init();
      camera_init();
    }
    
    void loop()
    {
      if (client.connect(serverIP, serverPort)) //尝试访问目标地址
      {
         client.println(esp32_cam_head + "Hello Server,I Am ESP32CAM!");
         digitalWrite(ZHESHI_LED, LOW);
         while (client.connected() || client.available()) //如果已连接或有收到的未读取的数据
         {
            // 读取串口esp32数据 转发给服务器
            if(Serial.available())
            {
              esp32_data = Serial.readStringUntil('\n');
              client.println(esp32_data);
            }
            if (client.available()) //如果tcp网络有数据可读取 读取网络数据
            {
              String line = client.readStringUntil('\n'); //读取数据到换行符
              // 如果数据是服务器发送给esp32的 则通过串口发给esp32
              if (line.substring(0,esp32_head.length()) == esp32_head)
              {
                  Serial.println(line);
              }
              // 如果数据是服务器发送给esp32cam的 则 根据指令处理相关逻辑
              if (line.substring(0,esp32_cam_head.length()) == esp32_cam_head)
              {
                // 打开摄像头
                if(line == (esp32_cam_head + "openCam") ){
                  cam_state = true;
                  client.println(esp32_cam_head + "Camera ON!");
                }
                // 关闭摄像头
                if(line == (esp32_cam_head + "closeCam") ){
                  cam_state = false;
                  client.println(esp32_cam_head + "Camera OFF!");
                }
                // 打开闪光灯
                if(line == (esp32_cam_head + "openLed") ){
                  digitalWrite(LED, HIGH);
                  client.println(esp32_cam_head + "Led ON!");
                }
                // 关闭闪光灯
                if(line == (esp32_cam_head + "closeLed") ){
                  digitalWrite(LED, LOW);
                  client.println(esp32_cam_head + "Led OFF!");
                }
              }
            }
            // 视频传输
            if(cam_state)
            {
              camera_fb_t * fb = esp_camera_fb_get();
              uint8_t * temp = fb->buf; //这个是为了保存一个地址,在摄像头数据发送完毕后需要返回,否则会出现板子发送一段时间后自动重启,不断重复
              if (!fb)
              {
                  Serial.println(esp32cam_to_esp32 +  "Camera Capture Failed");
              }
              else
              { 
                //先发送Frame Begin 表示开始发送图片 然后将图片数据分包发送 每次发送1430 余数最后发送 
                //完毕后发送结束标志 Frame Over 表示一张图片发送完毕 
                client.print("Frame Begin"); //一张图片的起始标志
                // 将图片数据分段发送
                int leng = fb->len;
                int timess = leng/maxcache;
                int extra = leng%maxcache;
                for(int j = 0;j< timess;j++)
                {
                  client.write(fb->buf, maxcache); 
                  for(int i =0;i< maxcache;i++)
                  {
                    fb->buf++;
                  }
                }
                client.write(fb->buf, extra);
                client.print("Frame Over");      // 一张图片的结束标志
                //Serial.print("This Frame Length:");
                //Serial.print(fb->len);
                //Serial.println(".Succes To Send Image For TCP!");
                //return the frame buffer back to the driver for reuse
                fb->buf = temp; //将当时保存的指针重新返还
                esp_camera_fb_return(fb);  //这一步在发送完毕后要执行,具体作用还未可知。        
              }
              delay(20);//短暂延时 增加数据传输可靠性
            }
        }
      }
      else
      {
          digitalWrite(ZHESHI_LED, HIGH);
          Serial.println(esp32cam_to_esp32 + "Connect To Tcp Server Failed!After 10 Seconds Try Again!");
          client.stop(); //关闭客户端
      }
      delay(10000);
    }

  3. TCP Server
    import socket
    import threading
    import time
    import cv2
    import numpy as np
    from tkinter import *
    # 监听连接
    def handle_accept():
        while True:
            # 阻塞连接 info一个socket对象
            info, addr = server.accept()
            client_list.append(info) #有客户端连接 将客户端对象放在列表
            # 开线程 用于接受客户端数据
            t = threading.Thread(target=recv_data, args=(info, addr))
            t.start()
    # 接收客户端数据
    def recv_data(sock, addr):
        temp_data = b''   # 保存一张照片数据
        while True:
            try:
                data = sock.recv(1430)
            except Exception as e:
                print(e)
                sock.close()
                client_list.remove(sock) #data = sock.recv(1430) 出现异常 先关闭在移除
            if bool(data): # 判断数据不为 b''
                # print('收到客户端的信息:' + data.decode('utf-8'))
                # 如果这一帧数据包的开头是 b'Frame Begin' 则是一张图片的开始
                #图像数据  标志位 b'Frame Begin'
                if data[0:len(begin_data)] == begin_data:
                    t1 = int(round(time.time() * 1000))
                    # 将这一帧数据包的开始标志信息(b'Frame Begin')清除   因为他不属于图片数据
                    data = data[len(begin_data):len(data)]
                    # 判断这一帧数据流是不是最后一个帧 最后一针数据的结尾时b'Frame Over'
                    while data[-len(end_data):] != end_data:
                        temp_data = temp_data + data  # 不是结束的包 讲数据添加进temp_data
                        data = sock.recv(1430)  # 继续接受数据 直到接受的数据包包含b'Frame Over' 表示是这张图片的最后一针
                    # 判断为最后一个包 将数据去除 结束标志信息 b'Frame Over'
                    temp_data = temp_data + data[0:(len(data) - len(end_data))]  # 将多余的(\r\nFrame Over)去掉 其他放入temp_data
                    # 显示图片
                    receive_data = np.frombuffer(temp_data, dtype='uint8')  # 将获取到的字符流数据转换成1维数组
                    r_img = cv2.imdecode(receive_data, cv2.IMREAD_COLOR)  # 将数组解码成图像
                    r_img = r_img.reshape(480, 640, 3)
                    t2 = int(round(time.time() * 1000))
                    fps = 1000 // (t2 - t1)
                    cv2.putText(r_img, "FPS" + str(fps), (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)
                    cv2.imshow('server_frame', r_img)
                    if cv2.waitKey(1) & 0xFF == ord('q'):
                        break
                    t1 = t2
                    print("接收到的数据包大小:" + str(len(temp_data)))  # 显示该张照片数据大小
                    temp_data = b''  # 清空数据 便于下一章照片使用
                # esp32发送过来的数据 标志位b'esp32_head:'
                if data[0:len(esp32_head)] == esp32_head:
                    print('来自ESP32的消息:' + data.decode())
                # esp32cam发送过来的数据 标志位 b'esp32_cam_head:'
                if data[0:len(esp32_cam_head)] == esp32_cam_head:
                    print('来自ESP32CAM的消息:' + data.decode())
    # 关闭ESP32cam摄像头 别忘记发送数据带 esp32_cam_head
    def close_cam():
        for s in client_list:
            try:
                s.send(esp32_cam_head + 'closeCam'.encode())
            except:
                try:
                    s.close()
                    client_list.remove(s)
                except:
                    print("Client is Closed!")
    # 打开ESP32cam摄像头 别忘记发送数据带 esp32_cam_head
    def open_cam():
        for s in client_list:
            try:
                s.send(esp32_cam_head + 'openCam'.encode())
            except:
                try:
                    s.close()
                    client_list.remove(s)
                except:
                    print("Client is Closed!")
    # 打开ESP32cam闪光灯 别忘记发送数据带 esp32_cam_head
    def open_led():
        for s in client_list:
            try:
                s.send(esp32_cam_head + 'openLed'.encode())
            except:
                try:
                    s.close()
                    client_list.remove(s)
                except:
                    print("Client is Closed!")
    # 关闭ESP32cam闪光灯 别忘记发送数据带 esp32_cam_head
    def close_led():
        for s in client_list:
            try:
                s.send(esp32_cam_head + 'closeLed'.encode())
            except:
                try:
                    s.close()
                    client_list.remove(s)
                except:
                    print("Client is Closed!")
    # 打开ESP32上的led 别忘记发送数据带 esp32_head
    def open_esp32_led():
        for s in client_list:
            try:
                s.send(esp32_head + 'openLed'.encode())
            except:
                try:
                    s.close()
                    client_list.remove(s)
                except:
                    print("Client is Closed!")
    # 关闭ESP32上的led 别忘记发送数据带 esp32_head
    def close_esp32_led():
        for s in client_list:
            try:
                s.send(esp32_head + 'closeLed'.encode())
            except:
                try:
                    s.close()
                    client_list.remove(s)
                except:
                    print("Client is Closed!")
    
    client_list = [] #列表存放客户端对象
    begin_data = b'Frame Begin' # 接受摄像头一张照片数据的起始标志
    end_data = b'Frame Over'    # 接受摄像头一张照片数据的结束标志
    esp32_head = b'esp32_head:' # 发送和接受esp32消息的标志头
    esp32_cam_head = b'esp32_cam_head:' # 发送和接受esp32cam消息的标志头
    # 创建窗口
    win = Tk()
    # 设置窗口大小
    win.geometry("440x300")
    # 添加一个Label用于显示视频
    label = Label(win)
    label.grid(row=0, column=0)
    btn = Button(win, text="关闭摄像头", command=close_cam)
    btn.grid(row=1, column=1)
    btn = Button(win, text="打开摄像头", command=open_cam)
    btn.grid(row=2, column=1)
    btn = Button(win, text="打开闪光灯", command=open_led)
    btn.grid(row=3, column=1)
    btn = Button(win, text="关闭闪光灯", command=close_led)
    btn.grid(row=4, column=1)
    btn = Button(win, text="打开ESP32灯", command=open_esp32_led)
    btn.grid(row=5, column=1)
    btn = Button(win, text="关闭ESP32灯", command=close_esp32_led)
    btn.grid(row=6, column=1)
    server = socket.socket() #创建回话对象
    host = ('192.168.0.2', 8080) #设置主机
    server.bind(host)  #建立长期的连接
    server.listen(5)   #设置监听
    # 开线程 监听连接
    t0 = threading.Thread(target=handle_accept)
    t0.start()
    win.mainloop()
    

你可能感兴趣的:(ESP32,tcp/ip,单片机,网络)