搭建属于你的家庭网络实时监控–HTML5在嵌入式系统中的应用·高级篇

Home / 博客 / 搭建属于你的家庭网络实时监控–HTML5在嵌入式系统中的应用·高级篇

*本文已刊登在《无线电》2014年第6期

《搭建属于你的在线实时采集系统》中已经对HTML5平台有了初步的认识,并基于此向大家展示了如何将采集到的数据上传至网络,实现实时观测。HTML 5是近十年来Web开发标准最巨大的飞跃。想必你已经体会到了HTML5的便捷之处,为了帮助大家更好在此平台上拓展应用,本文更加深入的探析“智能家居”与HTML5的完美结合,让家庭网络实时监控轻松实现。

关于智能家居 

2013年,太多公司包括三星和飞利浦在内都对智能家居动了念头,在CES的展览上三星展示了他们的新型智能清洁机器人,飞利浦的Hue智能LED灯泡,遭众多模仿的Nest的智能恒温器,结合智能控制、智能终端、智能显示的智能家居,将迎来更多创意。

所有这些,都让我们感受到智能家居已经脱离一种概念,进入到了下一个产品化阶段。但从用户的角度出发,更多人还是希望体验智能家居最基本的安全保障,便捷、经济、实用依然是多数人关注的主题,所以家庭远程监护无疑得到更多青睐,特别是家中有老人、孩子,或者是你的宠物,发生了紧急状况,怎样才能第一时间得知情况并避免事故的发生?

 图1 图1 家庭监控系统示意图

(图片来自:http://dynews.zjol.com.cn/pic/0/11/43/44/11434423_934000.jpg

从此角度出发,我想到DIY一个家庭网络实时监控系统,用手机或电脑连接网络,就能看到家中各个角落的情况。今天我们介绍的内容就是,用一个摄像头 + 一块单片机就可以搞定的家居网络实时监控,搭载html5平台,实现更佳效果。

关于HTML5—WebSocket数据传输

网络实时访问,图像画质是否能够达到理想值呢?

以前的web界面在数据采集显示方面,都是采用的轮询方式,数据及时性不好,如果轮询过快,不但加大浏览器的负担,而且可能达不到你想要的效果。这次之所以选择HTML5,由于新增的websocket API函数,几乎完美的解决了实时性的问题。

那么介于我们是采用单片机,将采集到的数据上传至网页,所以可负载的数据量收到缓存大小的限制,为解决此问题, 我们将采集到的视频数据直接输出JPEG格式的图片,暂设播放速率设置为5帧/秒,即画面每秒切换5幅,如此我们就可以实现视频的观看了。

 

网络摄像头系统演示

  1. 系统环境

a) 单片机:STM32F103RC,256K字节Flash,48K字节SRAM,2K字节EEPROM

b) 以太网控制器:W5500,SPI接口与单片机相连

c) 电源:USB供电

d) 硬件外设:OV2640摄像头

图2

图2 W5500EVB与OV2640摄像头连接图

  1. 开发工具: IAR for ARM v5.41,这是我们工程所使用的版本。如果使用不同版本的IAR,请对STM的库稍作调整。

看代码之前,我们还是先来了解一下整个的程序流程,流程图由一个主流程图和两个子流程图组成。如下图所示,程序采用轮询机制来处理请求和图像数据。在硬件初始化完成之后,将进行网络参数配置,这是要根据自己网络的情况来配置W5500的IP地址等网络参数,确保W5500能连接外网;本程序中,我们会使用W5500的两个socket资源,一个用来创建Http Server,这样在浏览器上输入配置的IP地址,浏览器作为客户端就能远程访问我们的硬件了;另一个用来创建Web Socket Server,与网页端建立通信链路,用来传输我们的摄像头图像数据。

 图3

图3 系统主流程图

 图4a  图4b

图4 a Http处理函数流程图            图4b WebSocket处理函数流程图

 

当我们在浏览器上访问硬件的IP地址,会向W5500发送http请求,W5500在收到请求后将html5的网页程序发送给浏览器,在网页程序中,浏览器会主动与硬件建立WebSocket连接。在完成握手操作后,图像传输的数据通道即建立了。这样每当硬件轮询到新的图像准备好后,就会通过WebSocket将图像数据发送给浏览器,浏览器收到数据后,在界面上绘制图像。实际效果图请见图5。接下来将对摄像头的初始化和图像数据缓存程序,html5中在画布上绘制图像的程序以及WebSocket数据传输程序做详细介绍。

图5

图5 系统浏览器端效果图

 图6

图6 系统实际工作情况

OV2640介绍

1.OV2640硬件简介

图7

图7 OV2640摄像头

