需求:
做一个小型的监控,类似电子猫眼,监测到人之后,取一张图 然后发送到自己的邮箱。
架构:
1.sr602 传感器监测到人
2. esp32 cam 取图 并通过mqtt协议传到远端服务器
3, 服务器利用python 搭建一个mqtt客户端,订阅到数据后 将图片保存,并发送到指定邮箱
硬件连接:
结构设计:
软件:
服务端代码
#mqtt客户端
import paho.mqtt.client as mqtt
import logging
import os
import email_send
APPEAR_TOPIC = "APPEAR_TOPIC"
DETAIL_TOPIC = "DETAIL_TOPIC"
mqtt_server = 'xxxxxxxxxx'
image_path="images"
image_index=0
def on_connect(client, userdata, flags, rc):
logging.info('连接到MQTT服务器 '+str(rc))
def on_message(client, userdata, msg):
print(msg.topic + " " + str(msg.payload))
global image_index
if msg.topic ==APPEAR_TOPIC:
if image_index!=0:
#发送全部图片
image_index=0
email_send.send_email(image_path)
logging.info('发送到邮箱完毕')
if msg.topic == DETAIL_TOPIC:
logging.info('收到图片,准备保存到本地')
save_location = "images/"+str(image_index)+".jpg"
f = open(save_location, 'wb')
data = msg.payload
f.write(data)
f.close()
image_index+=1
logging.info('图片保存到本地完毕')
def run():
logging.basicConfig(level=logging.INFO)
client = mqtt.Client(protocol=3)
client.on_connect = on_connect
client.on_message = on_message
client.connect(host=mqtt_server,port=1883,keepalive=60,bind_address="")
client.subscribe(APPEAR_TOPIC)
client.subscribe(DETAIL_TOPIC)
logging.info('启动 MQTT客户端... \n')
try:
client.loop_forever()
except KeyboardInterrupt:
pass
logging.info('停止 MQTT 客户端\n')
if __name__ =='__main__':
run()
#邮件发送
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
from datetime import datetime
import logging
import os
import shutil
def send_email(img_path=""):
# 设置发件人、收件人和邮件主题
from_email = "xxxx"
to_email = "xxxx"
subject = "注意,有人出现"
logging.info('设置收发地址...')
# 创建邮件对象
msg = MIMEMultipart()
msg['From'] = from_email
msg['To'] = to_email
msg['Subject'] = subject
# 添加邮件正文
body = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
msg.attach(MIMEText(body, 'plain'))
logging.info('添加正文...')
for root,dirs,files in os.walk(img_path):
for file in files:
if file.endswith(".jpg"):
img = os.path.join(root,file)
# 添加附件
attachment_filename = img
attachment_path = img
attachment = open(attachment_path, "rb")
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', f'attachment; filename= {attachment_filename}')
msg.attach(part)
# 连接到SMTP服务器
smtp_server = "smtp.yeah.net" # 修改为你的SMTP服务器
smtp_port = 465
smtp_username = "xxxx" # 修改为你的邮箱地址
smtp_password = "xxxx" # 修改为你的邮箱密码
server = smtplib.SMTP_SSL(smtp_server,smtp_port)
logging.info('登录邮件服务器...')
server.login(smtp_username, smtp_password)
logging.info('发送邮件')
# 发送邮件
server.sendmail(from_email, to_email, msg.as_string())
# 关闭连接
server.quit()
shutil.rmtree(img_path)
# 重新创建空文件夹
os.makedirs(img_path)
if __name__ =='__main__':
logging.basicConfig(level=logging.INFO)
send_email("images",True)
客户端代码
#include "esp_camera.h"
#include
#include
#define CAMERA_MODEL_AI_THINKER
#include "camera_pins.h"
//WIFI 用户名 密码
//const char* ssid = "xxxx";
//const char* password = "xxxx";
const char* ssid = "xxxx";
const char* password = "xxxx";
//MQTT 服务器地址
const char* mqttServer = "xxxx";
uint16_t mqttPort = 1883;
const char* APPEAR_TOPIC = "APPEAR_TOPIC";
const char* DETAIL_TOPIC = "DETAIL_TOPIC";
//检测是否有人
uint8_t FOUND_PEOPLE_PIN = 14;
//人出现的次数
uint8_t found_count = 0;
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
//调试串口
Serial.begin(115200);
Serial.setDebugOutput(true);
//人体红外检测引脚
pinMode(FOUND_PEOPLE_PIN, INPUT);
//配置相机
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
config.frame_size = FRAMESIZE_VGA;
config.jpeg_quality = 10;
config.fb_count = 1;
// 相机初始化
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
sensor_t* s = esp_camera_sensor_get();
s->set_vflip(s, 1);
s->set_brightness(s, 2);
s->set_saturation(s, -2);
s->set_framesize(s, FRAMESIZE_VGA);
//连接 WIFI
WiFi.begin(ssid, password);
WiFi.setSleep(false);
while (WiFi.status() != WL_CONNECTED) {
Serial.println("Connecting to WIFI ...");
delay(500);
}
Serial.println("WiFi connected");
//连接mqtt 服务器。 640*480 图片大小
client.setBufferSize(50 * 1024);
client.setServer(mqttServer, mqttPort);
while (!client.connected()) {
if (client.connect("app")) {
Serial.println("Connected to MQTT");
} else {
Serial.println("Failed to connect to MQTT server, ");
Serial.print(client.state());
delay(1000);
}
}
Serial.print(WiFi.localIP());
}
//上传图片
void take_send_photo() {
Serial.println("Taking picture...");
camera_fb_t* fb = NULL;
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
return;
}
if (client.beginPublish(DETAIL_TOPIC, fb->len + sizeof(long), false)) {
unsigned long m = millis();
int noBytes;
noBytes = client.write(fb->buf, fb->len);
noBytes = client.write((byte*)&m, sizeof(long));
if (!client.endPublish()) {
Serial.println("\nupload image error.");
}
}
esp_camera_fb_return(fb);
Serial.println("upload image ok.");
}
void loop() {
//检测到有人后 拍照并上传
if (digitalRead(FOUND_PEOPLE_PIN) == 1) {
take_send_photo();
found_count += 1;
delay(2000);
} else {
found_count = 0;
}
if (found_count >= 3) {
found_count = 0;
client.publish(APPEAR_TOPIC, "", 0);
}
client.loop();
}
注意事项:
1,接收图片的邮箱,需要开通smtp服务。
2,mqtt发送图片,不需要转成base64格式,但是需要重新设置下缓存大小。
3,这个玩意 发热很严重,还没测试 两节18650电池能用多久。