blinker控制esp32cam拍照并通过邮件发送图片
esp32cam是一款便宜的摄像头模块,但自制摄像头却有一些小小的问题,例如没有自己的服务器就无法远程获取图像,blinker由于服务器成本问题也不支持传图,于是,这里我就盯上了那些大公司,嫖一波他们。基本思路是esp32cam在接收到指令后拍照,然后通过邮件发送到指定邮箱中,具体过程是收到指令-拍照保存到SPIFFS中-mail服务从SPIFFS中读取文件添加到附件,发送。
种种原因,我选择使用两片esp32开发板,一个为esp32cam,另一个为之前用来上报温湿度,两片开发板使用udp通信,具体通信格式没定义,只要esp32cam接收到长度为12的udp包,就启动拍照并通过邮件发送。另一个esp32中负责接收blinker发来的信息,当为拍照按钮信息时向cam发指令。
同时当你处于cam局域网环境时,还能通过网页获取cam图像。
smtpData.setLogin("smtp.XXX.com", 465, "123456789@XX.com", "123456789");
//(smtp服务器,服务器端口,账号,授权码)
smtpData.setSender("ESP32", "123456789@XX.com");
//设置发件人名称和地址
//将电子邮件优先级或重要性设置为高,正常,低或1到5(1为最高)
smtpData.setPriority("High");
//邮件标题
smtpData.setSubject("测试邮件");
//设置消息-普通文本或html格式
smtpData.setMessage("<div style=\"color:#ff0000;font-size:20px;\">Hello World! - From ESP32</div>", true);
smtpData.addRecipient("123456789@XX.com");//收件人
smtpData.addAttachFile("/photo.jpg");//从SPIFFS中添加附件
smtpData.setFileStorageType(MailClientStorageType::SPIFFS);//附件来源,SD或SPIFFS
//开始发送电子邮件
if (!MailClient.sendMail(smtpData))
Serial.println("Error sending Email, " + MailClient.smtpErrorReason());
//清除数据释放内存
smtpData.empty();
Serial.println("send over");
登录邮箱需要授权码,或者邮箱密码,不同服务商不同,以qq邮箱为例
第一步,登录邮箱网页版,点击最上面的设置
之后选择账户选项
向下翻,将IMAP/SMTP服务设置为开启
上图为qq邮箱的设置
下面为esp32cam中完整代码
#include "WiFi.h"
#include "esp_camera.h"
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "soc/soc.h" // Disable brownour problems
#include "soc/rtc_cntl_reg.h"// Disable brownour problems
#include "driver/rtc_io.h"
#include <ESPAsyncWebServer.h>
#include <StringArray.h>
#include <SPIFFS.h>
#include <FS.h>
#include "ESP32_MailClient.h"
#include <WiFiUdp.h>
SMTPData smtpData;
WiFiUDP Udp; //创建UDP对象
unsigned int localUdpPort = 2333; //本地端口号
const char* ssid = "ssid";
const char* password = "key";
// 设置网页端口
AsyncWebServer server(80);
boolean takeNewPhoto = false;
// 保存到 SPIFFS中的文件名
#define FILE_PHOTO "/photo.jpg"
// OV2640 camera module pins (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
//html界面
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { text-align:center; }
.vert { margin-bottom: 10%; }
.hori{ margin-bottom: 0%; }
</style>
</head>
<body>
<div id="container">
<h2>ESP32-CAM</h2>
<p>maybe need 5s</p>
<p>
<button>change</button>
<button>take</button>
<button>download</button>
</p>
</div>
<div><img src="saved-photo" id="photo" width="70%"></div>
</body>
<script>
var deg = 0;
function capturePhoto() {
var xhr = new XMLHttpRequest();
xhr.open('GET', "/capture", true);
xhr.send();
}
function rotatePhoto() {
var img = document.getElementById("photo");
deg += 90;
if(isOdd(deg/90)){ document.getElementById("container").className = "vert"; }
else{ document.getElementById("container").className = "hori"; }
img.style.transform = "rotate(" + deg + "deg)";
}
function isOdd(n) { return Math.abs(n % 2) == 1; }
</script>
</html>)rawliteral";
void setup() {
// Serial port for debugging purposes
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
if (!SPIFFS.begin(true)) {
Serial.println("An Error has occurred while mounting SPIFFS");
ESP.restart();
}
else {
delay(500);
Serial.println("SPIFFS mounted successfully");
}
Udp.begin(localUdpPort); //启用UDP监听以接收数据
// Turn-off the 'brownout detector'
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
// OV2640 camera module
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_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_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;
if (psramFound()) {
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// Camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
ESP.restart();
}
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send_P(200, "text/html", index_html);
});
server.on("/capture", HTTP_GET, [](AsyncWebServerRequest * request) {
takeNewPhoto = true;
request->send_P(200, "text/plain", "Taking Photo");
});
server.on("/saved-photo", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send(SPIFFS, FILE_PHOTO, "image/jpg", false);
});
// Start server
server.begin();
}
void loop() {
if (takeNewPhoto) //网页拍照
{
capturePhotoSaveSpiffs();
takeNewPhoto = false;
}
int packetSize = Udp.parsePacket(); //获取当前队首数据包长度
if (packetSize) //如果有数据可用
{
char buf;
Udp.read(buf, packetSize); //读取当前包数据
Serial.println(packetSize);
Serial.print("Received: ");
Serial.println(buf);
Serial.print("From IP: ");
Serial.println(Udp.remoteIP());
Serial.print("From Port: ");
Serial.println(Udp.remotePort());
if(packetSize==12)//如果接收到包长度为12,拍照并发送
{
Serial.println("Send mail");
capturePhotoSaveSpiffs();
mail();
}
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); //准备发送数据
Udp.print("Received: "); //复制数据到发送缓存
Udp.write((const uint8_t*)buf, packetSize); //复制数据到发送缓存
Udp.endPacket(); //发送数据
}
}
// Check if photo capture was successful
bool checkPhoto( fs::FS &fs ) {
File f_pic = fs.open( FILE_PHOTO );
unsigned int pic_sz = f_pic.size();
return ( pic_sz > 100 );
}
// Capture Photo and Save it to SPIFFS
void capturePhotoSaveSpiffs( void ) //获取图像
{
camera_fb_t * fb = NULL; // pointer
bool ok = 0;
do {
Serial.println("Taking a photo...");
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
return;
}
// Photo file name
Serial.printf("Picture file name: %s\n", FILE_PHOTO);
File file = SPIFFS.open(FILE_PHOTO, FILE_WRITE);
// Insert the data in the photo file
if (!file) {
Serial.println("Failed to open file in writing mode");
}
else {
file.write(fb->buf, fb->len);
Serial.print("The picture has been saved in ");
Serial.print(FILE_PHOTO);
Serial.print(" - Size: ");
Serial.print(file.size());
Serial.println(" bytes");
}
// 关闭文件
file.close();
esp_camera_fb_return(fb);
// 检查文件是否已正确保存在SPIFFS中
ok = checkPhoto(SPIFFS);
} while ( !ok );
}
void mail()//邮箱发送
{
//(smtp服务器,服务器端口,账号,授权码)
smtpData.setSender("ESP32", "123456789@XX.com");
//设置发件人名称和地址
//将电子邮件优先级或重要性设置为高,正常,低或1到5(1为最高)
smtpData.setPriority("High");
//邮件标题
smtpData.setSubject("测试邮件");
//设置消息-普通文本或html格式
smtpData.setMessage("<div style=\"color:#ff0000;font-size:20px;\">Hello World! - From ESP32</div>", true);
smtpData.addRecipient("123456789@XX.com");//收件人
smtpData.addAttachFile("/photo.jpg");//从SPIFFS中添加附件
smtpData.setFileStorageType(MailClientStorageType::SPIFFS);//附件来源,SD或SPIFFS
//开始发送电子邮件
if (!MailClient.sendMail(smtpData))
Serial.println("Error sending Email, " + MailClient.smtpErrorReason());
//清除数据释放内存
smtpData.empty();
Serial.println("send over");
下面为另一块esp32的代码,除了控制cam以为你还可以添加其他功能,Button在app中设置为开关,按键名称为jpg,这个blinker入门级的代码就不详细讲了
#include <WiFi.h>
#include <WiFiUdp.h> //引用以使用UDP
#define BLINKER_WIFI
#include <Blinker.h>
char auth[] = "XXXXXXX";
char ssid[] = "XXXX";
char pswd[] = "XXXXXX";
#define BUTTON_1 "jpg"
byte buf;
BlinkerButton Button1(BUTTON_1);
WiFiUDP Udp; //创建UDP对象
const char * udpAddress = "192.168.0.255";//udp接收地址,我这里是使用的广播
const int udpPort = 2333;//udp端口
void button1_callback(const String & state)
{
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
BLINKER_LOG("get button state: ", state);
if (state == BLINKER_CMD_ON) {
BLINKER_LOG("Toggle on!");
Button1.print("on");
send();
Button1.print("off");
}
}
void dataRead(const String & data)
{
BLINKER_LOG("Blinker readString: ", data);
Blinker.vibrate();
uint32_t BlinkerTime = millis();
Blinker.print("millis", BlinkerTime);
}
void setup()
{
Serial.begin(115200);
Serial.println();
Blinker.begin(auth, ssid, pswd);
Blinker.attachData(dataRead);
Button1.attach(button1_callback);
Blinker.notify("! 系统已上线!");
Udp.begin(udpPort); //启用UDP监听以接收数据
}
void loop()
{
Blinker.run();
}
void send()//发送数据
{
byte buf;
Udp.beginPacket(udpAddress, udpPort); //准备发送数据
Udp.print("Received: "); //复制数据到发送缓存
Udp.write((byte*)buf, 2); //复制数据到发送缓存,我也不知道为啥长度会自动加10
Udp.endPacket(); //发送数据
}
邮箱收到的示例
局域网网页图片
另附部分库,
编译过程中缺啥装啥。
本人菜鸟一个,有错误请指正。
f969439474 发表于 2020-12-3 19:12
你好,请问有教程吗?
方便的话,加969439474 厉害,顶一下 你好,请问有教程吗?
本帖最后由 Sn5dxlx 于 2021-3-20 15:48 编辑
楼主 请问一下你的 smtpData.setLogin("smtp.qq.com", 465, "123456789@qq.com", "xxxx");是不是没加到下面的代码里面 需要自己加0.0? 调试串口出现 Error sending Email, identification failed
请问一下出现这个错误是什么原因? Sn5dxlx 发表于 2021-3-21 17:21
调试串口出现 Error sending Email, identification failed
请问一下出现这个错误是什么原因? ...
这个版本的esp32 mail库作者已经弃用,可以自己去搜新的esp mail库 三月份可以运行 同样的代码现在不行了 是不是blinker改了什么 我看 发送邮件的串口是正常的,大佬的还可以用嘛?
页:
[1]