esp32-cam 通过nbiot sim7020 mqtt通道分批定时上报图片

1、环境配置及抓图

基础部分请参考前序几篇文章。

2、esp32-cam通讯串口定义

esp32-cam折腾了很久串口方式,在arduino直接调用即可。
可以设置任何可用的pin作为rx、tx,我们选择定义针脚13,12


image.png

image.png

先定义串口
HardwareSerial swSer(1);
然后初始化针脚即可,rx 13,tx 12,对应gpio13、gpio12
swSer.begin(115200,SERIAL_8N1,13,12);
即可使用串口

经过多次测试,esp32-cam用自定义针脚会引起图片数据乱码,故需采用默认的rx gpio3、tx gpio1通讯,找到原因花了将近7-8个小时,刷了3、40遍才偶然发现,居坑。

3、sim7020 nbiot

image.png

开发板已经封装,直接at指令即可。
参考地址:http://www.waveshare.net/wiki/SIM7020C_NB-IoT_HAT
esp32-cam和sim7020用rx和tx 接tx1和rx1就可以通讯。

需要sim7020后启动才不会串口乱码。

4、sim7020 mqtt相关at指令

跟mqtt服务器建立连接,1024经测试是可定义缓冲的最大长度。超出会报错。

AT+CMQNEW="IP地址","端口号",12000,1024
连接mqtt服务器,esp32是设备名称,最好用mac地址或者是sim卡号码或其他唯一标记。
AT+CMQCON=0,3,"esp32",600,0,0
发布主题,1000是长度,必须跟字符长度对应,1000是sim7020 at指令 mqtt语句能够接受的最大长度。
test为主题名称,可用主题/唯一标记的方式分开同一图片的不同序列。可以标记最大值和传续值;
AT+CMQPUB=0,"test",1,0,0,1000,"8246D69FA2EA77508915E519ABBFF0008CEA3DCB9F7C55DA1FCC47335D069F0E6A27F8E61F5A4FF00847750C60CF2FE1472C7BFE30B8868EF4C4390FCC2AE8A48246D69FA2EA77508915E519ABBFF0008CEA3DCB9F7C55DA1FCC47335D069F0E6A27F8E61F5A4FF00847750C60CF2FE1472C7BFE30B8868EF4C4390FCC2AE8A48246D69FA2EA77508915E519ABBFF0008CEA3DCB9F7C55DA1FCC47335D069F0E6A27F8E61F5A4FF00847750C60CF2FE1472C7BFE30B8868EF4C4390FCC2AE8A48246D69FA2EA77508915E519ABBFF0008CEA3DCB9F7C55DA1FCC47335D069F0E6A27F8E61F5A4FF00847750C60CF2FE1472C7BFE30B8868EF4C4390FCC2AE8A48246D69FA2EA77508915E519ABBFF0008CEA3DCB9F7C55DA1FCC47335D069F0E6A27F8E61F5A4FF00847750C60CF2FE1472C7BFE30B8868EF4C4390FCC2AE8A48246D69FA2EA77508915E519ABBFF0008CEA3DCB9F7C55DA1FCC47335D069F0E6A27F8E61F5A4FF00847750C60CF2FE1472C7BFE30B8868EF4C4390FCC2AE8A48246D69FA2EA77508915E519ABBFF0008CEA3DCB9F7C55DA1FCC47335D069F0E6A27F8E61F5A4FF00847750C60CF2FE1472C7BFE30B8868EF4C4390FCC2AE8A48246D69FA2EA77508915E519ABBFF0008CEA3DCB9F7C55DA1FCC47335D069F0E6A27F8E61F5A4FF00847750C60CF2FE1472C7BFE"
关闭mqtt连接
AT+CMQDISCON=0

5、获取esp32-cam默认mac地址

计划采用mac地址作为设备唯一码,在mqtt中标出.
https://qiita.com/tapinu00/items/9fa3d7b0ba6d8b44da08
查资料发现日本对于物联网的应用要更深入和细致,文章写的非常好。