OV2640像素输出最大支持200万像素,支持QCIF(176*144)、QVGA(320*240)、VGA(640*480)、1027*768、1600*1200等像素输出。

 

具备两种输出格式:

a、原始数据如RGB565,RGB RAW,YUV422;

b、JPEG压缩图像格式(可极大减少传输带宽,例如640*480分辨率的原图片大小在300KB左右, JPEG编码输出后大小仅约为16KB)

在本系统中,由于使用原始数据格式,图像文件过大(以RGB RAW为例,一帧640*480的图像文件大小为640*480*3=900Kbytes),影响数据传输速率和更新图像的频率,进而影响浏览器的视频显示效果;其次stm32f103RBT6处理能力有限,无法做复杂的图像压缩算法,所以选择OV2640内部DSP压缩后的JPEG压缩图像格式是最好的选择。

MCU与OV2640的通信采用串行与并行结合,OV2640带有SCCB(Serial Camera Control Bus)双线串行接口,MCU通过SCCB接口配置和读取OV2640的信息;MCU通过并行总线的方式来接收OV2640的图像数据。系统的硬件连接图如下:

 图8

图8 系统硬件电路连接简图

 

其中,Y(2..9)为8位MSB(Most Significant Bit,最高有效位模式)并行总线,SDIO、SCLK为SCCB接口,PCLK为像素时钟输出管脚(每个周期从并行总线上输出一个像素),VSYNC为列同步输出管脚(每帧图像发生一次跳变),HERF为行参考输出管脚(每个周期总线从并行总线上输出一行图像数据)。SVGA模式时序参考图如下:

 图9

图9 OV2640 SVGA模式下图像输出时序图

系统上电后,MCU配置OV2640的工作方式,在OV2640准备好图像后,VSYNC会被拉高一段时间,MCU通过PCLK上升沿中断按字节接收图像数据。接下来我们将对OV2640的初始化配置程序和图像数据缓存程序进行介绍。

2.OV2640程序介绍

初始化配置程序:

iic_init();/*初始化MCU I2C_2,与OV2640 SCCB接口通信*/

ov2640_jpeg_config(JPEG_640x480); /*设置输出图像格式*/

/* 设置COMS参数 */

ov2640_brightness_config(0x40); /*设置亮度模式:亮度+2*/

ov2640_auto_exposure(3); /*设置自动曝光等级 0-4*/

ov2640_contrast_config(0x28,0x0c); /*设置对比度:对比度+2*/

ov2640_black_white_config(0x00); /*设置黑白彩色模式:正常模式*/

ov2640_color_saturation(0x68,0x68); /*设置色饱和度:饱和度+2*/

ov2640_light_mode_config(OFFICE); /*设置场景模式:办公室*/

o2640_capture_gpio_init();/*初始化并行传输IO管脚*/

注:以上代码在main.c文件中 

图像数据缓存程序:

u8 temp;

EXTI_ClearITPendingBit(EXTI_Line0);/*清除PC0(PCLK)中断*/

if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_1)==0)/*HREF管脚为低*/

return;

temp =(u8)((GPIOC->IDR)>>8&0x00ff);/*读取一个字节图像数据*/

switch(jpg_flag)

{

case0:

if(temp==0xff)/*图像数据以0xff 0xd8开头*/

{

JPEGBuffer[4]=0xff;

jpg_flag=1;

}

break;

case1:

if(temp==0xd8)

{

JPEGBuffer[5]=0xd8;

jpg_flag=2;

JPEGCnt=6;

}

elseif(temp!=0xff)

jpg_flag=0;

break;

case2:

JPEGBuffer[JPEGCnt++]= temp;/*存储数据*/

if(temp==0xff)

jpg_flag=3;

break;

case3:

JPEGBuffer[JPEGCnt++]= temp;/*图像数据以0xff 0xd9结尾*/

if(temp==0xd9)

{

jpg_flag=4;

counter++;

}

elseif(temp!=0xff)

jpg_flag=2;

break;

case4:

break;

}

注:以上代码在websocket.c文件中

在中断函数中通过以上缓存数据即可正确读取每一帧图像的数据了。其中JPEGBuffer为一个全局的图像缓存区,WebSocket数据发送函数中检测到缓存区数据准备完毕后,就可以将图像发送给浏览器了。

 

Canvas上绘制图片

Canvas API中有趣的一面就是对图片的支持,我们可以借助drawImage函数,通过多种方法操作图片,drawImage有三种格式:

n  drawImage(image, dx, dy):将image URL指定的图片显示在dx,dy位置

n  drawImage(image, dx, dy, dw, dh):根据提供的显示宽度(dw)和显示高度(dh)缩放显示图片

