本篇目标:将摄像头OV7670的照片数据,转换成BMP二进制,上传到ONENET平台,用于远程监测。

材料准备:

  • 之前移植的温湿度及红外修改工程:(温湿度及红外修改工程),继续往里面移植摄像头驱动上传代码。
  • STM32F407最终摄像头上传ONENET平台工程:(STM32F4摄像头数据上传onenet)(git仓库地址),里面包含温湿度,红外感应,摄像头照片按一定时间周期上传ONENET平台。

摄像头OV7670硬件连接与接口

这里的摄像头OV7670与上一章的一致,详细可以见(家庭IOT监测之摄像头OV7670测试)。


移植摄像头等相关驱动

用keil打开准备材料1的stm32f407_iot工程,往里面添加代码,驱动摄像头OV7670:

  • 将准备材料最终修改中的摄像头驱动文件夹(ov7670)与(rgb2bmp)拷贝到stm32f407_iot标准工程dev文件夹下。
  • 在Manage Project Items添加文件夹ov7670与rgb2bmp,然后添加刚才对应拷贝文件夹下的C文件。
  • 在Options->C/C+±>Include Paths添加dev下复制文件夹ov7670与rgb2bmp的路径。
  • 需要修改ov7670.h与sccb.h文件中对应的IO引脚,修改成自己对应的引脚口,应该用注释标出。
  • 仔细检查ov7670.c与sccb.c文件中的引脚初始化函数,确认相关RESET、PWDN、SCCB等引脚初始化正确;修改dcmi.c的My_DCMI_Init函数中对于DCMI相关引脚的初始化,与自己的引脚对应。
  • 在main.c中添加头文件:
#include "ov7670.h"
#include "dcmi.h"
#include "ov7670test.h"
#include "rgb2bmp.h"
  • 向main函数中添加代码,如下:
 //..../* WIFI模块IO初始化配置 */NET_DEVICE_IO_Init();/* 摄像头OV7670相关初始化配置 */if (OV7670_Init() != 0)    //新添加{printf("Ov7670 Init Failed.\r\n");}else{printf("Ov7670 Init Succeed.\r\n");
#ifdef OV7670_DBGOV7670_USART_Init();
#endif}
  • 编译通过即可,这样就像上一章一样,移植成功了ov7670的驱动代码,即可使用ov7670摄像头了。

RGB转BMP代码##

在修改之前,先来了解一下,摄像头的数据和BMP文件的数据有哪些不同:

  1. 摄像头数据分很多种,RGB565、RGB555、RGB444、YUV、YCbCr等,这里选取的是RGB565(常用于液晶屏显示直接输出)。RGB565包含16位,如下:
  2. BMP格式数据由几部分组成:
    (1)bmp文件头:共包含有14个字节:

    (2)位图信息头:共包含40个字节;

    (3)调色板:单色,16色,256色包含调色板;16位,24位,32位不包含调色板;
    (4)位图数据;

现在来修改代码:

  • 代码摄像头将获取到的RGB565数据存在了数组camera_buffer[]中,要将数据转换成BMP文件,需要增加bmp文件头和信息头共54字节;
  • 定位文件ov7670.c的17行camera_buffer数组,数组增加54字节,修改成:
uint16_t camera_buffer[PIC_WIDTH*PIC_HEIGHT+27]={0};
  • 定位ov7670.h的23行,同样修改成:
extern uint16_t camera_buffer[PIC_WIDTH*PIC_HEIGHT+27];
  • 修改DMA开始地址与数据长度,往后移54个字节,定位ov7670.c的88行,修改DCMI_DMA_Init传入参数:
DCMI_DMA_Init((uint32_t)&camera_buffer+54,(sizeof(camera_buffer)-54)/4,DMA_MemoryDataSize_HalfWord,DMA_MemoryInc_Enable);
  • 这样一来数组camera_buffer前54字节放bmp数据头,因为是16位位图,没有调色板,接下来的字节都放ov7670的rgb565数据。
  • 调用rgb2bmp.c中的rgb565tobmp()函数即可将camera_buffer数组修改成二进制bmp文件。

这里贴出RGB2BMP文件的代码:
rgb2bmp.c:

