1 Star 0 Fork 1

VENTIM / 22.7 esp32cam多功能摄像机

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
esp32cam_TFT14.0 优化存图的文件夹序号存,整理.txt 62.45 KB
一键复制 编辑 原始数据 按行查看 历史
志宇益生菌 提交于 2023-10-28 11:48 . 提交已有的代码备份文件
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588
// Example for library:
// https://github.com/Bodmer/TJpg_Decoder
//这里使用内核0 解码jpg 内核1执行逻辑代码和tft显示
//并行使用esp32 的2个内核,例程测试解码+渲染=66+37=66ms
/*我自己测试结果:
//自带320 240 图片,无缩放显示160 120上,36ms
//缩放2 79ms
//改为DMA传输 缩放2 为80ms 使用DMA改善不大?
//测试改为摄像头图像 缩放1 24-28ms, DMA 比上一个解码库快20ms左右!
//测试 缩放1 320 240 的一角 48ms左右
//测试 缩放2 320 240变160 120 65-71ms,整体都比上一个库快!
FRAMESIZE_QVGA (320 x 240)
FRAMESIZE_CIF (352 x 288)
FRAMESIZE_VGA (640 x 480)
FRAMESIZE_SVGA (800 x 600)
FRAMESIZE_XGA (1024 x 768)
FRAMESIZE_SXGA (1280 x 1024)
FRAMESIZE_UXGA (1600 x 1200)
*/
//11.1 移植eeprom的代码,//实现模式、延时摄相间隔时间 的修改保存. 字节存储麻烦
//11.2 增加读取块函数,用结构体存储,部分,加函数检查改正数值or全默认,
//12.1 拍照后,等界面是否保存重启/退出。
//12.2 [1功能]全放结构体
//12.3 小完善,可正常运行,但部分照片存储后为空
//12.4 移植rtc,文件夹/文件命名ok, sd存图有点异常,7拍照是正常的
//12.5 解决sd存图异常情况 10.1存图正常 11.2 正常 12.1开始一半成功
//12.6 可设置延时摄影间隔执行,可存储,任意按键重启(仅开机的前10s内可进入并修改)
//12.7 延时摄影模式下 开机进入设置则不响应任意按键重启,可更改取消
//12.8 整理
//13.1 读取电压带参数 适应部分不显示情景(未使用)。确定网页页面,表单解析打印ok
//13.2 内嵌网页变量,更改关旧的重新初始化新的摄像头。 ok res != ESP_OK不该注释!
//13.3 完善信息 更改摄像头参数才重新初始化 恢复res != ESP_OK, 》解决刷新无图问题
//13.4 机设置改cam参数后,同样重新初始化视频流;改好设置界面;led引脚初始化放初始化最后,即可控制 》解决led亮度异常问题
//14.0 dir_num_change_flag变量在check_dir_num()中判断保存 》只有需要拍+存,才会文件夹数+1保存 +整理
//界面:1、tft摄像机,2、局域网摄像头 3、物联网摄像头 4、桌面天气 5、手机来蓝牙拍照遥控 6、手机蓝牙丢失提醒 .7、播放音乐 8、网络mic/网络收音机
//开发按键功能菜单, 增加wifi图传功能,桌面天气
//自动复位后,存储SD卡失败!!再次测试可以!
//SD卡存储 与 tft分时使用! 或者能否同时. 好像不能
//大led引脚是否改动?
/* //(短按/长按) 【【功能设计】】 灭屏下 任意按键亮屏!
* 面对屏幕
* key3 key4
* key1 key2
按键中心像素:4312 106,0 154,0 121,120 151,120 最小字符6*8
//【tft摄像机】 质量、大小、开关屏、放大、延时摄影、录像?
//3(设置/切换模式) 4(拍照存储/提示界面) 1(led增亮/增亮+) 2(led减暗/减暗+)
//[设置界面] 3(保存退出/切换模式) 4(ok下一项/放弃设置) 1(加) 2(减)
//【局域网wifi图传】 网页界面控制 默认不亮屏,
//3( /切换模式) 4(/提示界面) 1(led增亮/ ) 2(led减暗/灭屏)
//
//【物联网摄像头】 小程序控制查询
//3( /切换模式) 4(/提示界面+亮屏) 1( ) 2( /灭屏)
//【桌面天气】 wifi联网tft显示 关闭摄像头
//3( /切换模式) 4(/提示界面+亮屏) 1( ) 2( /灭屏)
//【BLE键盘】 键盘控制
//3( /切换模式) 4(/切换按键功能) 1( ) 2( )
上 下 左 右 上一页 下一页
复制 粘贴 剪切 全选 撤回 保存
暂停/播放 + - 上一曲 下一曲 》ok
上 左 ctrl1s alt1s ctrl+alt2s 按下ctrl/松开ctrl 》对应电脑缺少的按键
//【BLE鼠标】 鼠标
//3( /切换模式) 4(/切换按键功能) 1( ) 2( )
鼠标移动: 上 下 左 右 ,左键 右键返回 按多次移动距离越远。
页面滚动: 上 下 左 右 ,1s下滚1次 3s下滚1次 一次执行一下
点击按键: 左边 中间 右边 左键1s1次 , 前进键 后退键 点击1次
*/
//现在tft的引脚连接: 在tft的User_Setup.h里
//#define TFT_MOSI 13 固定
//#define TFT_SCLK 14 固定
//#define TFT_CS 15 //可省略 片选 可接地持续运行
//#define TFT_DC 2 //1数据 0命令
#define TFT_RST 12 //可省略 低电平复位 可连接esp32复位引脚,上电复位 BLK也连在这个引脚
/*SD卡占用引脚: 剩余引脚: 串口(1、3),0,16(spram占用) 33(adc做多按键!)
//HS2_DATA0 2 spi-MISO
//HS2_DATA1 4 可省略
//HS2_DATA2 12 可省略
//HS2_DATA3 13 spi-SS
//HS2_CMD 15 spi-MOSI
//HS2_CLK 14 spi-SCK
*/
#define adc_io 33 //adc输入,开机检测电池电压 按键降低电压,可判断
#define adc_power_change 0.00112//(3.3* 197.0 / 150.0 /4096.0 ) //adc转化为电池电压参数
int adc_num = 0; //adc数值
float power_v=3.6; //理论电压值 变量名不改
#define power_adjust ((power_v-3.16)*0.46) //调整电压值 非线性
char adc_flag=0; //0可更新电源电压 1-4为对应按键按下
#define adc_flag0_upnum 5 //当按键按下<=检测次数,认为是短按
#define adc_Lpress_num 13 //当按键 按下>检测次数 认为长按
#define LED1 4 //大白灯 高电平亮 可pwm
#define LED1_channel 5//8 //对应的通道 好像8-15的在摄像头重新初始化后无法使用
int LED1_bright = 0; //大白灯 亮度 0-255
/*#########################
Taking picture...
Camera capture ok
===============
JPEG image info
===============
Width :320
Height :240
Components :1
MCU / row :20
MCU / col :30
Scan type :2
MCU width :16
MCU height :8
===============
Total render time was : 80 ms
#########################
Mouse 1
===============
JPEG image info
===============
Width :160
Height :107
Components :1
MCU / row :10
MCU / col :7
Scan type :4
MCU width :16
MCU height :16
===============
Total render time was : 51 ms
*/
//#define minimum(a,b) (((a) < (b)) ? (a) : (b)) //返回小的数值
#define u8 unsigned char
#define u16 unsigned int
//头文件
#include <ESP32Time.h>
#include <EEPROM.h>
#include <SPI.h>
#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
#include <TJpg_Decoder.h> //该文件里注释了//#include <LittleFS.h>
#include "FS.h"
#include "SD_MMC.h"
#include "esp_camera.h" //默认是CAMERA_MODEL_AI_THINKER的,改动在下面camera_init函数改
#include <WiFi.h>
#include "esp_http_server.h"
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h" //禁用掉电检测器 用
#include "soc/rtc_cntl_reg.h" //禁用掉电检测器 用
const char* ssid = "hello123";
const char* password = "123hello";
//const char* ssid = "CAOMUFAN";
//const char* password = "cmf88888";
ESP32Time rtc(0); // 偏置小时 GMT+3600s
struct tm timeinfo = rtc.getTimeStruct();
//wifi服务器,给wifi cam用
WiFiServer server(80);
String header; //存放 接收到client的请求 内容
unsigned long currentTime=0; //有关client超时时间 当前
unsigned long previousTime = 0; //有关client超时时间 上次
char reinit_flag1=0; //是否需要重新初始化 1当前重新初始化摄像头
#define server_timeoutTime 2000 //有关client超时时间 ms
int forwards = 0; //有关网页按键 变量 0 off 1 on
#define PART_BOUNDARY "123456789000000000000987654321"
//有关视频数据流的
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
httpd_handle_t stream_httpd = NULL; //句柄 有关开启数据流
//图片相关
camera_fb_t * fb = NULL; //照片指针
//char frame_size_num1=2; //有关设置图片大小的 0-8 越大越大
//char jpeg_quality_num1=10; //有关图片 质量 越小越好 10跟20好像肉眼差不多
//unsigned char time_span1=0; //用于演示摄影间隔 s
//DMA有关的 缓冲区
uint16_t dmaBuffer1[16*8]; //乒乓缓冲区1
uint16_t dmaBuffer2[16*8]; //2
uint16_t* dmaBufferPtr = dmaBuffer1; //指向缓冲区
bool dmaBufferSel = 0; //选哪一个区
//文件路径名称
String path = "/2020_6_18_TIME_19_0_47.jpg"; //空的
//其他变量
char dir_num_change_flag=0; //开机已加1的标记 后续拍摄保存1次
uint16_t main_count=0; //主循环 统计循环多少次
//char esp32cam_mode=0; //esp32cam的模式,0-3对应4种 开发功能——————————————————————————————————————————
char core0_work_flag = 0; //内核0 是否运行 0不运行 1运行
long tft_work_millis=0; //tft亮屏 最后操作时刻
#define tft_light_times 5 //无操作亮屏时间 s
long span_pic_millis=0; //用于延时摄影的临时millis变量 开机10s后才开始延时摄影
// 全局变量对处理器0和1都可用
TaskHandle_t Task1; //任务句柄
//const uint8_t* arrayName; // 包含Jpeg的闪存阵列的名称
bool decoding_flag = false; //1开始+正在解码 0全部解码完成
bool mcuReady_showOk_flag = false; // 当前已解码MCU块 1解码完成未显示 0显示完成可以解码下一个
uint16_t mcuBuffer[16*16]; // 抓取解码MCU块快照的缓冲区
int32_t mcu_x, mcu_y, mcu_w, mcu_h; // 呈现MCU的位置 显示的x,y,mcu的宽高
#define RGBto565(R,G,B) (((R >> 3) << 11) | ((G >> 2) << 5) | (B >> 3)) /* RGB转换宏 565 */
//没问题后,需要进行合并
typedef struct//模拟eeprom,存储的结构体. 默认关机前得手动选保存参数
{
u8 esp32cam_mode1; //选择的工作页面序号 esp32cam的模式,0-3对应4种 开发功能
u8 pic_size; //图片大小 0-8 默认2
u8 pic_quality; //图片质量 0-64 默认10
u8 time_span1; //摄像机下 延时摄影的间隔s
u16 dir_num;
// char ignore_APP_versions[5]; //
} all_eeprom_struct_t;
all_eeprom_struct_t all_eeprom_info = {0};
//存储到eeprom的相关参数(结构体) 范围检查 超则改默认 不保存
void eeprom_info_check(u8 default_flag=0){ //传1,则全改为默认值
if(default_flag==1){ //设为 默认值
all_eeprom_info.esp32cam_mode1=0;
all_eeprom_info.pic_size=2;
all_eeprom_info.pic_quality=10;
all_eeprom_info.time_span1=0;
all_eeprom_info.dir_num=1;
}
else{ //检查+纠正
if(all_eeprom_info.esp32cam_mode1<0||all_eeprom_info.esp32cam_mode1>3) all_eeprom_info.esp32cam_mode1=0;
if(all_eeprom_info.pic_size<0||all_eeprom_info.pic_size>8) all_eeprom_info.pic_size=2;
if(all_eeprom_info.pic_quality<0||all_eeprom_info.pic_quality>64) all_eeprom_info.pic_quality=10;
if(all_eeprom_info.time_span1<0||all_eeprom_info.time_span1>255) all_eeprom_info.time_span1=0;
}
}
//读eeprom数据块 返回值暂时没什么用
bool EEPROM_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead){
u8 val=0;
while(NumToRead)
{
// if(AT24CXX_ReadOneByte(ReadAddr++,&val)!=0) return 1; //好像无法判断长度
val=EEPROM.read(ReadAddr++);
*pBuffer++=val;
NumToRead--;
}
return 0;
}
//写eeprom数据块 返回值没什么用
bool EEPROM_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite){
while(NumToWrite--)
{
// if(AT24CXX_WriteOneByte(WriteAddr,*pBuffer)!=0) return 1;
EEPROM.write(WriteAddr, *pBuffer);
WriteAddr++;
pBuffer++;
}
EEPROM.commit();
return 0;
}
//有关摄像头及解码
bool camera_init(char frame_size_num = 3 , char jpeg_quality_num = 12){ //图像大小 0-6 越大越大
// 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
//配置摄像头 参数
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;
//判断 若有PSRAM则质量更高一些
if (psramFound()) { //有PSRAM硬件,更大的ram空间
Serial.println("psramFound!"); //此时io16被占用 CS
switch(frame_size_num){ //FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
case 0: config.frame_size = FRAMESIZE_QQVGA; break; //QQVGA (160 x 120)
case 1: config.frame_size = FRAMESIZE_QQVGA2; break; //QQVGA2 (128 x 160)
//HQVGA
case 2: config.frame_size = FRAMESIZE_QVGA; break; //QVGA (320 x 240)
case 3: config.frame_size = FRAMESIZE_CIF; break; //CIF (352 x 288)
case 4: config.frame_size = FRAMESIZE_VGA; break; //VGA (640 x 480)
case 5: config.frame_size = FRAMESIZE_SVGA; break; //SVGA (800 x 600)
case 6: config.frame_size = FRAMESIZE_XGA; break; //XGA (1024 x 768)
case 7: config.frame_size = FRAMESIZE_SXGA; break; //SXGA (1280 x 1024)
case 8: config.frame_size = FRAMESIZE_UXGA; break; //UXGA (1600 x 1200)
default: frame_size_num=3;config.frame_size = FRAMESIZE_SVGA; break;
}
Serial.printf("frame_size = %d [0-8]\r\n", frame_size_num);
Serial.printf("jpeg_quality = %d [0-xx]\r\n", jpeg_quality_num);
config.jpeg_quality = jpeg_quality_num; //10; //越小越好
config.fb_count = 2;
} else { //没有则质量低一些
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
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 false;
}
else return true;
}
//处理器0 当前MCU解码后 拷贝副本
bool core0_mcu_decoded(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap){ //处理器0 调用执行
if ( y >= tft.height() ) return 0; //停止进一步解码,因为图像已超出屏幕底部
while(mcuReady_showOk_flag) yield(); //如果最后一个MCU块到TFT的渲染仍在进行中,请在此等待
memcpy(mcuBuffer, bitmap, 16*16*2); //复制当次解码的 MCU块图像
mcu_x = x; // Grab postion and size of MCU block
mcu_y = y;
mcu_w = w;
mcu_h = h;
mcuReady_showOk_flag = true; //告知处理器1可以开始渲染MCU 解码好了未显示
return 1; // 返回1以解码下一个Jpeg MCU块
}
//处理器0 开始及解码Jpeg图像
void core0_decode_jpg(void* p) { //处理器0 执行
for(;;) {
// 解码Jpeg图像
if (decoding_flag) { //1 才开始进行解码
TJpgDec.drawJpg(0, 0, fb->buf, fb->len); // 运行,直到完整的图像解码,阻塞
decoding_flag = false; // 0 表示解码结束
}
yield(); //节省资源给其他进程运行时间 Must yield in this loop
if(core0_work_flag==0) break;
}
vTaskDelete(NULL); //结束当前任务
}
//DMA显示已解码的jpg 其实是跟解码进程并行执行
void show_JPEG_160x120DMA(int ypos=8) { //使用dma的方式 屏幕刷新不闪烁! 这个速度下不用dma也不闪吧?
//espcam中 mcu_w=16,mcu_h=8
uint16_t w = 0, h = 0;
//检索关于图像的信息
//TJpgDec.getJpgSize(&w, &h, fb->buf, fb->len);
//Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h);
uint32_t drawTime = millis(); //有关计算耗时
mcuReady_showOk_flag = false; // Flag which is set true when a MCU block is ready for display
decoding_flag = true; // Flag to tell task to decode the image
//仅在解码正在进行或MCU准备好渲染时渲染MCU块
//注意:需要或mcuReady_showOk_flag,以便在解码结束后呈现最后一个块
// 必须首先使用startWrite,以便在DMA和SPI通道设置保持配置期间TFT片选保持低电平
tft.startWrite();
while(decoding_flag || mcuReady_showOk_flag) {
if (mcuReady_showOk_flag) {
//tft.pushImage(mcu_x, mcu_y, mcu_w, mcu_h, mcuBuffer);
if (dmaBufferSel) dmaBufferPtr = dmaBuffer2;
else dmaBufferPtr = dmaBuffer1;
dmaBufferSel = !dmaBufferSel; //交替选择 缓冲区
tft.pushImageDMA(mcu_x, mcu_y + ypos, mcu_w, mcu_h, mcuBuffer, dmaBufferPtr);
//tft.pushImageDMA(mcu_x, mcu_y, mcu_w, mcu_h, mcuBuffer, dmaBufferPtr);
mcuReady_showOk_flag = false;
}
// Must yield in this loop
yield();
}
// 必须使用endWrite来释放TFT片选并释放SPI通道
tft.endWrite();
// 计算绘制图像需要多长时间
drawTime = millis() - drawTime;
Serial.print(F("render(ms):")); Serial.println(drawTime);
}
//SDMMC初始化------------
void sd_init(){
if (!SD_MMC.begin()) {
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD_MMC.cardType();
if (cardType == CARD_NONE) {
Serial.println("No SD_MMC card attached");
return;
}
Serial.print("SD_MMC Card Type: ");
if (cardType == CARD_MMC) {
Serial.println("MMC");
}
else if (cardType == CARD_SD) {
Serial.println("SDSC");
}
else if (cardType == CARD_SDHC) {
Serial.println("SDHC");
}
else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024); //获取SD卡大小,大小单位是MB
Serial.printf("SD_MMC size: %lluMB\n", cardSize);
//SD卡测试
// listDir(SD_MMC, "/", 0);
// createDir(SD_MMC, "/mydir");
// listDir(SD_MMC, "/", 0);
// removeDir(SD_MMC, "/mydir");
// listDir(SD_MMC, "/", 2);
// writeFile(SD_MMC, "/hello.txt", "Hello ");
// appendFile(SD_MMC, "/hello.txt", "World!\n");
// readFile(SD_MMC, "/hello.txt");
// deleteFile(SD_MMC, "/foo.txt");
// renameFile(SD_MMC, "/hello.txt", "/foo.txt");
// readFile(SD_MMC, "/foo.txt");
// testFileIO(SD_MMC, "/test.txt");
// Serial.printf("Total space: %lluMB\n", SD_MMC.totalBytes() / (1024 * 1024));
// Serial.printf("Used space: %lluMB\n", SD_MMC.usedBytes() / (1024 * 1024));
//
}
void sd_deinit(){
SD_MMC.end(); //卸载
// pinMode(2,INPUT ); //
// pinMode(4,INPUT ); //
// pinMode(12,INPUT ); //
// pinMode(13,INPUT ); //
// pinMode(14,INPUT ); //
// pinMode(15,INPUT ); //
}
void get_plus_dir_num(){ //读取目录序号 +1 保存
//读取eeprom中的 文件夹序号
if(rtc.getHour()==0&&rtc.getMinute()==0&&(rtc.getSecond()<5)){ //第一次通电
Serial.println("first power on,dir_num++");
all_eeprom_info.dir_num+=1;
dir_num_change_flag=1; //已加1的 标记
// EEPROM_Write(0,(u8 *)&all_eeprom_info,sizeof(all_eeprom_info));
}
Serial.print("now the dir_num is ");
Serial.println(all_eeprom_info.dir_num);
}
void check_dir_num(){ //检查是否存在 jpg_1 文件夹 !!需要挂载sd卡先!
// fs::FS &fs = SD_MMC; fs.open ...
//判断文件夹
File root = SD_MMC.open("/jpg_1");
if(!root){ //不存在
Serial.println("Failed to open directory"); //打开失败/文件不存在
all_eeprom_info.dir_num=1;
EEPROM_Write(0,(u8 *)&all_eeprom_info,sizeof(all_eeprom_info));
Serial.print("reset dir_num =1");
dir_num_change_flag=0; //已存储处理
}
else{ //存在 则保存(开机已加1)
EEPROM_Write(0,(u8 *)&all_eeprom_info,sizeof(all_eeprom_info));
dir_num_change_flag=0; //已存储处理
}
}
//存储图片到SD卡中
void save_pic_sd(camera_fb_t *fb){ //存储图片到根目录
path = "/2022+"+String(millis())+".jpg";
Serial.print("save pic path:"); Serial.println(path);
check_dir_num();
if (fb == NULL)
{
Serial.println( "get picture failed"); //代表获取图片失败
} else {
File file = SD_MMC.open(path, FILE_WRITE);
if (!file)
{
Serial.println("file creat failed");
}
else
{
file.write(fb->buf , fb->len); //payload , lengte vd payload
Serial.println("write ok");
}
}
}
void save_pic_sd_temp(camera_fb_t *fb){ //尝试创建+将图片指针存到./temp/下
// fs::FS &fs = SD_MMC;
check_dir_num();
if(SD_MMC.mkdir("/jpg_"+String(all_eeprom_info.dir_num))){ //尝试创建文件夹
Serial.println("Dir created"); //创建成功/已存在
} else {
Serial.println("mkdir failed"); //失败
// if(SD_MMC.rename("/temp","/OK")) Serial.println("rename dir ok");
// else Serial.println("rename Dir failed");
}
//path = "/jpg_"+String(all_eeprom_info.dir_num)+"/2023+"+String(random(0,99))+"_"+String(millis())+".jpg";
path = "/jpg_"+String(all_eeprom_info.dir_num)+"/2023_"+String(rtc.getHour())+"-"+String(rtc.getMinute())+"-"+String(rtc.getSecond())+".jpg";
Serial.print("save pic path:"); Serial.println(path);
if (fb == NULL) //在外边已判断?
{
Serial.println( "get picture failed"); //代表获取图片失败
}
else {
File file = SD_MMC.open(path, FILE_WRITE);
if (!file)
{
Serial.println("file creat failed");
}
else
{
file.write(fb->buf , fb->len); //payload , lengte vd payload
Serial.println("write ok");
}
}
}
//tft显示屏
void tft_init(){
pinMode(TFT_RST,INPUT ); // tft的复位引脚 也连着BLK背光控制引脚
//tft.init(); //之前用这个
tft.initDMA(); //初始化对应DMA!! 需要在begin前,不然有时得执行2次
tft.begin();
tft.setTextColor(0xFFFF, 0x0000); //白字 黑底
tft.fillScreen(TFT_BLACK); //屏幕全黑
tft.setRotation(3); //显示方向 13横向 3为wifi天线与tft排针同向
tft.setTextSize(1); //设置字体大小 小
// tft.begin();
// pinMode(2,INPUT ); //
// pinMode(4,INPUT ); //
// pinMode(12,INPUT ); //
// pinMode(13,INPUT ); //
// pinMode(14,INPUT ); //
// pinMode(15,INPUT ); //
// tft.initDMA(); //初始化对应DMA
// pinMode(15,OUTPUT ); //
// digitalWrite(15, 0);
//
// pinMode(12,OUTPUT ); //
// digitalWrite(12, 1);
}
void tft_deinit(){ //去初始化
tft.deInitDMA(); //初始化对应DMA
// pinMode(2,INPUT ); //
// pinMode(12,INPUT ); //
// pinMode(13,INPUT ); //
// pinMode(14,INPUT ); //
// pinMode(15,INPUT ); //
}
void tft_off(){ //去初始化 关闭tft屏及背光,SD卡使用时无效
tft_deinit();
delay(50); //原100
pinMode(TFT_RST,OUTPUT ); // tft的复位引脚 也连着BLK背光控制引脚
digitalWrite(TFT_RST, 0); //低电平为关闭
}
//求平均 原先的数值,此时数值,多少个数平均
float average1(float aver_old,float now_value,int num){
aver_old = (aver_old*(num-1)+now_value)/num;
return aver_old;
}
//adc读取电压,更新电池电压+判断按键 不会重复触发,按下只返回1次 【以及模式切换:跳转对应的初始化函数中】
char adc_power_key(char show_v_tft=1){ //1-4为对应按键短按,5-8为按键长按, 0是可更新电源电压,//-1是在判断是否长按短按 默认在tft显示电压出来
static int adc_flag_num=0; //计算adc判断 非0连续次数,用于判断按键长按 短按逻辑
static char adc_key_num=0; //上次的按键数 用于长按 短按 的判断逻辑
adc_num = analogRead(adc_io);
if(adc_num>2200){ //是电池电压的
power_v = average1(power_v,adc_num * adc_power_change ,5);
Serial.println(" "+ String(power_v-power_adjust) + 'V');
if(show_v_tft==1){
tft.drawString("V:", 0,0);
tft.drawFloat(power_v-power_adjust, 3, 16,0);
}
//tft.drawString("V ", 82,0);
if(adc_flag_num==0) return 0; //无按键按下 此时为检测电池电压
else{ //有按键按下
//tft_work_millis = millis();
if(digitalRead(TFT_RST)==0){ //说明tft关了
digitalWrite(TFT_RST, 1); //通电
delay(5); //10ok
tft.initDMA(); //初始化对应DMA
tft.begin();
tft.setTextColor(0xFFFF, 0x0000); //白字 黑底
//tft.fillScreen(TFT_BLACK); //屏幕全黑
tft.setRotation(3); //显示方向 13横向 3为wifi天线与tft排针同向
tft.setTextSize(1); //设置字体大小 小
}
if(adc_flag_num>adc_Lpress_num){ //长按
adc_flag_num=0;
return adc_key_num+4;
}
else if(adc_flag_num<=adc_flag0_upnum){ //短按
adc_flag_num=0;
return adc_key_num;
}
else adc_flag_num=0; //在这之间,无响应
}
}
else if(adc_num<200){ //按键4检测 拍照/提示界面
adc_key_num = 4;
adc_flag_num++;
}
else if(adc_num<750){ //按键3 设置/切换模式
adc_key_num = 3;
adc_flag_num++;
if(adc_flag_num>=adc_Lpress_num){ //第一次判断为长按
all_eeprom_info.esp32cam_mode1 = 0; //默认从0开始
while(adc_num<2200){ //若继续按着,就死循环
tft.fillScreen(TFT_BLACK); //背景黑色
tft.setTextSize(2); //设置字体大小
tft.setTextColor(0xFFFF, 0x0000); //白字 黑底
tft.drawString("1.TFT CAM", 16,32);
tft.drawString("2.WIFI CAM", 16,48);
tft.drawString("3.IOT CAM", 16,64);
tft.drawString("4.TFT Weather", 16,80);
tft.setTextSize(1); //设置字体大小
//all_eeprom_info.esp32cam_mode1=(all_eeprom_info.esp32cam_mode1+1)%4;
tft.fillCircle(8,16*all_eeprom_info.esp32cam_mode1+40,8,TFT_BLUE); //画圆
for(int i=0;i<10;i++){
if(analogRead(adc_io)>=2200) break;
else delay(100);
}
adc_num = analogRead(adc_io);
if(adc_num<750){ //按键还按着,就 模式序号+1
all_eeprom_info.esp32cam_mode1=(all_eeprom_info.esp32cam_mode1+1)%4;
}
else{ //不按了,按 all_eeprom_info.esp32cam_mode1 跳转对应的功能模块
delay(500);
LED1_bright=0;
//切换跳转
switch(all_eeprom_info.esp32cam_mode1){ //界面:1、tft摄像机,2、局域网摄像头 3、物联网摄像头 4、桌面天气 5、手机来蓝牙拍照遥控 6、手机蓝牙丢失提醒
case 0: TFT_cam_init(); break; //tft摄像机
case 1: WIFI_cam_init(); break;
case 2: break;
case 3: break;
case 4: break;
case 5: break;
default: break;
}
adc_flag_num=0;
return adc_key_num+4;
}
}
}
}
else if(adc_num<1350){ //按键2
adc_key_num = 2;
adc_flag_num++;
}
else{ //按键1
adc_key_num = 1;
adc_flag_num++;
}
return -1; //按键按下,还在判断长按还是短按的时候
}
//是否存? 保存照片到sd卡后 重启设备
void save_restart(camera_fb_t *fb){
tft.drawString("quit", 82,0); //位置已校准
tft.drawString("save+rst ", 110,0);
delay(100);
while(1) {
adc_flag = adc_power_key();
delay(30); //原80
if(adc_flag==3){ //退出不保存
tft.drawString(" ", 82,0); //清空显示
return;
}
if(adc_flag==4){ //保存重启
//ok的
tft_deinit();
sd_init(); //SD卡初始化
save_pic_sd_temp(fb);
// save_pic_sd(fb);
sd_deinit();
delay(50); //原100
ESP.restart(); // 板子重启 这里复位前后图片时间间隔3s左右 好像可以,循环复位使用正常
}
}
}
void span_pic_save(){ //连续摄影 不显示在tft,//拍照存储 不重启
// tft_deinit();
// sd_init(); //SD卡初始化
save_pic_sd_temp(fb);
// save_pic_sd(fb);
sd_deinit();
delay(50); //原100
ESP.restart(); // 板子重启
}
//考虑比较周全的 启动/关闭
bool wifi_init(){
//连接wifi
if(WiFi.status() != WL_CONNECTED){ //
Serial.println("\r\nConnecting to: " + String(ssid));
WiFi.begin(ssid, password);
for (int i = 0; i < 30; i++) { //尝试连接15s
if (WiFi.status() != WL_CONNECTED ) {
delay(500); Serial.print(".");
}
else break;
}
if (WiFi.status() != WL_CONNECTED ) {
Serial.println("Connect wifi failed!");
wifi_off();
return false;
}
Serial.println("");
Serial.println("WiFi connected");
}
//Serial.print("Camera Stream Ready! Go to: http://");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
return true;
}
void wifi_off(){
WiFi.disconnect(true); //断开且关闭STA网络
WiFi.mode( WIFI_MODE_NULL ); //关闭wifi
}
bool camera_reinit(char frame_size_num = 3 , char jpeg_quality_num = 12){ //重新给电 初始化
//确保cam断开
esp_camera_deinit(); //摄像头去初始化 防止没断电导致cam初始化失败
pinMode(32,OUTPUT ); // 32是 cam电源控制引脚
digitalWrite(32, 1); // 高电平 断电 cam的
delay(20);
pinMode(32,INPUT ); //释放 引脚
if(camera_init(frame_size_num,jpeg_quality_num)==true){
Serial.println("camera init ok!");
return true;
}
else{
Serial.println("camera init error!");
return false;
}
}
//数据流传输的执行 句柄
static esp_err_t stream_handler(httpd_req_t *req){
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
size_t _jpg_buf_len = 0;
uint8_t * _jpg_buf = NULL;
char * part_buf[64];
httpd_resp_set_hdr(req,"Access-Control-Allow-Origin","*");
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
if(res != ESP_OK){ return res; }
while(true){
if(reinit_flag1>0){ //需要重新初始化 摄像头
esp_camera_fb_return(fb);
fb = NULL;
free(_jpg_buf);
_jpg_buf = NULL;
Serial.println("Out stream_handler");
break;
}
fb = esp_camera_fb_get(); //拍照
if (!fb) { //拍照失败
delay(200);
Serial.println("Camera capture failed");
res = ESP_FAIL;
}
else { //拍照成功
/* if(fb->width > 400){ //jpg压缩?
if(fb->format != PIXFORMAT_JPEG){ //不是jpg 默认从相机拍摄是jpg的
Serial.println("need JPEG compression ");
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len); //转为jpg
esp_camera_fb_return(fb); //回收指针
fb = NULL;
if(!jpeg_converted){ //压缩失败
Serial.println("JPEG compression failed");
res = ESP_FAIL;
}
} else
{ //是jpg就直接指向
_jpg_buf_len = fb->len;
_jpg_buf = fb->buf;
}
} */
_jpg_buf_len = fb->len;
_jpg_buf = fb->buf;
//传输数据流 相关发送
if(res == ESP_OK){
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
if(fb){ //还没释放,就释放
esp_camera_fb_return(fb);
fb = NULL;
_jpg_buf = NULL;
} else if(_jpg_buf){ //释放
free(_jpg_buf);
_jpg_buf = NULL;
}
if(res != ESP_OK){ //异常就退出,但依旧能传图的! 不可注释
break; //有失败的,就跳出,视频流还会继续传
}
//Serial.printf("MJPG: %uB\n",(uint32_t)(_jpg_buf_len));
}
}
return res;
}
//开启视频数据流服务器 ip/stream:81
void startCameraServer(){
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 81;
httpd_uri_t index_uri = {
.uri = "/stream",
.method = HTTP_GET,
.handler = stream_handler,
.user_ctx = NULL
};
//Serial.printf("Starting web server on port: '%d'\n", config.server_port);
if (httpd_start(&stream_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(stream_httpd, &index_uri);
}
}
void led_pwm_init(){ //led 的 pwm初始化
//大白灯
pinMode(LED1,INPUT ); // 解除
ledcDetachPin(LED1);
delay(10);
//ledcSetup(LED1_channel, 5000, 8);//通道数 频率 位数 占空比数值(0-255)
ledcAttachPin(LED1, LED1_channel); //与通道绑定
ledcWrite(LED1_channel, LED1_bright); //调节对应通道的亮度
}
//各个模式下的初始化 以及关闭耗电的硬件
void TFT_cam_init(){
wifi_off();
led_pwm_init(); //初始化led
//创建在处理器0上运行的任务core0_decode_jpg来解码Jpeg 避免多个同任务
if(core0_work_flag==0) xTaskCreatePinnedToCore(core0_decode_jpg, "core0_decode_jpg", 10000, NULL, 0, NULL, 0);
core0_work_flag=1;
tft_init(); //初始化tft
//jpg解码库设置
TJpgDec.setJpgScale(2);// 设置缩放 1, 2, 4, or 8
TJpgDec.setSwapBytes(true); // The byte order can be swapped (set true for TFT_eSPI)
TJpgDec.setCallback(core0_mcu_decoded);//每个mcu解码后 会执行的函数 回调函数
camera_reinit(all_eeprom_info.pic_size,all_eeprom_info.pic_quality);
//是否考虑 初始化失败 的操作
}
void WIFI_cam_init(){ //目前要求了图片大小 质量?
//led_pwm_init(); //初始化led 放前面试试
tft_init();
tft.drawString("connect to ", 0,32);
tft.drawString( ssid, 68,32);
wifi_init();
tft.drawString("wifi connected :", 0,48);
tft.drawString(WiFi.localIP().toString().c_str(), 0,64);
tft.drawString("need computer page", 0,80);
core0_work_flag=0; //关闭core0
Serial.print("Camera Stream Ready! Go to: http://");
Serial.print(WiFi.localIP());
if(camera_reinit(all_eeprom_info.pic_size,all_eeprom_info.pic_quality)==true) tft.drawString("cam init ok", 0,96);
else tft.drawString("cam init error!!!", 0,96);
//启动流式web服务器
startCameraServer();
server.begin();
delay(4500); //显示
tft_off();
led_pwm_init(); //初始化led 只能放最后,不然led亮度控制异常
}
void IOT_cam_init(){
}
void TFT_weather_init(){
}
//各个模式下的设置界面
void TFT_cam_set(){ //1设置界面逻辑
char set_cursor_num=1; //1表第一个设置项目
char need_flash_flag=0; //1需要刷新标志
//设置临时参数变量
char reinit_flag=0; //是否需要重新初始化 1当前重新初始化摄像头
char frame_size_num2=all_eeprom_info.pic_size; //有关设置图片大小的 0-8 越大越大
char jpeg_quality_num2=all_eeprom_info.pic_quality; //有关图片 质量 越小越好 10跟20好像肉眼差不多
int time_span2=all_eeprom_info.time_span1; //用于延时摄影间隔 s
char save_flash_flag=0; //保存到flash的标志 ,默认0 设为1则退出保存时保存到flash
#define menu_num 4 //4个设置行
#define TFT_cam_drawString1 tft.drawNumber(frame_size_num2, 120,48); //图片大小
#define TFT_cam_drawString2 {tft.drawString(" ", 126,64); tft.drawNumber(jpeg_quality_num2, 120,64);} //图片质量
#define TFT_cam_drawString3 {tft.drawString(" ", 126,80); tft.drawNumber(time_span2, 120,80); } //时间间隔
#define TFT_cam_drawString4 tft.drawNumber(save_flash_flag, 120,96);
tft.setTextColor(RGBto565(176,224,230), 0x0000); //浅蓝色 黑底
tft.drawString("save", 90,0); //位置已校准
tft.drawString("ok/quit", 118,0); //确认保存 退出的那一刻更新配置?
tft.drawString("+", 118,120);
tft.drawString("-", 148,120);
// tft.drawString("+.u", 112,120);
// tft.drawString("-.d", 142,120);
tft.setTextSize(2); //设置字体大小 小
tft.setTextColor(RGBto565(255,0,0), 0x0000); // 红色 黑底
tft.drawString("[1.Setting]", 0,16);
tft.setTextSize(1); //设置字体大小 小
tft.setTextColor(0xFFFF, 0x0000); //白字 黑底
tft.drawString("1.pic_size(0-8L)", 0,48); //图片大小
tft.drawString("2.pic_qualityL-64", 0,64); //图片质量
tft.drawString("3.time_span0-255", 0,80); //时间间隔
tft.drawString("4.save_flash?", 0,96); //是否存储一次eeprom
need_flash_flag=1; //刷新一下
while(1){ //设置死循环 按3退出 需要判断是否修改了设置
delay(80);
if(need_flash_flag==1){ //刷新显示
need_flash_flag=0;
tft.setTextColor(0xFFFF, 0x0000); //白字 黑底
TFT_cam_drawString1;
TFT_cam_drawString2;
TFT_cam_drawString3;
TFT_cam_drawString4;
tft.setTextColor(RGBto565(160,32,240), 0x0000); //紫色 黑底
switch(set_cursor_num){ //选中哪个 变颜色
case 1: TFT_cam_drawString1; break;
case 2: TFT_cam_drawString2; break;
case 3: TFT_cam_drawString3; break;
case 4: TFT_cam_drawString4; break;
default: break;
}
}
adc_flag = adc_power_key(); //
if(adc_flag==3){ //保存退出
break; //短按3 保存退出 函数内判断重新初始化
}
if(adc_flag==7) break; //长按3 是原有的切换功能 切换后让其退出设置界面 不保存退出
if(adc_flag==8) break; //长按4 不保存退出
switch(adc_flag){ //
//case -1: break;
case 0: break;//Serial.println(""); //电池电压获取成功
case 1: tft.drawString("key1S", 50,0);
switch(set_cursor_num){
case 1: if(frame_size_num2<8) frame_size_num2++; break;
case 2: if(jpeg_quality_num2<64) jpeg_quality_num2++; break;
case 3: time_span2+=(time_span2/10+1);if(time_span2>255) time_span2=255; break;
case 4: save_flash_flag=1-save_flash_flag; break; //取反
}
need_flash_flag=1; //刷新显示
break; //按键1短按 +
case 2: tft.drawString("key2S", 50,0);
switch(set_cursor_num){
case 1: if(frame_size_num2>0) frame_size_num2--; break;
case 2: if(jpeg_quality_num2>0) jpeg_quality_num2--; break;
case 3: time_span2-=(time_span2/10+1);if(time_span2<0) time_span2=0; break;
case 4: save_flash_flag=1-save_flash_flag; break; //取反
}
need_flash_flag=1; //刷新显示
break; //按键2短按 -
// case 3: tft.drawString("key3S", 50,0); break; //按键3短按
case 4: tft.drawString("key4S", 50,0);
if(set_cursor_num<menu_num) set_cursor_num++;
else set_cursor_num=1;
need_flash_flag=1; //刷新显示
break; //按键4短按 确认
// case 5: tft.drawString("key1L", 50,0); break; //按键1长按
// case 6: tft.drawString("key2L", 50,0); break; //按键2长按
// case 7: tft.drawString("key3L", 50,0); break; //按键3长按
// case 8: tft.drawString("key4L", 50,0); break; //按键4长按
default: break;
}
}
if(adc_flag==3){ //最后是 短按3 保存退出 函数内判断重新初始化
if(all_eeprom_info.pic_size!=frame_size_num2) reinit_flag=1;
if(all_eeprom_info.pic_quality!=jpeg_quality_num2) reinit_flag=1;
if(all_eeprom_info.time_span1!=time_span2){ //修改延时摄影的
all_eeprom_info.time_span1=time_span2;
if(all_eeprom_info.time_span1!=0){ //改变了 但需要延时摄影的
tft.fillScreen(TFT_BLACK); //屏幕全黑
tft.setTextSize(2); //设置字体大小
tft.drawString("span pic", 0,60);
tft.drawString("will start", 0,76);
tft.setTextSize(1); //设置字体大小
delay(3000);
tft_deinit();
sd_init(); //SD卡初始化
}
}
if(save_flash_flag==1){ //需要保存到flash 没检查是否不同
all_eeprom_info.pic_quality=jpeg_quality_num2;
all_eeprom_info.pic_size=frame_size_num2;
all_eeprom_info.time_span1=time_span2;
// all_eeprom_info.time_span1=0;//不能保存,不然开机没法取消
// EEPROM.write(0, all_eeprom_info.esp32cam_mode1);
// EEPROM.write(1, all_eeprom_info.time_span1);
eeprom_info_check(); //检查参数
EEPROM_Write(0,(u8 *)&all_eeprom_info,sizeof(all_eeprom_info));
//EEPROM.commit(); //保存更改的数据 上面已包含
}
if(reinit_flag==1){ //重新初始化 摄像头相关的
all_eeprom_info.pic_size=frame_size_num2;
all_eeprom_info.pic_quality=jpeg_quality_num2;
camera_reinit(all_eeprom_info.pic_size,all_eeprom_info.pic_quality); //调节缩放的?
// camera_reinit(frame_size_num2,jpeg_quality_num2);
}
}
adc_flag=0;
}
void WIFI_cam_set(){ //2设置界面逻辑 从1移植,待改
char set_cursor_num=1; //1表第一个设置项目
char need_flash_flag=0; //1需要刷新标志
//设置临时参数变量
char reinit_flag=0; //是否需要重新初始化 当修改了设置并保存时检测
char frame_size_num2=all_eeprom_info.pic_size; //有关设置图片大小的 0-8 越大越大
char jpeg_quality_num2=all_eeprom_info.pic_quality; //有关图片 质量 越小越好 10跟20好像肉眼差不多
//int time_span2=all_eeprom_info.time_span1; //用于演示摄影间隔 s
char save_flash_flag2=0; //保存到flash的标志 ,默认0 设为1则退出保存时保存到flash
#define menu_num 4 //4个设置行
#define TFT_cam_drawString1 tft.drawNumber(frame_size_num2, 120,48); //图片大小
#define TFT_cam_drawString2 {tft.drawString(" ", 126,64); tft.drawNumber(jpeg_quality_num2, 120,64);} //图片质量
#define TFT_cam_drawString3 {tft.drawString(" ", 126,80); tft.drawNumber(save_flash_flag2, 120,80); } //时间间隔
#define TFT_cam_drawString4 tft.drawString("xx", 120,96);
tft.fillScreen(TFT_BLACK); //屏幕全黑
tft.setTextColor(RGBto565(176,224,230), 0x0000); //浅蓝色 黑底
tft.drawString("save", 90,0); //位置已校准
tft.drawString("ok/quit", 118,0); //确认保存 退出的那一刻更新配置?
tft.drawString("+", 118,120);
tft.drawString("-", 148,120);
// tft.drawString("+.u", 112,120);
// tft.drawString("-.d", 142,120);
tft.setTextSize(2); //设置字体大小 小
tft.setTextColor(RGBto565(255,0,0), 0x0000); // 红色 黑底
tft.drawString("[2.Setting]", 0,16);
tft.setTextSize(1); //设置字体大小 小
tft.setTextColor(0xFFFF, 0x0000); //白字 黑底
tft.drawString("1.pic_size(0-8L)", 0,48); //图片大小
tft.drawString("2.pic_qualityL-64", 0,64); //图片质量
tft.drawString("3.save_flash?", 0,80); //时间间隔
tft.drawString("4.xxxxx", 0,96);
need_flash_flag=1; //刷新一下
while(1){ //设置死循环 按3退出 需要判断是否修改了设置
delay(80);
if(need_flash_flag==1){ //刷新显示
need_flash_flag=0;
tft.setTextColor(0xFFFF, 0x0000); //白字 黑底
TFT_cam_drawString1;
TFT_cam_drawString2;
TFT_cam_drawString3;
TFT_cam_drawString4;
tft.setTextColor(RGBto565(160,32,240), 0x0000); //紫色 黑底
switch(set_cursor_num){ //选中哪个 变颜色
case 1: TFT_cam_drawString1; break;
case 2: TFT_cam_drawString2; break;
case 3: TFT_cam_drawString3; break;
case 4: TFT_cam_drawString4; break;
default: break;
}
}
adc_flag = adc_power_key(); //
if(adc_flag==3){
break; //短按3 保存退出 函数内判断重新初始化
}
if(adc_flag==7) break; //长按3 是原有的切换功能 切换后让其退出设置界面 不保存退出
if(adc_flag==8) break; //长按4 不保存退出
switch(adc_flag){ //
//case -1: break;
case 0: break;//Serial.println(""); //电池电压获取成功
case 1: tft.drawString("key1S", 50,0);
switch(set_cursor_num){
case 1: if(frame_size_num2<8) frame_size_num2++; break;
case 2: if(jpeg_quality_num2<64) jpeg_quality_num2++; break;
case 3: save_flash_flag2=1-save_flash_flag2; break;
}
need_flash_flag=1; //刷新显示
break; //按键1短按 +
case 2: tft.drawString("key2S", 50,0);
switch(set_cursor_num){
case 1: if(frame_size_num2>0) frame_size_num2--; break;
case 2: if(jpeg_quality_num2>0) jpeg_quality_num2--; break;
case 3: save_flash_flag2=1-save_flash_flag2; break;
}
need_flash_flag=1; //刷新显示
break; //按键2短按 -
// case 3: tft.drawString("key3S", 50,0); break; //按键3短按
case 4: tft.drawString("key4S", 50,0);
if(set_cursor_num<menu_num) set_cursor_num++;
else set_cursor_num=1;
need_flash_flag=1; //刷新显示
break; //按键4短按 确认
// case 5: tft.drawString("key1L", 50,0); break; //按键1长按
// case 6: tft.drawString("key2L", 50,0); break; //按键2长按
// case 7: tft.drawString("key3L", 50,0); break; //按键3长按
// case 8: tft.drawString("key4L", 50,0); break; //按键4长按
default: break;
}
}
if(adc_flag==3){ //最后是 短按3 保存退出 函数内判断重新初始化
if(all_eeprom_info.pic_size!=frame_size_num2) reinit_flag=1;
if(all_eeprom_info.pic_quality!=jpeg_quality_num2) reinit_flag=1;
if(save_flash_flag2==1){ //需要保存到flash 没检查是否不同
all_eeprom_info.pic_quality=jpeg_quality_num2;
all_eeprom_info.pic_size=frame_size_num2;
eeprom_info_check(); //检查参数
EEPROM_Write(0,(u8 *)&all_eeprom_info,sizeof(all_eeprom_info));
}
if(reinit_flag==1){ //重新初始化 摄像头相关的
reinit_flag1=1; delay(400); //让core1运行监测到 停止
reinit_flag1=0;
all_eeprom_info.pic_size=frame_size_num2;
all_eeprom_info.pic_quality=jpeg_quality_num2;
camera_reinit(all_eeprom_info.pic_size,all_eeprom_info.pic_quality); //调节缩放的?
startCameraServer(); //重新启动服务
server.begin();
// led_pwm_init(); //初始化led 不能在这
}
}
tft.setTextColor(0xFFFF, 0x0000); //白字 黑底
tft.fillScreen(TFT_BLACK); //屏幕全黑
tft.setTextSize(1); //设置字体大小 小
tft.drawString("connect to ", 0,32);
tft.drawString( ssid, 68,32);
tft.drawString("wifi connected :", 0,48);
tft.drawString(WiFi.localIP().toString().c_str(), 0,64);
tft.drawString("need computer page", 0,80);
}
void setup()
{
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //禁用掉电检测器
EEPROM.begin(1024); //申请操作到地址4095(比如你只需要读写地址为100上的一个字节,该处也需输入参数101)
//读取eeprom中数据 + 检查各项数据范围
EEPROM_Read(0,(u8 *)&all_eeprom_info,sizeof(all_eeprom_info));
if(all_eeprom_info.esp32cam_mode1>20){ //认为是第一次,全改为默认值
eeprom_info_check(1); //设为默认值
// EEPROM.commit(); //保存更改的数据
}
else{
eeprom_info_check(); //检查参数
// EEPROM.commit(); //保存更改的数据
}
Serial.begin(115200);
Serial.println("\n\n 【项目】: esp32 cam tft 摄像机");
// btStop(); //关闭 蓝牙 好像开机默认是关闭的
//rtc.setTime(30, 0, 10, 2, 1, 2023); //设置时间 2th Jan 2023 10:0:30 默认1970 1/1 00:00:00
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); // (tm struct) Sunday, January 17 2021 07:24:38
get_plus_dir_num(); //有关存储照片的文件夹编号的
ledcSetup(LED1_channel, 5000, 8);//通道数 频率 位数 占空比数值(0-255)
//默认 功能0
TFT_cam_init();all_eeprom_info.esp32cam_mode1=0;
// WIFI_cam_init();all_eeprom_info.esp32cam_mode1=1;
}
void loop()
{
if(main_count>60000) main_count=0;
else main_count++;
if(main_count%10==0){
Serial.println(rtc.getTime()); // (String) 15:24:38
}
adc_flag = adc_power_key(); //有关电池电压/按键的获取
// Serial.println(adc_num); //
/* switch(adc_flag){ //这行也应放在不同功能内
// //case -1: break;
// case 0: break;//Serial.println(""); //电池电压获取成功
// case 1: Serial.println("key1 Short press"); tft.drawString("key1 S", 60,0); core0_work_flag=0; break; //按键1短按
// case 2: Serial.println("key2 Short press"); tft.drawString("key2 S", 60,0); pinMode(32,OUTPUT );digitalWrite(32, 1); break; //按键2短按
// case 3: Serial.println("key3 Short press"); tft.drawString("key3 S", 60,0); break; //按键3短按
// case 4: Serial.println("key4 Short press"); tft.drawString("key4 S", 60,0); save_restart(fb); break; //按键4短按
// case 5: Serial.println("key1 Long press"); tft.drawString("key1 L", 60,0); break; //按键1长按
// case 6: Serial.println("key2 Long press"); tft.drawString("key2 L", 60,0);pinMode(32,OUTPUT );digitalWrite(32, 0);pinMode(32,INPUT );break; break; //按键2长按
// case 7: Serial.println("key3 Long press"); tft.drawString("key3 L", 60,0); break; //按键3长按
// case 8: Serial.println("key4 Long press"); tft.drawString("key4 L", 60,0); tft_off(); break; //按键4长按
// default: break;
// }
*/
//各个功能的loop
if(all_eeprom_info.esp32cam_mode1==0){ //1、tft摄像机-------------------------------------
//tft.drawString("V:", 0,0);
//tft.drawFloat(power_v-power_adjust, 3, 16,0);
switch(adc_flag){ //这行也应放在不同功能内
//case -1: break;
case 0: break;//Serial.println(""); //电池电压获取成功
case 1: Serial.println("key1 Short press"); tft.drawString("key1S", 50,0);
LED1_bright+=(LED1_bright/10+1); //阶梯增加亮度
if(LED1_bright>255) LED1_bright=255;
ledcWrite(LED1_channel, LED1_bright); //设置亮度
tft.drawString(" ", 96,0);
tft.drawNumber(LED1_bright, 90,0);
Serial.print("LED1_bright :"); Serial.println(LED1_bright);
Serial.print("zankongbi :"); Serial.println(ledcRead(LED1_channel));
break; //按键1短按
case 2: Serial.println("key2 Short press"); tft.drawString("key2S", 50,0);
LED1_bright-=(LED1_bright/10+1); //阶梯降低亮度
if(LED1_bright<0) LED1_bright=0;
ledcWrite(LED1_channel, LED1_bright); //设置亮度
tft.drawString(" ", 96,0);
tft.drawNumber(LED1_bright, 90,0);
Serial.print("LED1_bright :"); Serial.println(LED1_bright);
Serial.print("zankongbi :"); Serial.println(ledcRead(LED1_channel));
break; //按键2短按
case 3: Serial.println("key3 Short press"); tft.drawString("key3S", 50,0); TFT_cam_set(); tft.drawString(" ", 82,0); break; //按键3短按 设置
case 4: Serial.println("key4 Short press"); tft.drawString("key4S", 50,0); save_restart(fb); break; //按键4短按 拍照
case 5: Serial.println("key1 Long press"); tft.drawString("key1L", 50,0);
LED1_bright+=50; //阶梯增加亮度
if(LED1_bright>255) LED1_bright=255;
ledcWrite(LED1_channel, LED1_bright); //设置亮度
tft.drawString(" ", 96,0);
tft.drawNumber(LED1_bright, 90,0);
break; //按键1长按
case 6: Serial.println("key2 Long press"); tft.drawString("key2L", 50,0);
LED1_bright-=50; //阶梯降低亮度
if(LED1_bright<0) LED1_bright=0;
ledcWrite(LED1_channel, LED1_bright); //设置亮度
tft.drawString(" ", 96,0);
tft.drawNumber(LED1_bright, 90,0);
break; //按键2长按
case 7: Serial.println("key3 Long press"); tft.drawString("key3L", 50,0); break; //按键3长按
case 8: Serial.println("key4 Long press"); tft.drawString("key4L", 50,0);
tft.setTextColor(RGBto565(0,255,0), 0x0000); //浅蓝色 黑底
tft.drawString("set/fun", 52,0);
tft.drawString("photo/note", 100,0);
tft.drawString("led+/++", 90,120);
tft.drawString("-/--", 136,120);
tft.drawString(rtc.getTime(), 30,60);
tft.setTextColor(0xffff, 0x0000); // 黑底
delay(3600); //停留s
tft.fillScreen(TFT_BLACK); //屏幕全黑
break; //按键4长按 提示界面
default: break;
}
if(fb != NULL) esp_camera_fb_return(fb); //应该是销毁图片指针 需要放这,否则可能导致拍照存储失败!
fb = esp_camera_fb_get(); //拍照 返回照片指针
if (!fb) { //拍照失败
Serial.println("Camera capture failed!");
}
else{ //拍照成功
if(all_eeprom_info.time_span1!=0){ //开启了延时摄影功能
if(millis()>13000){
if(adc_flag>0){ //任意按键按下了,则重启
if(all_eeprom_info.time_span1!=0){ //若处于延时摄影中,则任意按键按下取消并重启
if(millis()>15000){
all_eeprom_info.time_span1=0;
sd_deinit();
delay(50); //原100
ESP.restart(); // 板子重启
}
}
}
if(millis()-span_pic_millis>=all_eeprom_info.time_span1*1000){ //周期连续拍照
span_pic_millis=millis();
Serial.println("span pic: time to pic");
tft_deinit();
sd_init(); //SD卡初始化
save_pic_sd_temp(fb);
}
}
else{
show_JPEG_160x120DMA(8);//固定偏移 解码+显示
tft.setTextSize(2); //设置字体大小
tft.drawString("span pic", 0,60);
tft.drawString("will start", 0,76);
tft.setTextSize(1); //设置字体大小
}
}
else //解码+显示到tft
{ //在这的话,大约 40-73ms 复杂图70ms左右
//tft_init();
//tft.setRotation(3); //显示方向 横向
//tft.fillScreen(TFT_BLACK);
show_JPEG_160x120DMA(8); //固定偏移 解码+显示
Serial.print("###");
}
}
}
else if(all_eeprom_info.esp32cam_mode1==1){ //2、局域网摄像头-----------------------------------
// if(millis()-tft_work_millis>tft_light_times*1000){ //无操作 延时熄屏
// tft_off();
// }
switch(adc_flag){ //按键
//case -1: break;
case 0: break;//Serial.println(""); //电池电压获取成功
case 1: tft.drawString("key1S", 50,0);
LED1_bright+=(LED1_bright/6+1); //阶梯增加亮度
if(LED1_bright>255) LED1_bright=255;
if(LED1_bright<0) LED1_bright=0;
ledcWrite(LED1_channel, LED1_bright); //设置亮度
tft.drawString(" ", 96,0);
tft.drawNumber(LED1_bright, 90,0);
break; //按键1短按
case 2: tft.drawString("key2S", 50,0);
LED1_bright-=(LED1_bright/6+1); //阶梯降低亮度
if(LED1_bright<0) LED1_bright=0;
ledcWrite(LED1_channel, LED1_bright); //设置亮度
tft.drawString(" ", 96,0);
tft.drawNumber(LED1_bright, 90,0);
break; //按键2短按
case 3: WIFI_cam_set(); break; //按键3短按 设置
case 4: break; //按键4短按
case 5: break; //按键1长按
case 6: tft_off(); break; //按键2长按 熄屏
case 7: break; //按键3长按
case 8:
tft.setTextColor(RGBto565(0,255,0), 0x0000); //浅蓝色 黑底
tft.drawString("set/fun", 80,0);
tft.drawString("/note", 130,0);
tft.drawString("led+/", 70,120);
tft.drawString("-/tft_off", 104,120);
tft.setTextColor(0xffff, 0x0000); // 黑底
delay(3600); //停留s
tft.fillScreen(TFT_BLACK); //屏幕全黑
break; //按键4长按 提示界面
break; //按键4长按 提示界面
default: break;
}
WiFiClient client = server.available(); //监听客户端接入 80端口
if (client) { //如果新客户端连接
currentTime = millis(); previousTime = currentTime; //获取此时millis
Serial.println("New Client.");
String currentLine = ""; //创建一个字符串来保存从客户端传入的数据
while (client.connected() && currentTime - previousTime <= server_timeoutTime) { //有连接/断开但未超时
currentTime = millis(); //最新millis
if (client.available()) { //client有字节数据接收到
char c = client.read(); //读1字节
Serial.write(c); //全打印
header += c;
if (c == '\n') { //一行数据 的换行符
if (currentLine.length() == 0) { //一个空白行,则GET请求结束,发回个响应:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println(); //空行
//解析命令-----------------------------
int place_temp = -1;
//Serial.println("[]deal data: ");
if ((place_temp = header.indexOf("order")) != -1) {
if(place_temp<200){ //有时其他表单提交后,也会触发,在400多
// Serial.println(place_temp);
String value = header.substring(header.indexOf('=', place_temp) + 1, header.indexOf(' ', place_temp));
//注意排除 空的情况
Serial.println("[]get order:" + value);
}
}
if ((place_temp = header.indexOf("pic_size")) != -1) {
if(place_temp<200){ //有时其他表单提交后,也会触发,在400多
String value = header.substring(header.indexOf('=', place_temp) + 1, header.indexOf('&', place_temp));
Serial.println("[]get pic_size:" + value);
if(all_eeprom_info.pic_size != value.toInt()){ //跟此时的不同
reinit_flag1=1;
all_eeprom_info.pic_size = value.toInt();
}
}
}
if ((place_temp = header.indexOf("pic_quality")) != -1) {
if(place_temp<200){ //有时其他表单提交后,也会触发,在400多
String value = header.substring(header.indexOf('=', place_temp) + 1, header.indexOf('&', place_temp));
Serial.println("[]get pic_quality:" + value);
if(all_eeprom_info.pic_quality != value.toInt()){
reinit_flag1=1;
all_eeprom_info.pic_quality = value.toInt();
}
}
}
if ((place_temp = header.indexOf("light")) != -1) {
if(place_temp<200){ //有时其他表单提交后,也会触发,在400多
String value = header.substring(header.indexOf('=', place_temp) + 1, header.indexOf(' ', place_temp));
Serial.println("[]get light:" + value);
LED1_bright = value.toInt()*10;
if(LED1_bright<0||LED1_bright>255) LED1_bright=0;
ledcWrite(LED1_channel, LED1_bright); //设置亮度
}
}
if(reinit_flag1>0){ //需要重新初始化 摄像头
delay(300); //让core1运行监测到 停止
reinit_flag1=0;
camera_reinit(all_eeprom_info.pic_size,all_eeprom_info.pic_quality); //调节缩放的?
startCameraServer(); //重新启动服务
server.begin();
// led_pwm_init(); //初始化led
}
// 显示HTML网页内容++++++++++++++++++不支持中文!
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// 设置开/关按钮样式的CSS
// 根据您的喜好随意更改背景色和字体大小属性
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: left;}");
client.println(".button_on { background-color: #6FCF7F; border: none; color: black; padding: 5px 20px;");
client.println("text-decoration: none; font-size: 26px; margin: 2px; cursor: pointer;}");
client.println(".button_off {background-color: #AAAAAA;}</style></head>");
//网页标题+载入81端口的图片流
client.println("<body onload=\"document.getElementById('stream').src=document.location.origin+':81/stream';\"><h1>use computer page!</h1>");
client.println("<img id='stream' src='' crossorigin='anonymous'>");
//表单1
client.println("<form action=\"index\" method=\"get\">"); //url 无所谓?
client.println("<p>order(num):<input type=\"text\" name=\"order\">");
client.println("<input type=\"submit\" value=\"Send / null to reflash\" class=\"button_on\"></p></form>");
client.print("<p>----------now power: ");
client.println(String(power_v-power_adjust));
client.println("v------------ </p>");
//表单2
client.println("<form action=\"index\" method=\"get\">");
client.print("<p>pic_size[0-8]:<input type=\"number\" name=\"pic_size\" value=");
client.print(String(all_eeprom_info.pic_size));
client.println(" max=8 min=0>");
client.print("pic_quality[1-64]:<input type=\"number\" name=\"pic_quality\" value=");
client.print(String(all_eeprom_info.pic_quality));
client.println(" max=64 min=2></p>");
client.print("<p>light[0-255]: low<input type=\"range\" name=\"light\" value=");
client.print(String(LED1_bright/10));
client.println(" max=25 min=0>high</p>");
client.println("<p><input type=\"submit\" value=\"Set\" class=\"button_on\">");
//client.println("<a href=\"/index\"><button class=\"button_on button_off\">Reflash</button></a></p></form>");
client.println("</p></form>");
client.println("</body></html>");
//HTTP响应以另一个空行结束
client.println();
break;
} else { // 不是空白行时 清空内容
currentLine = "";
}
} else if (c != '\r') { // 不是\n 也不是\r
currentLine += c; //拼接起来
}
}
}
header = ""; //清空变量
client.stop(); //通信关闭
Serial.println("Client disconnected.");
Serial.println("");
}
delay(100);
}
else if(all_eeprom_info.esp32cam_mode1==2){ //3、-------------------------------------------
}
else if(all_eeprom_info.esp32cam_mode1==3){
}
else if(all_eeprom_info.esp32cam_mode1==4){
}
}
C++
1
https://gitee.com/ventim/esp32cam-camera.git
git@gitee.com:ventim/esp32cam-camera.git
ventim
esp32cam-camera
22.7 esp32cam多功能摄像机
master

搜索帮助