*本文已刊登在《无线电》2014年第6期
《搭建属于你的在线实时采集系统》中已经对HTML5平台有了初步的认识,并基于此向大家展示了如何将采集到的数据上传至网络,实现实时观测。HTML 5是近十年来Web开发标准最巨大的飞跃。想必你已经体会到了HTML5的便捷之处,为了帮助大家更好在此平台上拓展应用,本文更加深入的探析“智能家居”与HTML5的完美结合,让家庭网络实时监控轻松实现。
关于智能家居
2013年,太多公司包括三星和飞利浦在内都对智能家居动了念头,在CES的展览上三星展示了他们的新型智能清洁机器人,飞利浦的Hue智能LED灯泡,遭众多模仿的Nest的智能恒温器,结合智能控制、智能终端、智能显示的智能家居,将迎来更多创意。
所有这些,都让我们感受到智能家居已经脱离一种概念,进入到了下一个产品化阶段。但从用户的角度出发,更多人还是希望体验智能家居最基本的安全保障,便捷、经济、实用依然是多数人关注的主题,所以家庭远程监护无疑得到更多青睐,特别是家中有老人、孩子,或者是你的宠物,发生了紧急状况,怎样才能第一时间得知情况并避免事故的发生?
图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幅,如此我们就可以实现视频的观看了。
网络摄像头系统演示
- 系统环境
a) 单片机:STM32F103RC,256K字节Flash,48K字节SRAM,2K字节EEPROM
b) 以太网控制器:W5500,SPI接口与单片机相连
c) 电源:USB供电
d) 硬件外设:OV2640摄像头
图2 W5500EVB与OV2640摄像头连接图
- 开发工具: IAR for ARM v5.41,这是我们工程所使用的版本。如果使用不同版本的IAR,请对STM的库稍作调整。
看代码之前,我们还是先来了解一下整个的程序流程,流程图由一个主流程图和两个子流程图组成。如下图所示,程序采用轮询机制来处理请求和图像数据。在硬件初始化完成之后,将进行网络参数配置,这是要根据自己网络的情况来配置W5500的IP地址等网络参数,确保W5500能连接外网;本程序中,我们会使用W5500的两个socket资源,一个用来创建Http Server,这样在浏览器上输入配置的IP地址,浏览器作为客户端就能远程访问我们的硬件了;另一个用来创建Web Socket Server,与网页端建立通信链路,用来传输我们的摄像头图像数据。
图3 系统主流程图
图4a Http处理函数流程图 图4b WebSocket处理函数流程图
当我们在浏览器上访问硬件的IP地址,会向W5500发送http请求,W5500在收到请求后将html5的网页程序发送给浏览器,在网页程序中,浏览器会主动与硬件建立WebSocket连接。在完成握手操作后,图像传输的数据通道即建立了。这样每当硬件轮询到新的图像准备好后,就会通过WebSocket将图像数据发送给浏览器,浏览器收到数据后,在界面上绘制图像。实际效果图请见图5。接下来将对摄像头的初始化和图像数据缓存程序,html5中在画布上绘制图像的程序以及WebSocket数据传输程序做详细介绍。
图5 系统浏览器端效果图
图6 系统实际工作情况
OV2640介绍
1.OV2640硬件简介
图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 系统硬件电路连接简图
其中,Y(2..9)为8位MSB(Most Significant Bit,最高有效位模式)并行总线,SDIO、SCLK为SCCB接口,PCLK为像素时钟输出管脚(每个周期从并行总线上输出一个像素),VSYNC为列同步输出管脚(每帧图像发生一次跳变),HERF为行参考输出管脚(每个周期总线从并行总线上输出一行图像数据)。SVGA模式时序参考图如下:
图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(0×40); /*设置亮度模式:亮度+2*/
ov2640_auto_exposure(3); /*设置自动曝光等级
0-4*/
ov2640_contrast_config(0×28,0x0c); /*设置对比度:对比度+2*/
ov2640_black_white_config(0×00); /*设置黑白彩色模式:正常模式*/
ov2640_color_saturation(0×68,0×68); /*设置色饱和度:饱和度+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=0×82;//FIN=1,
opcode=0×02: binary
uint8secondByte=126;//no
mask, extend length=2 bytes
uint8 extend[2]={0×00};//extend
header
while(jpg_flag!=4);/*图像缓存完毕*/
jpgLen=JPEGCnt;
extend[0]=(jpgLen-4)/256;/*提取payload高8位*/
extend[1]=(jpgLen-4)%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
搭建属于你的家庭网络实时监控–HTML5在嵌入式系统中的应用·高级篇