1 Star 0 Fork 1

VENTIM / 22.7 esp32cam多功能摄像机

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
esp32cam_TFT14.3 多处响应按键,默认不进深度睡眠.txt 83.45 KB
一键复制 编辑 原始数据 按行查看 历史
志宇益生菌 提交于 2023-10-28 12:08 . 提交已有的代码备份文件
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118
// 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)
*/
//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保存 +整理
//14.1 移植功能3:物联网摄像头 》图像、电压值上传ok,唤醒、拍摄间隔控制ok,led亮度调节ok,能深度睡眠唤醒,但深度睡眠时亮白屏!
//14.2 开机显示界面时可切换功能,运行中熄屏下任意按键恢复显示,功能切换后存mode重启加载。
//14.3 在连wifi也能响应按键, 加IOT_sleep_flag变量默认0不睡眠黑屏+能响应按键 ?远程控制变量,选择静默运行?虽然功耗大20ma
//简单未找到深度睡眠中能保持引脚电平的方法:深度睡眠中led、tft会亮。 ?
//》深度睡眠+亮白屏+串口模块,5v44ma 连wifi灭屏100ma-140
//》正常运行关闭所有+串口模块:63ma
//打印电压值弄个变量控制,
//界面:1、tft摄像机,2、局域网摄像头 3、物联网摄像头 4、桌面天气 5、手机来蓝牙拍照遥控 6、手机蓝牙丢失提醒 .7、播放音乐 8、网络mic/网络收音机 9秘钥?模拟键盘自动输入
//SD卡存储 与 tft分时使用! 或者能否同时. 好像不能
/* //(短按/长按) 【【功能设计】】 灭屏下 任意按键亮屏!
* 面对屏幕
* 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" //禁用掉电检测器 用
#include "esp_http_client.h"
#include <PubSubClient.h> //mqtt 【2.0】 搭配 小程序的mqtt v1.0使用
const char* ssid = "hello123";
const char* password = "123hello";
//const char* ssid = "LGST-2.4g";
//const char* password = "lgst8888";
//const char* ssid = "HUAWEI-2630";
//const char* password = "52649458";
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
u8 IOT_sleep_flag; //物联网摄像头模式下 是否深度睡眠 0不 1要睡眠(省电,但白屏)
u16 dir_num;
// char ignore_APP_versions[5]; //
} all_eeprom_struct_t;
all_eeprom_struct_t all_eeprom_info = {0};
//mode2 有关onenet的mqtt上传图片,http访问数据流的
#define mqtt_server "183.230.40.39" //OneNet-MQTT IP
#define mqtt_port 6002 //OneNet-MQTT port
#define mqtt_pubid "323078" //产品ID
#define mqtt_devid "1125291865" //设备ID !!下边上传图片路径也要改!!
const char* post_url = "http://api.heclouds.com/bindata?device_id=1125291865&datastream_id=stream_name"; // onenet对应设备上传图片地址
const char* apk_key = "Q=xxxx="; //用户api key 可能需要总的权限
WiFiClient espClient; //客户端
PubSubClient client(espClient); //绑定 mqtt使用。不能用于其他访问?
static String httpResponseString; //接收服务器返回信息
esp_http_client_handle_t http_client;
esp_http_client_config_t config_client = {0}; //客户端0
int KEY_state = 0; //暂时没有用到
bool esp32_work_flag=1; //1 工作 0睡眠
long last_order_time=0; //最近一次接收到命令的时间 的millis()
#define order_cycle_time 120000 //无操作 120s后,将进入深度睡眠
#define deepsleep_time 60 //进入深度睡眠 自动唤醒的时间 s
unsigned char main_delay=2; //loop中,yibu延时多久s 主要是控制时候的拍照 0不延时 大于睡眠周期 则不拍照上传
long last_main_delay=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;
all_eeprom_info.IOT_sleep_flag=0; //默认 多耗电但不进入睡眠,黑屏
}
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;
if(all_eeprom_info.IOT_sleep_flag<0||all_eeprom_info.IOT_sleep_flag>1) all_eeprom_info.IOT_sleep_flag=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);
}
//adc读取电压,更新电池电压+判断按键 不会重复触发,按下只返回1次 【以及模式切换:跳转对应的初始化函数中】
//注意:不可高频调用,需加延时,按键adc变化慢
char adc_power_key(char show_v_tft=1){ //1-4为对应按键短按,5-8为按键长按, 0是可更新电源电压,//-1是在判断是否长按短按 默认在tft显示电压出来
static int adc_flag_num=0; //计算adc判断 非0连续次数,用于判断按键长按 短按逻辑 可用于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); //设置字体大小 小
delay(50); return 0; //亮屏而已
}
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;
//切换跳转
Serial.println("KEY change mode:"+String(all_eeprom_info.esp32cam_mode1));
Serial.println("restart!");
char mode_temp = all_eeprom_info.esp32cam_mode1;
EEPROM_Read(0,(u8 *)&all_eeprom_info,sizeof(all_eeprom_info));
all_eeprom_info.esp32cam_mode1=mode_temp;
EEPROM_Write(0,(u8 *)&all_eeprom_info,sizeof(all_eeprom_info));
esp_camera_deinit(); //摄像头去初始化
tft_deinit();
tft_off();
sd_deinit();
wifi_off();
delay(50); //原100
ESP.restart();
// 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: IOT_cam_init(); break;
// case 3: TFT_weather_init(); 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; //按键按下,还在判断长按还是短按的时候
}
void up_state() {// mqtt上传有关状态
SendMQTT_onevalue("state", "{\"LED\":" + String(LED1_bright) + ",\"key\":" + String(KEY_state) + "}"); //上报数据
}
bool connect_WIFI() { //有超时时间
int i = 0;
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(100); adc_power_key(); //有关电池电压/按键的获取
delay(100); adc_power_key(); //有关电池电压/按键的获取
delay(100); adc_power_key(); //有关电池电压/按键的获取
delay(100); adc_power_key(); //有关电池电压/按键的获取
delay(100); adc_power_key(); //有关电池电压/按键的获取
Serial.print(".");
}
}
if (WiFi.status() != WL_CONNECTED ) {
Serial.println("Connect wifi failed!");
WiFi.disconnect(true); //断开且关闭STA网络 好像一样,约35ma
//WiFi.mode( WIFI_MODE_NULL ); //关闭wifi 这行而已,电流34ma
return false;
}
Serial.println(" WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
return true;
}
// 重新连接 mqtt
bool connect_MQTT() { //有超时时间
Serial.print("Attempting MQTT connection...");
client.connect(mqtt_devid, mqtt_pubid, apk_key); //连接
for (int i = 0; i < 10; i++) { //5s内尝试连接
if (!client.connected()) {
delay(500); Serial.print("-");
}
}
if (!client.connected()) { //失败
Serial.print("failed, rc=");
Serial.println(client.state());
return false;
}
Serial.println("MQTT connected");
// up_state(); //上报情况
return true;
}
// onenet的mqtt回调函数 接收云端下发命令
void callback(char* topic, byte* payload, unsigned int length) { //接收到的 数据,很快反应
Serial.print("Message arrived [");
Serial.print(topic); //主题 每次随机 $creq/8838d6df-0783-5ed9-80a4-07ec92aee0f3
Serial.print("] ");
String getmessage;
//类似 LED:1,report:0, 以,结尾
for (int i = 0; i < length; i++) getmessage = getmessage + (char)payload[i]; //合成字符串
Serial.println(getmessage);
// SendMQTT_onestring("state", getmessage);
int place_temp = -1;
//以下是 分析接收到的命令 并 执行
/* []云端唤醒设备: 数据流 work:1
*
* []云端下发数据格式: 多个: LED0:1,LED1:100,
LED1:1, //控制白色led亮度 0-255
jpg:1, //单独拍照一张上传上来
carup:100, //控制小车向前100ms 阻塞 运行后小车停止
cardown:100, //控制小车向后100ms 阻塞
carleft:100, //控制小车向左100ms 阻塞
carright:100, //控制小车向右100ms 阻塞
servo:65, //摄像头舵机视角
power:1, //读取一次电压值上传
cam_num:610, //摄像头参数组合 百位0-6,图片大小,越大越大 后两位,图片质量,越小越好
main_delay:0, //loop拍照上传的异步延时 s 0-255 超过睡眠时间就直接睡眠 50还正常 保险则超过30s进行重连接再拍照上传
【开机】默认参数
图片大小3 质量20 连续拍照间隔1s
小车前进后退时间 500ms
LED1亮度 1
舵机角度65
*/
if ((place_temp = getmessage.indexOf("LED1")) != -1) { //控制大 白色LED 0-255
String value = getmessage.substring(getmessage.indexOf(':', place_temp) + 1, getmessage.indexOf(',', place_temp));
Serial.println("get LED1:" + value);
LED1_bright = value.toInt();
if (LED1_bright > 255) LED1_bright = 255;
ledcWrite(LED1_channel, LED1_bright); //调节对应通道的亮度
}
if ((place_temp = getmessage.indexOf("jpg")) != -1) { //控制拍照并上传照片 1
String value = getmessage.substring(getmessage.indexOf(':', place_temp) + 1, getmessage.indexOf(',', place_temp));
Serial.println("get jpg:" + value);
if (value.toInt() == 1) { //如果是 1 则拍照上传
esp_http_client_cleanup(http_client); //清空客户端
http_client_init1();
take_send_photo(); //调用进行拍照+上传图片
}
}
//大小+质量 0-6 0-32 如 612
if ((place_temp = getmessage.indexOf("cam_num")) != -1) { //摄像头图片大小+质量
String value = getmessage.substring(getmessage.indexOf(':', place_temp) + 1, getmessage.indexOf(',', place_temp));
Serial.println("get cam_num:" + value);
all_eeprom_info.pic_size = value.toInt()/100;
all_eeprom_info.pic_quality = value.toInt()%100;
Serial.println("[cam_size]: " + String(all_eeprom_info.pic_size)+"[cam_quality]:"+ String(all_eeprom_info.pic_quality));
esp_err_t err = esp_camera_deinit(); //摄像头去初始化
if (err != ESP_OK) { //若失败
Serial.printf("Camera deinit failed with error 0x%x", err);
}
else camera_init(all_eeprom_info.pic_size,all_eeprom_info.pic_quality); //成功了再按参数初始化 否则摄像头就本次无法使用
}
if ((place_temp = getmessage.indexOf("main_delay")) != -1) { //控制拍照上传时间间隔 及关闭拍照上传
String value = getmessage.substring(getmessage.indexOf(':', place_temp) + 1, getmessage.indexOf(',', place_temp));
Serial.println("get main_delay:" + value);
main_delay =value.toInt();
}
if ((place_temp = getmessage.indexOf("key")) != -1) { //控制key 例子
String value = getmessage.substring(getmessage.indexOf(':', place_temp) + 1, getmessage.indexOf(',', place_temp));
Serial.println("get KEY:" + value);
KEY_state = value.toInt();
}
// up_state();
last_order_time = millis(); //更新最后时间 让进入深度睡眠时间 重新计算
}
// http请求处理 回调函数
esp_err_t _http_event_handler(esp_http_client_event_t *evt) {
if (evt->event_id == HTTP_EVENT_ON_DATA)
{
httpResponseString.concat((char *)evt->data); //将http接收的数据拼起来
}
return ESP_OK;
}
//初始化esp_http_client_init
void http_client_init1(){ //初始化1次 减少每次拍照上传初始化时间
config_client.url = post_url; //url
config_client.event_handler = _http_event_handler; //接收处理回调函数
config_client.method = HTTP_METHOD_POST; //POST方法
http_client = esp_http_client_init(&config_client); //初始化客户端
esp_http_client_set_header(http_client, "Content-Type", "image/jpg"); //设置http包数据类型
esp_http_client_set_header(http_client, "api-key", apk_key); //设置http头部字段
esp_http_client_set_header(http_client, "Connection", "keep-alive"); //设置http头部字段
}
// 拍照并推送图片到云端
static esp_err_t take_send_photo() {
if(main_delay>0) Serial.println("Taking picture...");
esp_err_t res = ESP_OK;
if(fb != NULL) esp_camera_fb_return(fb); //应该是销毁图片指针
fb = esp_camera_fb_get(); //拍照 返回照片指针
if (!fb) { //拍照失败
Serial.println("Camera capture failed!");
return ESP_FAIL;
}
httpResponseString = ""; //清空接收内容
// http_client_init1(); //初始化 http_client
esp_http_client_set_post_field(http_client, (const char *)fb->buf, fb->len);//设置http发送的内容和长度
esp_err_t err = esp_http_client_perform(http_client);//发送http请求
if(main_delay>0) Serial.println(httpResponseString);//打印接收到的数据
//esp_http_client_cleanup(http_client); //清空客户端
// esp_camera_fb_return(fb); //应该是销毁图片指针
}
// 检查 “work”数据流的数值
bool chack_work_flag(){ //1为要工作 0为待机
String data1;
char res = get_onedatapoint_HTTP("work" , apk_key , mqtt_devid , &data1 );
//Serial.println("data1:"+data1);
if(res == 0){ //访问正常时候
if (data1.toInt() == 0) { //检测到是 不需要工作标记
Serial.println("work:0");
return 0;
}
else {
Serial.println("work:1");
return 1;
}
}
else{
if(res==3){ //没有找的数据点名称,则创建一个。 适配小程序,需要按顺序上传创建
SendMQTT_onevalue("state", "0");delay(500);
SendMQTT_onevalue("temp", "0");delay(500);
SendMQTT_onevalue("humi", "0");delay(500);
SendMQTT_onevalue("stream_name" , "1"); //上传图片的数据流 有时新设备这条创建数据流名称
delay(500);
SendMQTT_onevalue("work", "0"); //回应数据流 知道要工作了,下次没意外就是睡眠
delay(500);
SendMQTT_onevalue("power", "0");
for(int i=0;i<50;i++){ //5s
client.loop(); //检测MQTT 是否收到数据 心跳
delay(100);
}
}
return 0; //访问失败时候,默认不工作
}
}
// 进入深度睡眠 会自动断网
void esp32_deep_sleep(int seconds = 60){ //多少s后自动唤醒 重启
pinMode(32,OUTPUT ); // 32是 cam电源控制引脚
digitalWrite(32, 1); // 高电平 断电 cam的
esp_sleep_enable_timer_wakeup(seconds*1000000); //唤醒时间 us
//Serial.println(esp_sleep_get_wakeup_cause());
WiFi.disconnect(true); //断开且关闭STA网络 好像一样,约35ma
//WiFi.mode( WIFI_MODE_NULL ); //关闭wifi 这行而已,电流34ma
Serial.println("ESP32 sleep now!"+String(millis()));
delay(20);
esp_deep_sleep_start(); //深度睡眠 确实小于1ma
//esp_light_sleep_start(); //浅度睡眠
}
//检查连接 自动重连wifi+mqtt +上报电压
void check_connect(){
if (!client.connected()) { //MQTT 没连接
if (WiFi.status() != WL_CONNECTED ) { //先判断wifi
//Serial.println("<-->WIFI disconnect");
connect_WIFI();
}
if (WiFi.status() == WL_CONNECTED ) { //wifi连接了才连接MQTT
//Serial.println("<-->MQTT disconnect");
connect_MQTT();
}
}
}
//onenet获取指定数据流内容
char get_onedatapoint_HTTP(String stream_id , String api_key , String device_id , String *data){ //数据data返回
/*String data1;
get_onedatapoint_HTTP("temp" , mqtt_password , mqtt_devid , &data1 );
Serial.println("data1:"+data1); */
#define timeout 5 //http通信响应等待最长时间 s
#define host "api.heclouds.com" //http访问网址
WiFiClient client_http;
int i = 0;
while (client_http.available()) { //清空接收缓存
client_http.read();
}
//建立连接
if (!client_http.connect( host , 80)) {
Serial.println("connection failed");
return 1; //建立连接失败
}
//组合发送get请求包 请求获取全部数据流数值
String url = "/devices/" + device_id + "/datastreams/" + stream_id;
Serial.println(">>Requesting URL: " + url);
client_http.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"api-key: " + api_key + "\r\n" +
"Connection: close\r\n\r\n");
//检测响应时间内 有无收到
for (i = 0; i < timeout * 100; i++) {
if (client_http.available() != 0)break;
delay(10);
}
if (i >= timeout * 100) {
Serial.println(">>> Client Timeout !");
client_http.stop();
return 2; //get请求超时
}
//提取数据中 指定的 数据流值
// Serial.println(client_http.readString()); //打印接收内容 打印了就影响后面的搜索
// if (client_http.find(stream_id) //这样不行,不知为啥?
////提取时间 Date: Thu, 18 May 2023 03:47:37 GMT
// if (client_http.find("Date: ")) //访问指定数据流 直接判断值存在
// {
// client_http.find("20");
// client_http.find(' ');
// String line = client_http.readStringUntil(':');
////Serial.println(line);
// hour1 = line.toInt();
// line = client_http.readStringUntil(':');
////Serial.println(line);
// min1 = line.toInt();
//
// hour1+=8;
// if(hour1>23) hour1-=24;
// Serial.println("[beijing time] " + String(hour1) + " : " + String(min1));
// }
if (client_http.find("\"current_value\":")) //访问指定数据流 直接判断值存在
{
//client_http.find("\"current_value\":");
String line = client_http.readStringUntil('}');
Serial.println(">>> " + stream_id + " : " + line);
//stream_value[i] = line.toInt();
*data = line ;
}
else { //没有数值在时
Serial.println(client_http.readString()); //打印接收的数据内容
Serial.println("no value!");
client_http.stop();
return 3; //没找到 数值
}
client_http.stop();
return 0;
}
//上传数据流 字符串
bool SendMQTT_onestring(String stream_id1 , String string1) {
return Send_onedatapoint_MQTT(stream_id1 , string1 , 1);
}
//上传数据流 数据点
bool SendMQTT_onevalue(String stream_id0 , String string0) {
return Send_onedatapoint_MQTT(stream_id0 , string0 , 0);
}
//上传数据流
bool Send_onedatapoint_MQTT(String stream_id , String string , char flag){ //flag=1 发送字符串 0 数值 好像要执行 client.loop(); 才有效。
String senddata;
if (flag == 1) senddata = "{\"" + stream_id + "\":\"" + string + "\"}";
else senddata = "{\"" + stream_id + "\":" + string + "}";
int json_len = senddata.length();
char msgJson[json_len + 3]; //实际+1
char msg_buf[json_len + 10] = {char(0x03), char(json_len >> 8), char(json_len & 0xff)}; //palyLoad packet type3(json type2)
senddata.toCharArray(msgJson, json_len + 1); //?
memcpy(msg_buf + 3, msgJson, json_len); //将msgJson数组的 字节 复制到msg_buf + 3上及以后
// Serial.print("Lngth of published message: ");
// Serial.println(strlen(msgJson));
// Serial.println(json_len);
Serial.print("Send message:");
Serial.println(msgJson);
//设备完成连接鉴权之后,将数据按照一定的格式(见协议文档说明)打包,将数据发布到$dp系统Topic上即可。
return client.publish("$dp", (uint8_t*)msg_buf, 3 + json_len); //// msg_buf as payload length which may have a "0x00"byte
}
//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;
}
//是否存? 保存照片到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 all_stop(){ //所有外设都关闭断电
esp_camera_deinit(); //摄像头去初始化
pinMode(32,OUTPUT ); // 32是 cam电源控制引脚
digitalWrite(32, 1); // 高电平 断电 cam的
sd_deinit();
tft_off();
wifi_off();
btStop();
}
//各个模式下的初始化 以及关闭耗电的硬件
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(){
adc_power_key();
tft_init();
tft.drawString("[esp32_cam_iot]", 0,16);
tft.drawString("connect to ", 0,32);
tft.drawString( ssid, 68,32);
tft.drawString("cam size/quality:"+String(all_eeprom_info.pic_size)+","+String(all_eeprom_info.pic_quality), 0,64);
tft.drawString("device id:"+String(mqtt_devid), 0,80);
tft.drawString("(0nosleep,1sleep):"+String(all_eeprom_info.IOT_sleep_flag), 0,96);
connect_WIFI(); //连接wifi
client.setServer(mqtt_server, mqtt_port); //配置mqtt
client.setCallback(callback);
check_connect(); //检查mqtt连接, 断了自动重连wifi+mqtt +上报电压
tft.drawString("IP:", 0,48);
tft.drawString(WiFi.localIP().toString().c_str(), 30,48);
esp32_work_flag = chack_work_flag(); // 1工作 0睡眠
SendMQTT_onevalue("power", String(power_v-power_adjust));
//delay(500); delay(1500);
for(int i=0;i<100;i++){ //边等待 边响应按键
adc_flag = adc_power_key(); //有关电池电压/按键的获取
delay(30);
if(adc_flag==7){ //长按的切换模式,则需要退出当前
return;
}
}
tft_off();
if(esp32_work_flag==1){ //访问 得知 要工作
last_order_time = millis(); //更新最后时间
Serial.print(millis()/1000);
Serial.println("s : work");
//摄像头初始化失败 则 进入睡眠
esp_camera_deinit(); //摄像头去初始化
//sensor_t *cam123 = esp_camera_sensor_get();
//cam123->reset(cam123); //控制 cam 复位
pinMode(32,OUTPUT ); // 32是 cam电源控制引脚
digitalWrite(32, 0); // 猜测 低电平通电 !在睡眠期间可能无法控制断电!
delay(100);
digitalWrite(32, 1); // 猜测高电平 断电 cam的
delay(100);
pinMode(32,INPUT ); //释放 引脚
if(camera_init(all_eeprom_info.pic_size,all_eeprom_info.pic_quality)==true) Serial.println("camera init ok!"); //默认是 FRAMESIZE_UXGA
else {
esp32_deep_sleep(deepsleep_time); //深度睡眠 自动时间唤醒
//Serial.println("restart!! ");
//ESP.restart(); // 板子重启 不知能否解决,不行
}
SendMQTT_onevalue("work", "0"); //回应数据流 知道要工作了,下次没意外就是睡眠
http_client_init1(); //初始化 有关拍照上传的client 全局一次性初始化
led_pwm_init(); //初始化 大led
}
else{ //不用工作的时候
all_stop();
if(all_eeprom_info.IOT_sleep_flag==1)esp32_deep_sleep(deepsleep_time); //深度睡眠 自动时间唤醒
else{ //不进入睡眠,则
for(int i=0;i<60;i++){
for(int j=0;j<20;j++) {
adc_power_key(); //有关电池电压/按键的获取
delay(50);
}
Serial.println("all stop"+String(60-i));
}
ESP.restart(); // 板子重启
}
}
}
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
//all_eeprom_info.esp32cam_mode1=2;
//all_stop();
//delay(3000);
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: IOT_cam_init(); break;
case 3: TFT_weather_init(); break;
case 4: break;
case 5: break;
default: break;
}
}
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、物联网摄像头-------------------------------------------
check_connect(); //检查mqtt连接, 断了自动重连wifi+mqtt
client.loop(); //检测MQTT 是否收到数据 心跳
delay(80); //延时 可以不用 降低刷新率
//超时没操作 进入睡眠
if(millis()-last_order_time>=order_cycle_time){ //当距离上一次数据交互时间 超过时间 就深度睡眠
//需要手动关闭一些硬件 如pwm,可省电 一点 好像没用?深度睡眠外设会断电?
// ledcWrite(LED1_channel, 0); //关闭LED1
//因为无法解决深度睡眠中: ,io14为低电平, 很多引脚电平都是无法改变。 可能外部上拉或其他原因。
all_stop();
if(all_eeprom_info.IOT_sleep_flag==1){
Serial.print(millis()/1000);
Serial.println("s : sleep");
esp32_deep_sleep(deepsleep_time); //深度睡眠 自动时间唤醒
}
else{ //不进入睡眠,则
for(int i=0;i<60;i++){
for(int j=0;j<20;j++) {
adc_power_key(); //有关电池电压/按键的获取
delay(50);
}
Serial.println("all stop"+String(60-i));
}
ESP.restart(); // 板子重启
}
}
//间隔周期时间 进行拍照上传
if (millis() - last_main_delay > main_delay*1000) { //
last_main_delay = millis();
if(main_delay>30){ //超过30s 防止连接断开,每次回重新连接后拍照上传
esp_http_client_cleanup(http_client); //清空客户端
http_client_init1();
}
take_send_photo();//这里需要一直拍照上传
}
}
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

搜索帮助