//主函数
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "rgb2bmp.h"static void RGBtoBMP(char *bmp_buffer, int nWidth, int nHeight, char bits)
{BmpHead m_BMPHeader;char bfType[2] = { 'B','M' };m_BMPHeader.imageSize = bits * nWidth*nHeight + 54;m_BMPHeader.blank = 0;m_BMPHeader.startPosition = 54;memcpy(bmp_buffer, bfType, sizeof(bfType));bmp_buffer += sizeof(bfType);memcpy(bmp_buffer, &m_BMPHeader.imageSize, sizeof(m_BMPHeader.imageSize));bmp_buffer += sizeof(m_BMPHeader.imageSize);memcpy(bmp_buffer, &m_BMPHeader.blank, sizeof(m_BMPHeader.blank));bmp_buffer += sizeof(m_BMPHeader.blank);memcpy(bmp_buffer, &m_BMPHeader.startPosition, sizeof(m_BMPHeader.startPosition));bmp_buffer += sizeof(m_BMPHeader.startPosition);InfoHead  m_BMPInfoHeader;m_BMPInfoHeader.Length = 40;m_BMPInfoHeader.width = nWidth;m_BMPInfoHeader.height = nHeight;m_BMPInfoHeader.colorPlane = 1;m_BMPInfoHeader.bitColor = BMP_BITS;m_BMPInfoHeader.zipFormat = 0;m_BMPInfoHeader.realSize = bits * nWidth*nHeight;m_BMPInfoHeader.xPels = 2835;m_BMPInfoHeader.yPels = 2835;m_BMPInfoHeader.colorUse = 0;m_BMPInfoHeader.colorImportant = 0;memcpy(bmp_buffer, &m_BMPInfoHeader.Length, sizeof(m_BMPInfoHeader.Length));bmp_buffer += sizeof(m_BMPInfoHeader.Length);memcpy(bmp_buffer, &m_BMPInfoHeader.width, sizeof(m_BMPInfoHeader.width));bmp_buffer += sizeof(m_BMPInfoHeader.width);memcpy(bmp_buffer, &m_BMPInfoHeader.height, sizeof(m_BMPInfoHeader.height));bmp_buffer += sizeof(m_BMPInfoHeader.height);memcpy(bmp_buffer, &m_BMPInfoHeader.colorPlane, sizeof(m_BMPInfoHeader.colorPlane));bmp_buffer += sizeof(m_BMPInfoHeader.colorPlane);memcpy(bmp_buffer, &m_BMPInfoHeader.bitColor, sizeof(m_BMPInfoHeader.bitColor));bmp_buffer += sizeof(m_BMPInfoHeader.bitColor);memcpy(bmp_buffer, &m_BMPInfoHeader.zipFormat, sizeof(m_BMPInfoHeader.zipFormat));bmp_buffer += sizeof(m_BMPInfoHeader.zipFormat);memcpy(bmp_buffer, &m_BMPInfoHeader.realSize, sizeof(m_BMPInfoHeader.realSize));bmp_buffer += sizeof(m_BMPInfoHeader.realSize);memcpy(bmp_buffer, &m_BMPInfoHeader.xPels, sizeof(m_BMPInfoHeader.xPels));bmp_buffer += sizeof(m_BMPInfoHeader.xPels);memcpy(bmp_buffer, &m_BMPInfoHeader.yPels, sizeof(m_BMPInfoHeader.yPels));bmp_buffer += sizeof(m_BMPInfoHeader.yPels);memcpy(bmp_buffer, &m_BMPInfoHeader.colorUse, sizeof(m_BMPInfoHeader.colorUse));bmp_buffer += sizeof(m_BMPInfoHeader.colorUse);memcpy(bmp_buffer, &m_BMPInfoHeader.colorImportant, sizeof(m_BMPInfoHeader.colorImportant));bmp_buffer += sizeof(m_BMPInfoHeader.colorImportant);
}void rgb565tobmp(char *rgb_buffer, unsigned short nWidth, unsigned short nHeight)
{    char bits = BMP_BITS / 8;int i = 0;int j = 0;unsigned char R,G,B;unsigned short RGB555,RGB565;char *rgb_buff;rgb_buff = rgb_buffer;rgb_buffer += 54;/* RGB565转RGB555 */for (i = 0; i < nHeight; i++){for (j = 0; j < nWidth; j++){/* 读取RGB565 */RGB565 = (*(rgb_buffer+1)<<8 | *rgb_buffer);/* 分别提取R、G、B数据 */B = RGB565 & 0x001f;G = (RGB565 >> 6) & 0x001f;R = (RGB565 >> 11) & 0x001f;/* 转换成RGB555数据 */RGB555 = (R << 10) | (G << 5) | (B);/* 写入数组 */*rgb_buffer = RGB555;*(rgb_buffer+1) = RGB555 >> 8;rgb_buffer += 2;}}/* 将BMP文件头和信息头写入数组 */RGBtoBMP(rgb_buff, nWidth, nHeight, bits);}

rgb2bmp.h:

