1 Star 0 Fork 1

VENTIM / 22.7 esp32cam多功能摄像机

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
esp32cam_TFT12.8 整理.txt 58.96 KB
一键复制 编辑 原始数据 按行查看 历史
志宇益生菌 提交于 2023-10-28 11:48 . 提交已有的代码备份文件
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502
// 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)
*/
//1 文字图形ok
//2 拍照显示到tft,周期显示特定图片。 显示缩放0.5和左上拍摄图片 相关函数
// 》有时正常,有时显示拍照的图片不完整
//3 拍照设置为160x120,尝试改善刷屏的屏闪 使用DMA方式 测试原装板子,运行正常 刚好大小的 1张图43ms
//4 移植SDMMC代码 拍照存储。间歇显示存储,存完需要重启tft才正常,原因可能是去初始化DMA导致。 重启前后图片间隔3s
//5.1 使用新库TJpg_Decoder,实现拍照显示,速度比上一个库快。
//5.2 将SD卡相关代码移植进来。 360*240缩放dma显示 70ms左右。 整体刷新间隔: 差不多70ms
//6 整理代码、增加adc功能按键及处理函数 电池电压,按键存sd
//7 增加长按短按的判断、关闭tft及背光函数、长按key3切换功能、条件让core0休眠
//8 整理代码函数、尝试将局域网图传代码移植进来,优化2个功能切换运行、熄屏与亮屏、 ok
//9 编写提示、设置简单界面,led的pwm,解决不同mode下led亮度调节、
//10 完善功能1设置界面和逻辑,增加功能2提示界面、复制设置界面给2 ?重新进入模式,led亮度调节有问题 获取占空比数值与设置相同
//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 整理
//??完善延时摄影:
//设置界面,间隔时间非0且保存,就会延时摄影。屏幕提示,但不显示图案,直接录存到内部。
//长按4 停止延时摄影+显示。
//延时摄影中,只初始化sd、摄像头、关闭tft
//界面:1、tft摄像机,2、局域网摄像头 3、物联网摄像头 4、桌面天气 5、手机来蓝牙拍照遥控 6、手机蓝牙丢失提醒
//开发按键功能菜单, 增加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();
WiFiServer server(80);
String header; //存放 接收到client的请求 内容
unsigned long currentTime=0; //有关client超时时间 当前
unsigned long previousTime = 0; //有关client超时时间 上次
const long 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"; //空的
//int dir_num=0; //sd卡中,保存到哪个文件夹下? 每次通电判断有无 更新+1
//文件夹名字 jpg_1 jpg_2
//其他变量
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-6]\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;
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");
}
}
//存储图片到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;
}
//是否存? 保存照片到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(); // 板子重启
}
//adc读取处理电压,更新电池电压以及判断按键按下抬起 不会重复触发,按下只返回1次 【以及模式切换】
char adc_power_key(){ //1-4为对应按键短按,5-8为按键长按, 0是可更新电源电压,//-1是在判断是否长按短按
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');
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; //按键按下,还在判断长按还是短按的时候
}
//数据流传输的执行 句柄
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){
fb = esp_camera_fb_get(); //拍照
if (!fb) {
Serial.println("Camera capture failed");
res = ESP_FAIL;
} else { //拍照成功
if(fb->width > 400){
if(fb->format != PIXFORMAT_JPEG){ //不是jpg
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;
}
}
}
//传输数据流 相关发送
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);
}
}
//考虑比较周全的 启动/关闭
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;
}
}
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(){ //目前要求了图片大小 质量?
tft_init();
tft.drawString("connect to ", 0,32);
tft.drawString( ssid, 68,32);
led_pwm_init(); //初始化led
wifi_init();
tft.drawString("wifi connected :", 0,48);
tft.drawString(WiFi.localIP().toString().c_str(), 0,64);
core0_work_flag=0; //关闭core0
//开启tft进行提示
//关闭core0
Serial.print("Camera Stream Ready! Go to: http://");
Serial.print(WiFi.localIP());
if(camera_reinit(4,12)==true) tft.drawString("cam init ok", 0,80);
else tft.drawString("cam init error!!!", 0,80);
//启动流式web服务器
startCameraServer();
server.begin();
delay(3800); //显示
tft_off();
}
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
#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.drawString("xx", 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("[2.Setting change]", 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.xxxx", 0,80); //时间间隔
tft.drawString("4.", 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: time_span2+=(time_span2/10+1);if(time_span2>255) time_span2=255; 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;
}
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){ reinit_flag=2;all_eeprom_info.time_span1=time_span2; } //修改延时摄影的
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);
//调节缩放的?
}
}
tft.setTextColor(0xFFFF, 0x0000); //白字 黑底
tft.fillScreen(TFT_BLACK); //屏幕全黑
tft.setTextSize(1); //设置字体大小 小
tft.drawString("wifi connected :", 0,48);
tft.drawString(WiFi.localIP().toString().c_str(), 0,64);
}
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();
}
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);
//span_pic_save(); //拍照存储 不重启
}
}
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;
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(); //目前的millis
previousTime = currentTime; //上一次的millis
Serial.println("New Client.");
String currentLine = ""; //创建一个字符串来保存从客户端传入的数据
while (client.connected() && currentTime - previousTime <= timeoutTime) { //有连接/断开但未超时
currentTime = 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(); //空行
// turns the GPIOs on and off
if (header.indexOf("GET /forwards/on") >= 0) { //看请求的url
forwards = 1;
Serial.println(forwards);
}
else if(header.indexOf("GET /forwards/off">=0)){
forwards =0;
Serial.println(forwards);
}
// 显示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: #4CAF50; border: none; color: black; padding: 8px 20px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
client.println(".button_off {background-color: #555555;}</style></head>");
//网页标题
//client.println("<body><h1>Rover Control Center</h1>");
//载入81端口的图片
client.println("<body onload=\"document.getElementById('stream').src=document.location.origin+':81/stream';\"><h1>This is a teat. use computer!</h1>");
client.println("<img id='stream' src='' crossorigin='anonymous'>");
// 显示当前状态和前进的开/关按钮
// 如果forwards_state为off,则显示ON按钮
if (forwards == 0) {
client.println("<p><a href=\"/forwards/on\"><button class=\"button_on\">forwards active</button></a></p>");
} else {
client.println("<p><a href=\"/forwards/off\"><button class=\"button_on button_off\">forwards off</button></a></p>");
}
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){
}
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

搜索帮助