移动物联网 之 家电节能 (2)

    本系列文章结合时下正热的“物联网”概念,介绍实现“家电节能”的一套解决方案。本部分讲述 “家电节能”的具体实现方法。

 

1. 系统结构

系统包括Sensor NodeAccess NodeServer这三个主要组成部分。各部分的主要功能如下:

a. Sensor Node

负责电量采集,包括电压、电流和功耗等物理量,将模拟量转换为数字量,传送给Access Node;同时,Sensor Node可以接收Access Node发送的控制信息,对设备进行控制。

b. Access Node

负责接收Sensor Node发送的信息,并将这些信息发送给Server;同时,Access Node可以接收Server的控制信息,转发给对应的Access Node

c. Server

    提供UI,负责参数采集命令,将接收到的数据存入本地数据库;接收用户对各Sensor Node的阈值设置和控制指令。同时,Server也提供接口,供其他互联网设备访问。

 

2. 系统实现

2.1 组网方式

基于目前短距离无线通信的现状,ZigbeeRF具有各自的技术特点。Zigbee通信距离中等,抗干扰能力强,性能可靠稳定;采用IEEE802.15.4,处于全球公用的2.4G频段;组网灵活、方便,星型、树型或者Mesh网络;低功耗,低复杂度;多信道、多速率。RF通信距离远,穿透性好,抗干扰能力强;微发射功率,微功耗。 因此,我们使用了基于ZigbeeRF射频的家电节能整体解决方案。系统采用分布式网络,底层电量采集使用RF射频通信,上层使用Zigbee进行组网。由于Zigbee的穿透能力不强,所以在部署的时候,尽量将Zigbee模块放在视距范围内,不要有墙体阻隔。

 

从网络规模上来看,可以分为小型网络和中大型网络这两种。在小型网络中,主机只需要插座的地址就可以与插座通信;Zigbee采用广播通信方式,实现主机与插座信息的透明传输;整个网络最多包含254个插座。 在中大型网络中,采用主从通信模式,以Zigbee节点作为中继,实现主机和插座之间的信息传递;每个Zigbee节点可以包含254个插座,而整个网络可以包含多个Zigbee节点组成的子网络。

 

2.2 Sensor NodeAccess Node

Sensor Node负责电量采集,内部包括AD模块,将模拟量转换为数字量,通过无线的方式传送给Access NodeAccess Node起到一个透明传输的作用,将信息传送给Server端。由于目前大多数家电的控制接口都不公开,因此,比较通用的解决方法是将Sensor Node嵌于插座中,通过插座来检测用电情况。

2.3 Server

Server可以采用成本比较低的嵌入式设备,也可以采用PC机。在我们的项目中,我们使用了PC机,利用PC机的USB口和Access Node通信。需要说明的是,PC机的USB口是通过USB转串口模块和Access Node进行串口通信的。

Server通过串口发送数据采集指令,经Access Node转发给Sensor Node。得到数据以后,存入到本地Access数据库,然后通过曲线图,实时显示采集的数据量。界面如下:

关键代码如下:

打开串口部分

ExpandedBlockStart.gif 代码
             // Error Check
            StringBuilder strErrorMsg  =   new  StringBuilder();
            
if  (textBoxComNo.TextLength  ==   0 )
            {
                strErrorMsg.Append(
" 请输入串口号!\n " );
            }
            
if  (textBoxDataRate.TextLength  ==   0 )
            {
                strErrorMsg.Append(
" 请输入串口速率!\n " );
            }
            
if  (strErrorMsg.Length  >   0 )
            {
                MessageBox.Show(strErrorMsg.ToString(), 
" Error " );
                
return ;
            }

            