image.png

 Serial.begin(115200);
  Serial.println("-----------------------------");
  uint64_t chipid;
  chipid=ESP.getEfuseMac();//The chip ID is essentially its MAC address(length: 6 bytes).
  Serial.printf("ESP32 Chip ID = %04X\r\n",(uint16_t)(chipid>>32));//print High 2 bytes
  Serial.printf("Chip Revision %d\r\n", ESP.getChipRevision());
  esp_chip_info_t chip_info;
  esp_chip_info(&chip_info);
  Serial.printf("Number of Core: %d\r\n", chip_info.cores);
  Serial.printf("CPU Frequency: %d MHz\r\n", ESP.getCpuFreqMHz());  
  Serial.printf("Flash Chip Size = %d byte\r\n", ESP.getFlashChipSize());
  Serial.printf("Flash Frequency = %d Hz\r\n", ESP.getFlashChipSpeed());
  Serial.printf("ESP-IDF version = %s\r\n", esp_get_idf_version());
  Serial.printf("Free Heap Size = %d\r\n", esp_get_free_heap_size());
  Serial.printf("System Free Heap Size = %d\r\n", system_get_free_heap_size());
  Serial.printf("Minimum Free Heap Size = %d\r\n", esp_get_minimum_free_heap_size());
  Serial.println();
  uint8_t mac0[6];
  esp_efuse_mac_get_default(mac0);
  Serial.printf("Default Mac Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac0[0], mac0[1], mac0[2], mac0[3], mac0[4], mac0[5]);
  uint8_t mac1[6];
  esp_efuse_read_mac(mac1);
  Serial.printf("EFuse Mac Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac1[0], mac1[1], mac1[2], mac1[3], mac1[4], mac1[5]);
  uint8_t mac2[6];
  system_efuse_read_mac(mac2);
  Serial.printf("System Mac Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac2[0], mac2[1], mac2[2], mac2[3], mac2[4], mac2[5]);
  uint8_t mac3[6];
  esp_read_mac(mac3, ESP_MAC_WIFI_STA);
  Serial.printf("[Wi-Fi Station] Mac Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac3[0], mac3[1], mac3[2], mac3[3], mac3[4], mac3[5]);
  uint8_t mac4[7];
  esp_read_mac(mac4, ESP_MAC_WIFI_SOFTAP);
  Serial.printf("[Wi-Fi SoftAP] Mac Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac4[0], mac4[1], mac4[2], mac4[3], mac4[4], mac4[5]);
  uint8_t mac5[6];
  esp_read_mac(mac5, ESP_MAC_BT);
  Serial.printf("[Bluetooth] Mac Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac5[0], mac5[1], mac5[2], mac5[3], mac5[4], mac5[5]);
  uint8_t mac6[6];
  esp_read_mac(mac6, ESP_MAC_ETH);
  Serial.printf("[Ethernet] Mac Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac6[0], mac6[1], mac6[2], mac6[3], mac6[4], mac6[5]);

代码运行

[13:55:57.088] -----------------------------
ESP32 Chip ID = A8F6
Chip Revision 1
Number of Core: 2
[13:55:57.097] CPU Frequency: 240 MHz
Flash Chip Size = 4194304 byte
Flash Frequency = 80000000 Hz
ESP-IDF version = v3.3-beta1-179-ge931fe9f5-dirty
Free Heap Size = 292120
System Free Heap Size = 292120
Minimum Free Heap Size = 286036

Default Mac Address = 3C:71:BF:C7:F6:A8
EFuse Mac Address = 3C:71:BF:C7:F6:A8
System Mac Address = 3C:71:BF:C7:F6:A8
[Wi-Fi Station] Mac Address = 3C:71:BF:C7:F6:A8
[Wi-Fi SoftAP] Mac Address = 3C:71:BF:C7:F6:A9
[Bluetooth] Mac Address = 3C:71:BF:C7:F6:AA
[Ethernet] Mac Address = 3C:71:BF:C7:F6:AB

采用esp_efuse_mac_get_default获取值作为设备唯一值;

6、核心代码

总共花200多行代码搞通。
注:设置5位随机数作为每次上报图片的识别码。
标有作业的地方留意。

//加载基本库
#include <Arduino.h>

//esp32-cam库,所有拍照相关定义
#include <esp_camera.h>


//变量定义----------------------------bigen

//esp32-cam针脚定义 拍照相关
constexpr int kCameraPin_PWDN   =  32;
constexpr int kCameraPin_RESET  =  -1;  // NC
constexpr int kCameraPin_XCLK   =   0;
constexpr int kCameraPin_SIOD   =  26;
constexpr int kCameraPin_SIOC   =  27;
constexpr int kCameraPin_Y9     =  35;
constexpr int kCameraPin_Y8     =  34;
constexpr int kCameraPin_Y7     =  39;
constexpr int kCameraPin_Y6     =  36;
constexpr int kCameraPin_Y5     =  21;
constexpr int kCameraPin_Y4     =  19;
constexpr int kCameraPin_Y3     =  18;
constexpr int kCameraPin_Y2     =   5;
constexpr int kCameraPin_VSYNC  =  25;
constexpr int kCameraPin_HREF   =  23;
constexpr int kCameraPin_PCLK   =  22;


//定义mqtt服务器ip和端口,ip需要用自己指定域名
extern const char    *kMqttServerAddress = "XX.XX.XX.XX";
extern const uint16_t kMqttServerPort    = 1883;


//设备mac地址
//char *Mac_Address;
char Mac_Address[12] = "";


//变量定义--------------------------------end





//函数定义----------------------------bigen

//初始化相机
void setupCamera() {
  const camera_config_t config = {
    .pin_pwdn     = kCameraPin_PWDN,
    .pin_reset    = kCameraPin_RESET,
    .pin_xclk     = kCameraPin_XCLK,
    .pin_sscb_sda = kCameraPin_SIOD,
    .pin_sscb_scl = kCameraPin_SIOC,
    .pin_d7       = kCameraPin_Y9,
    .pin_d6       = kCameraPin_Y8,
    .pin_d5       = kCameraPin_Y7,
    .pin_d4       = kCameraPin_Y6,
    .pin_d3       = kCameraPin_Y5,
    .pin_d2       = kCameraPin_Y4,
    .pin_d1       = kCameraPin_Y3,
    .pin_d0       = kCameraPin_Y2,
    .pin_vsync    = kCameraPin_VSYNC,
    .pin_href     = kCameraPin_HREF,
    .pin_pclk     = kCameraPin_PCLK,
    .xclk_freq_hz = 20000000,
    .ledc_timer   = LEDC_TIMER_0,
    .ledc_channel = LEDC_CHANNEL_0,
    //    图片格式
    .pixel_format = PIXFORMAT_JPEG,
    //    图片尺寸 可根据esp_camera.h内容自行修改
    .frame_size   = FRAMESIZE_SVGA,
    //    图片质量 可根据esp_camera.h内容自行修改
    .jpeg_quality = 10,
    .fb_count     = 1,
  };
  //初始化配置
  esp_err_t err = esp_camera_init(&config);
  //  Serial.printf("esp_camera_init: 0x%x\n", err);

  // sensor_t *s = esp_camera_sensor_get();
  // s->set_framesize(s, FRAMESIZE_QVGA);
}

//初始化串口
void setupSerial() {
  //  初始化默认串口
  Serial.begin(115200);
  //  因为采用默认串口和nb_iot模块通讯,可以另外用软串口输出调试信息;
}

//获取默认mac地址作为设备标示
void getMacAddress() {
  uint8_t mac0[6];
  //  获取默认mac地址
  esp_efuse_mac_get_default(mac0);
  //  Serial.printf("Default Mac Address = %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac0[0], mac0[1], mac0[2], mac0[3], mac0[4], mac0[5]);
  // 把mac地址转成字符串
  sprintf (Mac_Address, "%02X%02X%02X%02X%02X%02X",  mac0[0], mac0[1], mac0[2], mac0[3], mac0[4], mac0[5]);


}

//连接mqtt服务器
void initMqtt() {
  //  发送at判断通讯设备状态
  Serial.println("AT\r\n");
  delay(1000);
  //  作业:需要根据at返回状态判断是否执行下一步,如果连不上需要重启,重启采用 ESP.restart()命令

  //   建立mqtt通道
  Serial.println("AT+CMQNEW=\"" + String(kMqttServerAddress)  + "\",\"" + kMqttServerPort + "\",12000,1024\r\n");
  delay(1000);
  //  在mqtt服务器注册设备
  Serial.println("AT+CMQCON=0,3,\"" + String(Mac_Address) + "\",600,0,0\r\n");
  delay(1000);
}


//发送消息(拍照数据,图片长度)
void sendMqttMsg(uint8_t *data, int len) {
  //思路:把uint8格式数据转成16进制数据,然后分批发送,uint8数据长度和16进制字符数据长度有差异(重点)



  //  定义当前传输批次标记,采用随机5位数字,区分每天不同批次
  int batch = random(10000, 99999);


  //上报数据
  String msg;

  //定义临时取数据存储
  char m[1024] = "";

  //传送计数
  int timeCount = 0;

  //  计算总传输次数
  int tranCount = (len * 2 + (1000 - 1)) / 1000;


  //  总字符数量,临时输出
  //  Serial.printf("total: %d\n", len * 2);



  //  从uint8数据堆栈中逐一取出数据转成16进制,并发送,逢内容够1000发送
  for (int i = 0; i < len; i++) {
    //    堆栈取数据,转16进制,不足2位左边补0
    sprintf (m, "%02X", data[i]);
    //    逐一累加到传送字段中
    msg += m;
    //    传送数据达到1000即发送清空数据队列,最后一个队列不足需要判断
    if (msg.length() == 1000) {
      //      传送计数
      timeCount += 1;

      //      传送数据,主题可以自定义,示例格式为
      Serial.print( "AT+CMQPUB=0,\"lamp_iot/" + String(Mac_Address)  + "/" + String(batch) + "_" +  String(tranCount) + "_" + String(timeCount)  + "\",1,0,0,1000,\"" + msg + "\"\r\n");
      //      清空队列
      msg = "";
      //      nb_iot设备at指令有延时,此处需要有delay处理
      delay(500);
      //      作业:传输不成功需要处理
    }

  }
  //    最后一段数据可能不足1000字符传送门
  if (msg.length() > 0) {

    //      传送数据,主题可以自定义,示例格式为
    Serial.print( "AT+CMQPUB=0,\"lamp_iot/" + String(Mac_Address)  + "/" + String(batch) + "_" + String(tranCount) + "_" + String(tranCount)  + "\",1,0,0," + msg.length() + ",\"" + msg + "\"\r\n");
    //      清空队列
    msg = "";
    //      nb_iot设备at指令有延时,此处需要有delay处理
    delay(500);

    //      作业:传输不成功需要处理
  }
}


//断开连接
void disconnectMqtt() {
  //  断开mqtt连接
  Serial.println("AT+CMQDISCON=0\r\n");
  delay(1000);
}




//函数定义--------------------------------end



//主程序----------------------------bigen


//初始化
void setup() {
  //  初始化串口
  setupSerial();
  delay(1000);
  //  获取mac地址
  getMacAddress();
  delay(1000);
  //  初始化相机
  setupCamera();
  delay(1000);
}

//主循环函数
void loop() {
  //  连接mqtt
  initMqtt();

  //  抓拍图片
  camera_fb_t *fb = esp_camera_fb_get();

  //  抓拍成功进行处理,作业:不成功需要触发重启
  if ( fb ) {
    //    Serial.printf("width: %d, height: %d, buf: 0x%x, len: %d\n", fb->width, fb->height, fb->buf, fb->len);
    //    捕获图片成功,开始上报
    sendMqttMsg(fb->buf, fb->len);
  }

  //断开mqtt服务
  disconnectMqtt();

  //  间隔5分钟上报一次,此处定时可自行定义时间,单位为毫秒ms;
  delay(300000);  // [ms]

}



//主程序--------------------------------end

写代码感悟,逻辑思路要清晰,变量定义明确,函数定义明确,逻辑简洁准确,强迫症要格式对齐。

感悟技术的艺术之美。

7、接收mqtt数据效果

一张图片拆了80多个包,一分多钟传输完成。


image.png
可以用后端语言接受每个设备上报的数据,把数据包根据规则组包生成图片即可。

下列是通过python直接把16进制转成jpg图片,手工版数据拼起来图片效果如图:(夜晚阴影中拍摄,看得到黑色数据线和凳子一脚)


image.png

至此通过nb_iot设备跑通。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,941评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,397评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,345评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,851评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,868评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,688评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,414评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,319评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,775评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,945评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,096评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,789评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,437评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,993评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,107评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,308评论 3 372
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,037评论 2 355