n  drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh):根据X,Y坐标和宽高(sx,sy,sw,sh)剪裁出图片的一部分显示出来。

下面开看看我们的网页程序里是如何实现绘制图片的吧。

1.首先建立一张画布

<p><canvasclass=‘img filter-drop-shadow’id=‘cam’alt=‘W5500照相机></canvas></p>

2.定义画布的边距、宽高等信息

.img{margin:0 auto;display:block;margin-bottom:10px; width:640px;height:480px;cursor:pointer;}

3.为了在JavaScript中对canvas进行绘制,首先需要通过目标canvas的id获取绘制环境。代码需要通过id获取canvas元素,然后使用此元素的getContext方法获取其二维绘制环境的引用。

var canvas =document.getElementById(‘cam’);

var context =canvas.getContext(‘2d’);

4.WebSocket接收到数据,并绘制图像

websocket.onmessage= function (evt)/*收到服务器消息,使用evt.data提取*/

{

var image = new Image();

image.onload= function ()

{

context.clearRect(0,0,canvas.width,canvas.height);/*清除画布矩形区域*/

context.drawImage(image,0,0,canvas.width,canvas.height);/*绘制宽度640px,高度480px的图像*/

}

image.src=URL.createObjectURL(evt.data);/*生成本图像数据的URL信息*/

}

可见我们使用了第二种drawImage格式,指定缩放大小使其与画布大小刚好匹配,而我们从OV2640获取的图像恰好与画布大小相同。

注:以上代码在webpage.c文件中

 

WebSocket数据传输程序

在上一期的《搭建属于你的在线实时采集系统》中,我们详细介绍了WebSocket的使用和API函数,并分析了握手流程。在本篇文章中将不再赘述,这里将对数据较大的传输程序进行介绍。

if(handshaked)/*握手成功*/

{

uint32jpgLen=0;

    uint32 send_len=0;

uint8firstByte=0x82;//FIN=1, opcode=0x02: binary

uint8secondByte=126;//no mask, extend length=2 bytes

uint8 extend[2]={0x00};//extend header

while(jpg_flag!=4);/*图像缓存完毕*/

jpgLen=JPEGCnt;

    extend[0]=(jpgLen4)/256;/*提取payload高8位*/

    extend[1]=(jpgLen4)%256;/*提取payload低8位*/

    /*打包websocket数据包*/

JPEGBuffer[0]=firstByte;

JPEGBuffer[1]=secondByte;

JPEGBuffer[2]=extend[0];

JPEGBuffer[3]=extend[1];

while(jpgLen)

{

if(jpgLen>WS_PACKET_LEN)/*长度大于W5500发送缓存区大小(4K默认)*/

{

send(s,(uint8*)(JPEGBuffer+send_len), WS_PACKET_LEN);

send_len+= WS_PACKET_LEN;

jpgLen-= WS_PACKET_LEN;

}

else/*将数据包剩余的字节全部发送出去*/

{

send(s,(uint8*)(JPEGBuffer+send_len),jpgLen);

send_len+=jpgLen;

jpgLen-=jpgLen;

}

}

if(jpg_flag==4)/*发送完毕后,重置图像采集标志位*/

jpg_flag=0;

printf(“.%d\r\n”,send_len);/*调试口打印本次数据包长度*/

}

上一篇中,由于数据包长度较小只有7个字节,所以没有使用到扩展长度字节,由于OV2640采集到的图像经过压缩后,一帧图像的数据远大于125个字节,所以就需要用到扩展字节来表征数据长度,经过实测,本系统中OV2640在JPEG_480*640模式下,一帧图像大概12Kbytes左右,所以使用两个扩展字节(16位,最大可表示65535个字节)就可以了。根据上一篇数据包帧格式定义,当数据包第二个字节secondByte的后7位为126时表示使用2字节长度扩展,为127时表示使用8字节长度扩展。这里我们没有使用掩码,所以secondByte的第一位为0,在扩展字节后就是数据包的payload(图像数据)。在发送时,由于W5500每个socket有自己的收发缓存区,在设备初始化时可配置,默认设置4K,所以一帧大小为12K的图像是无法一次发送出去的,程序的后半部分就是将数据包切割分几次发送。W5500也再次展示它硬件协议栈易于使用,快速传输的优越性能。使远程监控轻松实现。

 

总结

于此,摄像头 + 单片机 + HTML5的组合完成了家庭网络监控的作品。其实很多创意和想法都是源于各种功能组合,非常乐意与你分享我们的程序,希望你能发挥自己的想象力,组合出更多新奇的web功能,体会DIY的价值与乐趣。

程序下载地址:http://wizwiki.net/forum/viewtopic.php?f=91&t=733