// State check
             if  ( ! PortOpen)
            {
                m_port 
=   " COM "   +   this .textBoxComNo.Text;
                m_serialPort 
=   new  System.IO.Ports.SerialPort(m_port, Int32.Parse( this .textBoxDataRate.Text.Trim()));
                m_serialPort.Parity 
=  Parity.None;
                m_serialPort.StopBits 
=  StopBits.One;
                m_serialPort.ReceivedBytesThreshold 
=   11

                interfaceUpdataHandle 
=     new  HandleInterfaceUpdataDelegate(ReceivedDataProcess);  

                m_serialPort.DataReceived 
+=   new  SerialDataReceivedEventHandler( this .m_serialPort_DataReceived);

                
try
                {
                    m_serialPort.Open();
                    
                }
                
catch  (Exception e1)
                {
                    System.Console.WriteLine(e1.Message);
                }

                PortOpen 
=   true ;
                
this .buttonOpenCom.Text  =   " 关闭串口 " ;
            }
            
else
            {
                
if  (StartInt)
                {
                    MessageBox.Show(
" 请先停止采集! " " Error " );
                    
return ;                                
                }
                m_serialPort.Close();             
                PortOpen 
=   false ;
                
this .buttonOpenCom.Text  =   " 打开串口 " ;
            }

 

串口数据处理部分

ExpandedBlockStart.gif 代码
             string  str  =  ByteArrayToHexString(message);
            
// check to claer the message window
             if  (listcounter  ==   23 )
            {
                listcounter 
=   0 ;
                
this .textBoxComMessage.Text  =   "" ;
            }
            
// display on message window
             this .textBoxComMessage.Text  +=   " RX: "   +  str  +   " \r\n " ;
            listcounter
++ ;
            