//rgb2bmp.h文件
#include <stdio.h>#define BMP_BITS 16typedef unsigned char  BYTE;
typedef unsigned short WORD;
// BMP图像各部分说明如下
/***********第一部分    位图文件头
该结构的长度是固定的,为14个字节,各个域的依次如下:2byte   :文件类型,必须是0x4d42,即字符串"BM"。4byte   :整个文件大小4byte   :保留字,为04byte   :从文件头到实际的位图图像数据的偏移字节数。
*************/
typedef struct
{    long imageSize;long blank;long startPosition;
}BmpHead;/*********************
第二部分    位图信息头
该结构的长度也是固定的,为40个字节,各个域的依次说明如下:4byte   :本结构的长度,值为404byte   :图像的宽度是多少象素。4byte   :图像的高度是多少象素。2Byte   :必须是1。2Byte   :表示颜色时用到的位数,常用的值为1(黑白二色图)、4(16色图)、8(256色图)、24(真彩色图)。4byte   :指定位图是否压缩,有效值为BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS。Windows位图可采用RLE4和RLE8的压缩格式,BI_RGB表示不压缩。4byte   :指定实际的位图图像数据占用的字节数,可用以下的公式计算出来:图像数据 = Width' * Height * 表示每个象素颜色占用的byte数(即颜色位数/8,24bit图为3,256色为1)要注意的是:上述公式中的biWidth'必须是4的整数倍(不是biWidth,而是大于或等于biWidth的最小4的整数倍)。如果biCompression为BI_RGB,则该项可能为0。4byte   :目标设备的水平分辨率。4byte   :目标设备的垂直分辨率。4byte   :本图像实际用到的颜色数,如果该值为0,则用到的颜色数为2的(颜色位数)次幂,如颜色位数为8,2^8=256,即256色的位图4byte   :指定本图像中重要的颜色数,如果该值为0,则认为所有的颜色都是重要的。
***********************************/
typedef struct{long    Length;long    width;long    height;WORD    colorPlane;WORD    bitColor;long    zipFormat;long    realSize;long    xPels;long    yPels;long    colorUse;long    colorImportant;/*  void show(){    printf("infoHead Length:%dn",Length);printf("width&height:%d*%dn",width,height);printf("colorPlane:%dn",colorPlane);printf("bitColor:%dn",bitColor);printf("Compression Format:%dn",zipFormat);printf("Image Real Size:%dn",realSize);printf("Pels(X,Y):(%d,%d)n",xPels,yPels);printf("colorUse:%dn",colorUse);    printf("Important Color:%dn",colorImportant);}*/
}InfoHead;
/***************************第三部分    调色盘结构  颜色表对于256色BMP位图,颜色位数为8,需要2^8 = 256个调色盘;对于24bitBMP位图,各象素RGB值直接保存在图像数据区,不需要调色盘,不存在调色盘区rgbBlue:   该颜色的蓝色分量。rgbGreen:  该颜色的绿色分量。rgbRed:    该颜色的红色分量。rgbReserved:保留值。
************************/
typedef struct
{         BYTE   rgbBlue;BYTE   rgbGreen;BYTE   rgbRed;BYTE   rgbReserved;/*   void show(void){printf("Mix Plate B,G,R:%d %d %dn",rgbBlue,rgbGreen,rgbRed);}*/
}RGBMixPlate;void rgb565tobmp(char *rgb_buffer, unsigned short nWidth, unsigned short nHeight);

添加代码上传摄像头数据

准备好了转换工作,就可以将BMP传输到ONENET平台上了:

  • 定位net_io.c文件第374行,添加头文件:
#include "dcmi.h"
  • 修改TIM3_IRQHandler中断函数:
void TIM3_IRQHandler(void)
{//清中断标识TIM_ClearFlag(TIM3, TIM_FLAG_Update);//---------------- 中断处理  ------------------//if(oneNetInfo.netWork == 1){//修改添加判断条件,在上传的照片过程中等待if (oneNetInfo.sendData != SEND_TYPE_PICTURE){net_send_time++;}if (net_send_time == 1){oneNetInfo.sendData = SEND_TYPE_DATA;LED2_TOGGLE;}else if ((net_send_time % 25) == 0)   //每25s发送心跳请求{oneNetInfo.sendData = SEND_TYPE_HEART;LED2_TOGGLE;}else if (net_send_time == NET_TIME_DELAY/2){//修改添加判断条件,只有在空闲的时候在启动摄像头,否则等待if (oneNetInfo.sendData == SEND_TYPE_OK){//启动摄像头拍照,等待DMA传输DCMI_Start();LED2_TOGGLE;}}else if (net_send_time == NET_TIME_DELAY){net_send_time = 0;}}LED1_TOGGLE;OneNet_Check_Heart();
}
  • 定位dcmi.c文件,添加头文件:
