blinker控制esp32cam拍照并通过邮件发送图片-Arduino中文社区 - Powered by Discuz! Archiver

吹向陆地的风 发表于 2020-5-15 17:38

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:13

f969439474 发表于 2020-12-3 19:12
你好,请问有教程吗?

方便的话,加969439474

千斤顶 发表于 2020-5-16 08:10

厉害,顶一下

f969439474 发表于 2020-12-3 19:12

你好,请问有教程吗?

Sn5dxlx 发表于 2021-3-20 14:51

本帖最后由 Sn5dxlx 于 2021-3-20 15:48 编辑

楼主 请问一下你的 smtpData.setLogin("smtp.qq.com", 465, "123456789@qq.com", "xxxx");是不是没加到下面的代码里面 需要自己加0.0?

Sn5dxlx 发表于 2021-3-21 17:21

调试串口出现 Error sending Email, identification failed
请问一下出现这个错误是什么原因?

吹向陆地的风 发表于 2021-3-22 09:26

Sn5dxlx 发表于 2021-3-21 17:21
调试串口出现 Error sending Email, identification failed
请问一下出现这个错误是什么原因? ...

这个版本的esp32 mail库作者已经弃用,可以自己去搜新的esp mail库

Sn5dxlx 发表于 2021-4-13 12:12

三月份可以运行 同样的代码现在不行了 是不是blinker改了什么 我看 发送邮件的串口是正常的,大佬的还可以用嘛?
页: [1]
查看完整版本: blinker控制esp32cam拍照并通过邮件发送图片