if  (message[ 2 ==   6 )
            {
                str 
=  BitConverter.ToString(message).Replace( " - " "" );
                pSave.Number 
=  Hex2Ten(str.Substring( 0 2 ));
                pSave.Voltage 
=  Hex2Ten(str.Substring( 6 4 ))  +   " 0 " ;
                pSave.Current 
=  Hex2Ten(str.Substring( 10 4 ));
                pSave.Power 
=  Hex2Ten(str.Substring( 14 4 ));
            }
            
else   if  (message[ 2 ==   4 )
            {
                
// add to database         
                str  =  BitConverter.ToString(message).Replace( " - " "" );
                
string  stemp  =  Hex2Ten(str.Substring( 6 8 ));
                
int  itemp  =  Int32.Parse(stemp);
                
float  b  =   3200 ;
                
float  ftemp  =  ( float )itemp  /  b;
                pSave.Consum 
=  ftemp.ToString();
                pSave.Infotime 
=  System.DateTime.Now;
                
int  id  =  ProductDao.insert(pSave);
                
// 统计数据库数据并显示
                Page page  =  PageQueryDao.getProducts( 0 );
                
this .labelMsg.Text  =  String.Format( " 记录数: {0:d}, \n每页{1:d}条记录, {2:d}/{3:d}页 " ,
                                page.TotalRecord, page.PageSize, page.CurPageIndex 
+   1 , page.TotalPage);
                totalPage 
=  page.TotalPage;

                DisplayChart(pSave);
            }

 

 

图像实时显示部分采用ZedGraphZedGraph是用于创建任意数据的二维线型、条型、饼型图表的一个类库,也可以作为Windows窗体用户控件和Asp.Net网页控件。这个类库具有高度的适应性,几乎所有式样的图表都能够被创建。这个类库的用法在于通过提供所有图表属性的省缺值来保持使用性的简单。这个类库包含了基于要绘制的数值范围内的可选择适当度量范围和跨度的代码。关于如何使用ZedGraph,可以参考园子里peterzbBlogC# WinForm开发系列 – ZedGraph

 

为了便于网络上其他设备对Server的数据访问,Server端程序每隔一定时间将数据通过socket发送到其他设备。

 

Server端关键代码:

ExpandedBlockStart.gif 代码
public       void    OnTimedEvent( object    source,   ElapsedEventArgs   e) 
        {
            mdbget();
            BeginSend();
        }

        
public   void  send_Click( object  sender, EventArgs e)
        {
            BeginSend();
        }
        
public    void  BeginSend()
        {

            
string  ip  =   " 127.0.0.1 " ;;

            
string  port  =   " 2000 " ;

            IPAddress serverIp 
=  IPAddress.Parse(ip);

            
int  serverPort  =  Convert.ToInt32(port);

            IPEndPoint iep 
=   new  IPEndPoint(serverIp, serverPort);

            
byte [] byteMessage;

         

            Socket socket 
=   new  Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            socket.Connect(iep);

            byteMessage 
=  Encoding.GetEncoding( " gb2312 " ).GetBytes(sendtext);
          
            socket.Send(byteMessage);

            socket.Shutdown(SocketShutdown.Both);

            socket.Close();

        }

        
private   void  getmdb_Click( object  sender, EventArgs e)
        {
            
this .sensorTableAdapter.Fill( this .sensorDataSet.sensor);
            mdbget();
           
           

        }
        
public   void  mdbget()
        {
           
            String connectionString 
=   " Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\\debug\\sensor.mdb " ;
            OleDbConnection connection 
=   new  OleDbConnection(connectionString);
            connection.Open();
            
string  sql  =   " select * from sensor " ;
            OleDbCommand cmd 
=   new  OleDbCommand(sql, connection);
            OleDbDataReader rs 
=  cmd.ExecuteReader();
            
int  i  =   0 ;
            sendtext 
=   "" ;
            
            
while  (rs.Read())
            {
                i
++ ;

                sendtext 
+=  (rs[ 0 ].ToString()  +   " , "   +  rs[ 1 ].ToString()  +   " , "   +  rs[ 2 ].ToString()  +   " , "   +  rs[ 3 ].ToString()  +   " , "   +  rs[ 4 ].ToString()  +   " , "   +  rs[ 5 ].ToString()  +   " , "   +  rs[ 6 ].ToString()  +   " " );
            }
           
            sendtext 
=  i.ToString()  +   " "   +  sendtext.ToString();
            rs.Close();
            connection.Close();
            
        }

        
private   void  client_Load( object  sender, EventArgs e)
        {
            
//  TODO: 这行代码将数据加载到表“sensorDataSet.sensor”中。您可以根据需要移动或移除它。
             this .sensorTableAdapter.Fill( this .sensorDataSet.sensor);
           
           
            System.Timers.Timer aTimer 
=   new  System.Timers.Timer( 30000 );    // 实例化Timer类,设置间隔时间为10000毫秒;
            aTimer.Elapsed  +=   new  ElapsedEventHandler(OnTimedEvent);    // 到达时间的时候执行事件
            
//    Only   raise   the   event   the   first   time   Interval   elapses.   
            aTimer.AutoReset  =   true ;   // 设置是执行一次(false)还是一直执行(true); 
            aTimer.Enabled  =   true ;    // 是否执行System.Timers.Timer.Elapsed事件; 
        }

 

其他设备端关键代码:

ExpandedBlockStart.gif 代码
rivate  void  server_Load( object  sender, EventArgs e)
        {
            openserver();
            System.Timers.Timer aTimer 
=   new  System.Timers.Timer( 30000 );    // 实例化Timer类,设置间隔时间为xx毫秒;
            aTimer.Elapsed  +=   new  ElapsedEventHandler(OnTimedEvent);    // 到达时间的时候执行事件
            
//    Only   raise   the   event   the   first   time   Interval   elapses.   
            aTimer.AutoReset  =   true ;   // 设置是执行一次(false)还是一直执行(true); 
            aTimer.Enabled  =   true ;    // 是否执行System.Timers.Timer.Elapsed事件;
        }
        
public   void  OnTimedEvent( object  source, ElapsedEventArgs e)
        {
            savedata();
        }
        
private   void  open_Click( object  sender, EventArgs e)
        {

            openserver();

           
        }
        
public   void  openserver()
        {
            open.Enabled 
=   false ;

            
try
            {

                mythread 
=   new  Thread( new  ThreadStart(BeginListen));

                mythread.Start();


            }

            
catch  (System.Exception er)
            {

                MessageBox.Show(er.Message, 
" 完成 " , MessageBoxButtons.OK, MessageBoxIcon.Stop);

            } 
        }
        
private   void  BeginListen()
        {
            
string  host  =   " 127.0.0.1 " ;
           

            IPAddress ServerIp 
=  IPAddress.Parse(host);

            IPEndPoint iep 
=   new  IPEndPoint(ServerIp, 2000 );

            socket 
=   new  Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            
byte [] recvBytes  =   new   byte [ 65536 ];
            

            
this .label1.Text  =  iep.ToString();

            socket.Bind(iep);


            
while  ( true )
            {

                
try
                {

                    socket.Listen(
5 );
                    Socket newSocket 
=  socket.Accept();
                    
int  bytes;
                    bytes 
=  newSocket.Receive(recvBytes, recvBytes.Length,  0 );
                    
string  sTime  =  DateTime.Now.ToShortTimeString();

                    
string  msg  =  sTime  +   " : "   +   " Message from: " ;

                    msg 
+=  newSocket.RemoteEndPoint.ToString() + " : "   + Encoding.GetEncoding( " gb2312 " ).GetString(recvBytes,  0 , bytes); //  Encoding.ASCII.GetString(recvBytes, 0, bytes);
                    textBox1.Text  =   "" ;
                    textBox1.Text 
=  Encoding.GetEncoding( " gb2312 " ).GetString(recvBytes,  0 , bytes);
                    
this .listBox1.Items.Add(msg);
                    

                }

                
catch  (SocketException ex)
                {

                    
this .label1.Text  +=  ex.ToString();

                }

            }

           

        }

        
private   void  save_Click( object  sender, EventArgs e)
        {
            savedata();
           
            
        }
        
public   void  savedata()
        {
            
string  info  =  textBox1.Text.ToString();
            
if  (info  !=   "" )
            {
               
                
string [] record  =  info.Split( ' ' );
                
int  i;
                
int  all  =   int .Parse(record[ 0 ].ToString());
                
// StreamWriter sw = new StreamWriter("data.txt", false);
                String connectionString  =   " Provider=Microsoft.Jet.OLEDB.4.0;Data Source=d:\\sensor.mdb " ;
                OleDbConnection connection 
=   new  OleDbConnection(connectionString);
                connection.Open();
                
string  sql;
                sql 
=   " delete * from sensor " ;
                OleDbCommand cmd 
=   new  OleDbCommand(sql, connection);
                cmd.ExecuteNonQuery();
              
for  (i  =   1 ; i  <=  all; i ++ )
                {
                    
string [] str  =  record[i].Split( ' , ' );
                    sql 
=   " insert into sensor  values ( "   +  str[ 0 +   " ,' "   +  str[ 1 +   " ',' "   +  str[ 2 +   " ',' "   +  str[ 3 +   " ',' "   +  str[ 4 +   " ',' "   +  str[ 5 +   " ',' "   +  str[ 6 +   " ') " ;
                    cmd 
=   new  OleDbCommand(sql, connection);
                    cmd.ExecuteNonQuery();
                }
                connection.Close();
               
//  MessageBox.Show("执行成功");
                
            }
           
/*  else
               MessageBox.Show("还没有数据,无法存储");
*/
        }

 

 

 

Server端软件视频已经上传至youku,地址:http://v.youku.com/v_show/id_XMTQwNDcwNDQ4.html

 

参考链接:

C# WinForm开发系列 – ZedGraph

http://baike.baidu.com/view/117166.htm

 

 

声明和致谢:本项目由北京邮电大学微软技术俱乐部成员施炯、曾阳和叶周全完成,感谢微软亚洲研究院对本项目的资金支持,感谢MSRA UR李贝、张静和王春晖。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值