- ESP32
#include <Arduino.h>
/*
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!");
}
}
}
- ESP32CAM
#include <Arduino.h>
#include <WiFi.h>
#include "esp_camera.h"
#include <vector>
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);
}
- 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()