#include "onenet.h"
  • 定位dcmi.c文件第78行,在DMA2_Stream1_IRQHandler中断中置位上传图片标志:
oneNetInfo.sendData = SEND_TYPE_PICTURE;
  • 定位main函数,在135行中添加如下函数:
             //....case SEND_TYPE_PICTURE://新添加
#ifdef OV7670_DBGShanWai_SendCamera(camera_buffer+27, PIC_WIDTH, PIC_HEIGHT);
#endifrgb565tobmp((char *)camera_buffer, PIC_WIDTH, PIC_HEIGHT);oneNetInfo.sendData = OneNet_SendData(FORMAT_TYPE2, NULL, NULL, NULL, 0);printf("\r\nOnenet Picture Ready.\r\n");//....
  • 定位onenet.c,添加头文件:
#include "ov7670.h"
  • 定位onenet.c第211行,修改替换OneNet_SendData_Picture参入函数如下:
OneNet_SendData_Picture(devid, (char *)camera_buffer, 320*200*2+54);
  • 优化onenet驱动部分函数,定位EdpKit.c第369的EDP_PacketSaveData函数,将第二个参数从int16型改为int32,防止出现图片过大而出现解析问题导致上传不了的问题:
uint8 EDP_PacketSaveData(const int8 *devid, int32 send_len, int8 *type_bin_head, SaveDataType type, EDP_PACKET_STRUCTURE *edpPacket)
  • 同样,在EdpKit.h第112行如上修改成一样:
uint8 EDP_PacketSaveData(const int8 *devid, int32 send_len, int8 *type_bin_head, SaveDataType type, EDP_PACKET_STRUCTURE *edpPacket);
  • 定位EdpKit.c第372行的remain_len变量,同样改为int32型:
 int32 remain_len = 0;
  • 编译,通过后烧写到stm32f407中,观察串口打印,是否上传照片工程:
  • 登录onenet平台查看数据,是否有照片数据上传,如图:

ps:若是发现上传的照片有垂直,水平颠倒,可以定位ov7670config.h文件第82行,0x1E寄存器:

{0x1e, 0x27},//0x07不翻转;0x17垂直翻转;0x27水平翻转;0x37水平垂直翻转

ONENET修改应用加入照片

进入自己onenet设备的应用管理,编辑:

  • 选择元件库,图片;
  • 属性图片类型选择图片格式的数据流;
  • 数据流选择刚才上传照片的数据流;
  • 调整图片大小合适即可;
  • 点击发布即可;
    在自己的应用主页就可以看到自己的应用数据了,也可以把链接发送给别人查看,如图:

小结:关于摄像头上传数据到ONENET的代码中,有许多可以优化的地方,比如:
(1)可以把摄像头的启动函数DCMI_Start()放在红外外部中断里面,红外一感应到范围里面有人,就用摄像头拍摄下来。
(2)现在用的是WIFI的UART接口,速度只有115200,传输图片需要10s左右之久,所以可以将WIFI改为SPI接口与STM32F407相连,可以大大地加快传输速率。
(3)想过用来传输视频流,但是stm32f407有点不够用,RAM也比较小,如果换成ARM,再用H.264压缩,可以大大降低数据传输压力,再用WIFI视频视频流的传输,初步设想,后期若是视频,再更新博客。

以上移植基本上就完成了,结合以前的数据,就已经把温湿度、红外感应数据以及摄像头照片数据都上传到ONENET平台了,可以达到一个简单的监控的目的了,但只是初步的实现,在调试过程中还存在一些不惹人注意的bug,所以需要耐心多次测试修改,加油,共勉~

家庭IOT监测之摄像头数据上传ONENET相关推荐

  1. 家庭IOT监测之摄像头OV7670测试

    本篇目标:使用STM32F407驱动摄像头OV7670,并上位机显示照片结果. 材料准备: STM32F4标准工程:stm32f407自建标准工程(stm32f4标准工程)(git仓库地址) STM3 ...

  2. 监测现场实时数据上传到中移物联网OneNet云平台

    OneNet,中移物联网云平台,是由中国移动打造的PaaS物联网开放平台.平台能够帮助开发者轻松实现设备接入与设备连接,快速完成产品开发部署,为智能硬件.智能家居产品提供完善的物联网解决方案(摘自36 ...

  3. 基于STM32的实时温湿度,蜂鸣器控制系统(温湿度,光敏数据上传 onenet(MQTT))

    一.开发环境搭建 云品台:onenet 协议支持:MQTT 硬件部署:STM32F407 ESP8266 DHT11 光敏传感器 二.部分源码展示(源程序太多了,MQTT协议支持和部分函数没有出现在这 ...

  4. 【AllJoyn专题】基于AllJoyn和Yeelink的传感器数据上传与指令下行的研究

    接触高通物联网框架AllJoyn不太久,但确是被深深地吸引了.在我看来,促进我深入学习的原因有三点:一.AllJoyn开源,对开源的软硬件总会有种莫名的喜爱,虽然或许不会都深入下去:二.顺应潮流,物联 ...

  5. C#工业物联网和集成系统解决方案的技术路线(数据源、数据采集、数据上传与接收、ActiveMQ、Mongodb、WebApi、手机App)

    2000年以后,互联网在中国的大地上如火如荼的发展,在这个行业竞争中比的是加速度.我清晰的记得<世界是平的>中有这样一段话:在非洲,羚羊每天早上醒来时,它知道自己必须跑得比最快的狮子还快, ...

  6. RaspberryPi+OneNET MQTT方式 数据上传和命令下发

    MQTT相关实验 第一节:Mosquitto 相关实验 第二节:域名解析和内网穿透实验 第三节:OneNET 相关实验 Gitee仓库地址: https://gitee.com/zhj0125/MQT ...

  7. 微信公众号H5开发——调用相册和摄像头并上传服务器

    之前的文章介绍了关于微信JSSDK调用,下面介绍一下其中的一个最常用的功能--调用相册和摄像头 使用此功能之前先参考:微信JSSDK 在通过了config接口验签成功之后,我们就可以调用微信JSSDK ...

  8. 掌控板结合Arduino实现数据上传阿里云

    硬件间串口通讯与阿里云传输 一.Arduino数据采集 1.硬件接线 接线: Arduino开发板-----光敏电阻传感器模块 VCC-----VCC GND-----GND A1--------AO ...

  9. C#工业物联网和集成系统解决方案的技术路线(数据源、数据采集、数据上传与接收、ActiveMQ、Mongodb、WebApi、手机App)...

    目       录 工业物联网和集成系统解决方案的技术路线... 1 前言... 1 第一章           系统架构... 3 1.1           硬件构架图... 3 1.2      ...

最新文章

  1. 求小数的小数点的第n位是什么
  2. Java基础:继承、多态、抽象、接口
  3. 播放图像有锯齿_反隔行操作
  4. python简单小游戏代码_一个简单的python小游戏---七彩同心圆
  5. React性能优化 PureComponent
  6. Django REST framework【学习内容】
  7. 统计范围内不含‘7‘的数字个数(洛谷P1590题题解,Java语言描述)
  8. 优秀的 jQuery 文本输入框自动完成 自动提示插件
  9. c语言交通灯程序闪烁,用C语言编写的交通灯程序
  10. 箱线图怎么判断异常值_异常数值识别(检测)
  11. 洛谷——P1320 压缩技术(续集版)
  12. ZFS 学习(转载)
  13. Atitit 数据库映射到redis存储的模式 1. 常规 每条db记录映射一个redis记录 1 1.1. 表名:主键为key ,记录序列化为json为val 1 2. 每个字段映射一个redi
  14. CAD卸载方法,如何完全彻底卸载删除清理干净CAD各种残留注册表和文件? 【转载】
  15. Unity经验分享——如何实现黑洞吸附效果
  16. ARM GIC(六) GIC V3 电源/功耗管理 分析笔记。
  17. 《BJBR虚拟仿真解决方案(描述精选)》(Yanlz+SteamVR+5G+AI+VR+AR+MR+HR+BR+CR+DR+ER+FR+GR+人工智能+人机交互+立钻哥哥+==)
  18. BTA前瞻 | 这家区块链公司币圈链圈通吃!专访井通科技CTO杨建新
  19. 超市选址c语言程序,谈谈超市选址的重要性
  20. 99%的手机Root方法都在这里

热门文章

  1. 指纹定位蓝牙打点工具
  2. jq点击事件实现防抖
  3. ERP项目团队激励困局的破解
  4. 如何通俗的理解最大似然估计法
  5. 数据集Advertising.csv——分析出广告媒体投入与销售额之间的关系
  6. 【MyCat】mycat简介
  7. 剑指Offer——“你最大的缺点是什么”回答技巧及范例
  8. 【DB2】How to resolve SQL20249N the statement was not processed with error
  9. 2020最新-精选基础算法100题(面试必备)
  10. python安装numba_Jetson Nano安装Numba遇到的一些坑