极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 14692|回复: 22

基于WIFI模块的家庭能耗监控搭建

[复制链接]
发表于 2013-8-14 13:45:08 | 显示全部楼层 |阅读模式
本帖最后由 chzhewl 于 2014-2-18 16:39 编辑

我在乐联网上的网络电表:
http://www.lewei50.com/u/g/3648
图片1.jpg

是不是很酷啊(怎么又是这句话~~{:soso_e112:})
有了数据后能做什么用呢? 咳咳~~ 听我慢慢道来
1.现实意义深厚,加强节能意识。
2.帮我抓出了家里的用电大号,一台不知道自动省电的老式电冰箱。
3.电器功率预警,将数据上传到乐联网,通过乐联网平台,当家里电器总功率达到一个数值时自动发送短信报警。
4. 5. 6. 此处省略1万字……
{:soso_e120:}

设备照片
电量模块,乐联网本次活动的详细信息:
http://www.lewei50.com/event/detail/1
图片6.jpg

电量模块和想用arduino+w5100或用乐联的E-KIT上传数据的方法在这里就不做详细介绍了, 可以参考虫哥(@瘦网虫)精彩分享:
http://www.geek-workshop.com/forum.php?mod=viewthread&tid=5649  
或者加入乐联网技术交流群 59162154 询问详细信息。
HLK-RM04 WIFI模块,乐联网之前做活动时送的

图片2.jpg
放入配电箱中
图片3.jpg
合上配电箱
图片7.jpg
可能由于备电箱整个是金属外壳的原因,合上盖后wifi信号非常差,就只好把天线放到外面了
上传方法:
上传时用到了网关,为什么不直接上传呢?大体思路是这样的:
网关向WIFI模块上的arduino 定时查询电量数据后上传到乐联网,使用网关是因为手头上刚好有这样的设备,其次电量模块那边的 arduino 只做数据提供,不做数据上传,今后如需要修改上传时间间隔、修改上传地址或在加入新功能时就不用打开备电箱修改arduino 上的程序了。{:soso_e112:}(网关是刷好openwrt 的路由器,也可以用树莓派,参见之前的文章 http://www.geek-workshop.com/thread-4950-1-1.html
当然也可以把Wifi 模块配置成client 模式,直接在arduino 上实现上传,这里就不做详细介绍了,如果你感兴趣可以改一下下面的 arduino 程序来实现,不过要记得分享哦。

1. 将HLK-RM04 WIFI模块设置成 Server模式
图片4.jpg
2. 烧写Arduino 上的程序,由于HLK-RM04 WIFI 已经把arduino 的串口给占用了,所以使用 SoftwareSerial 库同电量模块通讯。
[pre lang="arduino" line="1" file="dian.ino"]#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX


// LeWei AC Power Meter trail success2013.06.25
// LeWei AC Power Meter (ZongBiao60A)trail syccess 2013.06.30 18:50pm
// 4 Parameter: watt / kwh / Amp / Voltage / Pf

/* FIXME: not yet being used */
unsigned long interframe_delay = 2;  /* Modbus t3.5 = 2 ms */

/*
* preset_multiple_registers: Modbus function 16. Write the data from an
* array into the holding registers of a slave.
* INPUTS
* slave: modbus slave id number
* start_addr: address of the slave's first register (+1)
* reg_count: number of consecutive registers to preset
* data: array of words (ints) with the data to write into the slave
* RETURNS: the number of bytes received as response on success, or
*         0 if no bytes received (i.e. response timeout)
*        -1 to -4 (modbus exception code)
*        -5 for other errors (port error, etc.).
*/

int preset_multiple_registers(int slave, int start_addr,
int reg_count, int *data);

/*
* read_holding_registers: Modbus function 3. Read the holding registers
* in a slave and put the data into an array
* INPUTS
* slave: modbus slave id number
* start_addr: address of the slave's first register (+1)
* count: number of consecutive registers to read
* dest: array of words (ints) on which the read data is to be stored
* dest_size: size of the array, which should be at least 'count'
* RETURNS: the number of bytes received as response on success, or
*         0 if no valid response received (i.e. response timeout, bad crc)
*        -1 to -4 (modbus exception code)
*        -5 for other errors (port error, etc.).
*/

int read_holding_registers(int slave, int start_addr, int count,
int *dest, int dest_size);


void setup() {

   // start serial port:
   Serial.begin(115200);
   
     mySerial.begin(4800);
   
  delay(1000);               
}


int x=0; //simulated sensor output
int sampling=1;
int transfering=0;

/* Modbus para */
int retval;
int data[10];
int tt[30];  //int changed to unsigned int

void loop() {
   
   
if (Serial.available()) {
       char c = Serial.read();
                 if ( c=='g' ){
                      int i;     
                      /* example, this will write some data in the first 10 registers of slave 1  */
                      //                retval = preset_multiple_registers(1,1,10, data);
                    
                      //                data[0] = retval;
                      //                data[1]++;
                      //                data[8]=0xdead;
                      //                data[9] = 0xbeaf;
                      //                delay(500);
                      //int read_holding_registers(int slave, int start_addr, int count,int *dest, int dest_size);               
                      //                retval = read_holding_registers(2,1, 1,tt,6);      
                      retval = read_holding_registers(1, 0x49, 6, tt, 1); // 1:5,2:7,3:9
                      //                delay(1000);
                      //                Serial.print("receve flag=");               
                      //                Serial.println(retval);
                    
                    
                      int     Voltage  = tt[0];
                              Voltage  = Voltage / 100;
                      float   Amp      = tt[1];
                              Amp      = Amp / 1000;
                      int     Watt     = tt[2];
                      //long y=x0*65536+x1;
                      unsigned   int Kwhh = (unsigned int)tt[3];
                      //unsigned int Kwhh = (unsigned int)65535; //test maximum
                      unsigned   int Kwhl = (unsigned int)tt[4];
                      unsigned   long kwhA = (unsigned long) Kwhh *65536 + Kwhl;
                      //    unsigned  long kwhA = Kwhh <<16 + Kwhl;
                      float Kwh = kwhA;
                      Kwh = Kwh / 3200;
                      //    double Kwh  = kwhA / 3200; //Kwh  = kwh / 32;
                      //    int Kwh     = tt[4];
                      float Pf = tt[5];
                            Pf = Pf / 1000;
                      float Cabon  = tt[5];
                            Cabon  = Cabon / 1000;
                           
                    
                      Serial.print(Voltage);
                      Serial.print(",");
                      Serial.print(Amp);
                      Serial.print(",");
                      Serial.print(Watt);
                      Serial.print(",");
                      Serial.print(Kwh);
                      Serial.print(",");
                      Serial.print(Pf);
                      Serial.print(",");
                      Serial.print(Cabon);
                      Serial.println("");
                 }
               
      }

  
  

// 4 Parameter: watt / kwh / Amp / Voltage / Pf

//      lwc->append("T21", Kwh);
//      lwc->append("T22", Watt);
//      lwc->append("T23", Amp);
//      lwc->append("T24", Voltage);
//      lwc->append("T25", Pf);
//      lwc->append("06", Cabon);
}

// this method makes a HTTP connection to the server:


/****************************************************************************
* BEGIN MODBUS RTU MASTER FUNCTIONS
****************************************************************************/

//#define TIMEOUT 1000          /* 1 second */
#define TIMEOUT 10000          /* 10 second */
#define MAX_READ_REGS 125
#define MAX_WRITE_REGS 125
#define MAX_RESPONSE_LENGTH 256
#define PRESET_QUERY_SIZE 256
/* errors */
#define PORT_ERROR -5

/*
CRC

INPUTS:
buf   ->  Array containing message to be sent to controller.           
start ->  Start of loop in crc counter, usually 0.
cnt   ->  Amount of bytes in message being sent to controller/
OUTPUTS:
temp  ->  Returns crc byte for message.
COMMENTS:
This routine calculates the crc high and low byte of a message.
Note that this crc is only used for Modbus, not Modbus+ etc.
****************************************************************************/

unsigned int crc(unsigned char *buf, int start, int cnt)
{
  int i, j;
  unsigned temp, temp2, flag;

  temp = 0xFFFF;

  for (i = start; i < cnt; i++) {
    temp = temp ^ buf;

    for (j = 1; j <= 8; j++) {
      flag = temp & 0x0001;
      temp = temp >> 1;
      if (flag)
        temp = temp ^ 0xA001;
    }
  }

  /* Reverse byte order. */

  temp2 = temp >> 8;
  temp = (temp << 8) | temp2;
  temp &= 0xFFFF;

  return (temp);
}


/***********************************************************************
*
*      The following functions construct the required query into
*      a modbus query packet.
*
***********************************************************************/

#define REQUEST_QUERY_SIZE 6     /* the following packets require          */
#define CHECKSUM_SIZE 2          /* 6 unsigned chars for the packet plus   */
/* 2 for the checksum.                    */

void build_request_packet(int slave, int function, int start_addr,
int count, unsigned char *packet)
{
  packet[0] = slave;
  packet[1] = function;
  start_addr -= 1;
  packet[2] = start_addr >> 8;
  packet[3] = start_addr & 0x00ff;
  packet[4] = count >> 8;
  packet[5] = count & 0x00ff;

  //below test only
  //        packet[0] =0x01;
  //        packet[1] = 0x03;
  //        packet[2] = 0;
  //        packet[3] = 0x48;
  //        packet[4] = 0;
  //        packet[5] = 0x02;
}

/*************************************************************************
*
* modbus_query( packet, length)
*
* Function to add a checksum to the end of a packet.
* Please note that the packet array must be at least 2 fields longer than
* string_length.
**************************************************************************/

void modbus_query(unsigned char *packet, size_t string_length)
{
  int temp_crc;

  temp_crc = crc(packet, 0, string_length);

  packet[string_length++] = temp_crc >> 8;
  packet[string_length++] = temp_crc & 0x00FF;
  packet[string_length] = 0;
}



/***********************************************************************
*
* send_query(query_string, query_length )
*
* Function to send a query out to a modbus slave.
************************************************************************/

int send_query(unsigned char *query, size_t string_length)
{

  int i;

  modbus_query(query, string_length);
  string_length += 2;

  for (i = 0; i < string_length; i++) {
    mySerial.write(query); //JingLi
  }
  /* without the following delay, the reading of the response might be wrong
   * apparently, */
  delay(200);            /* FIXME: value to use? */

  return i;           /* it does not mean that the write was succesful, though */
}


/***********************************************************************
*
*      receive_response( array_for_data )
*
* Function to monitor for the reply from the modbus slave.
* This function blocks for timeout seconds if there is no reply.
*
* Returns:     Total number of characters received.
***********************************************************************/

int receive_response(unsigned char *received_string)
{

  int bytes_received = 0;
  int i = 0;
  /* wait for a response; this will block! */
  while(mySerial.available() == 0) {
    delay(1);
    if (i++ > TIMEOUT)
      return bytes_received;
  }
  delay(200);
  /* FIXME: does Serial.available wait 1.5T or 3.5T before exiting the loop? */
  while(mySerial.available()) {
    received_string[bytes_received] = mySerial.read();
    //                Serial.print(bytes_received);                       //only test
    //                Serial.print("-");                                //only test
    //                Serial.println(received_string[bytes_received]);  //only test
    bytes_received++;
    if (bytes_received >= MAX_RESPONSE_LENGTH)
      return PORT_ERROR;
  }   
  //Serial.print("bytes_received=");
  //Serial.println(bytes_received);
  return (bytes_received);
}


/*********************************************************************
*
*      modbus_response( response_data_array, query_array )
*
* Function to the correct response is returned and that the checksum
* is correct.
*
* Returns:     string_length if OK
*           0 if failed
*           Less than 0 for exception errors
*
*      Note: All functions used for sending or receiving data via
*            modbus return these return values.
*
**********************************************************************/

int modbus_response(unsigned char *data, unsigned char *query)
{
  int response_length;
  int i;
  unsigned int crc_calc = 0;
  unsigned int crc_received = 0;
  unsigned char recv_crc_hi;
  unsigned char recv_crc_lo;

  do {        // repeat if unexpected slave replied
    response_length = receive_response(data);
  }
  while ((response_length > 0) && (data[0] != query[0]));
  //      for (i = 0; i <response_length; i++) {           Serial.print(data);Serial.print("---");   Serial.println(query);}                       //only test

  if (response_length) {


    crc_calc = crc(data, 0, response_length - 2);

    recv_crc_hi = (unsigned) data[response_length - 2];
    recv_crc_lo = (unsigned) data[response_length - 1];

    crc_received = data[response_length - 2];
    crc_received = (unsigned) crc_received << 8;
    crc_received =
      crc_received | (unsigned) data[response_length - 1];


    /*********** check CRC of response ************/

    if (crc_calc != crc_received) {
      response_length = 0;
      //                       Serial.println("CRC erro");                       //only test
    }



    /********** check for exception response *****/

    if (response_length && data[1] != query[1]) {
      response_length = 0 - data[2];
    }
  }
  return (response_length);
}


/************************************************************************
*
*      read_reg_response
*
*      reads the response data from a slave and puts the data into an
*      array.
*
************************************************************************/

int read_reg_response(int *dest, int dest_size, unsigned char *query)
{

  unsigned char data[MAX_RESPONSE_LENGTH];
  int raw_response_length;
  int temp, i;

  raw_response_length = modbus_response(data, query);
  if (raw_response_length > 0)
    raw_response_length -= 2;

  if (raw_response_length > 0) {
    /* FIXME: data[2] * 2 ???!!! data[2] isn't already the byte count (number of registers * 2)?! */
    for (i = 0;
               i < (data[2] * 2) && i < (raw_response_length / 2);
               i++) {

      /* shift reg hi_byte to temp */
      temp = data[3 + i * 2] << 8;
      /* OR with lo_byte           */
      temp = temp | data[4 + i * 2];

      dest = temp;
    }
  }
  return (raw_response_length);
}


/***********************************************************************
*
*      preset_response
*
*      Gets the raw data from the input stream.
*
***********************************************************************/

int preset_response(unsigned char *query)
{
  unsigned char data[MAX_RESPONSE_LENGTH];
  int raw_response_length;

  raw_response_length = modbus_response(data, query);

  return (raw_response_length);
}


/************************************************************************
*
*      read_holding_registers
*
*      Read the holding registers in a slave and put the data into
*      an array.
*
*************************************************************************/

int read_holding_registers(int slave, int start_addr, int count,
int *dest, int dest_size)
{
  int function = 0x03;      /* Function: Read Holding Registers */
  int ret;

  unsigned char packet[REQUEST_QUERY_SIZE + CHECKSUM_SIZE];

  if (count > MAX_READ_REGS) {
    count = MAX_READ_REGS;
  }

  build_request_packet(slave, function, start_addr, count, packet);

  if (send_query(packet, REQUEST_QUERY_SIZE) > -1) {
    ret = read_reg_response(dest, dest_size, packet);
  }
  else {

    ret = -1;
  }

  return (ret);
}


/************************************************************************
*
*      preset_multiple_registers
*
*      Write the data from an array into the holding registers of a
*      slave.
*
*************************************************************************/

int preset_multiple_registers(int slave, int start_addr,
int reg_count, int *data)
{
  int function = 0x10;      /* Function 16: Write Multiple Registers */
  int byte_count, i, packet_size = 6;
  int ret;

  unsigned char packet[PRESET_QUERY_SIZE];

  if (reg_count > MAX_WRITE_REGS) {
    reg_count = MAX_WRITE_REGS;
  }

  build_request_packet(slave, function, start_addr, reg_count, packet);
  byte_count = reg_count * 2;
  packet[6] = (unsigned char)byte_count;

  for (i = 0; i < reg_count; i++) {
    packet_size++;
    packet[packet_size] = data >> 8;
    packet_size++;
    packet[packet_size] = data & 0x00FF;
  }

  packet_size++;
  if (send_query(packet, packet_size) > -1) {
    ret = preset_response(packet);
  }
  else {
    ret = -1;
  }

  return (ret);
}




[/code]
3. 编写网关上的程序(使用php,当然也可以使用python实现 )
[pre lang="php" line="1" file="updian.php"]<?php
function http( $url , $header = '', $dataStr = '' , $isPost = 0 )
{
                $httpInfo = array();
                $ch = curl_init();

                curl_setopt( $ch, CURLOPT_HTTP_VERSION , CURL_HTTP_VERSION_1_0 );
                //curl_setopt( $ch, CURLOPT_USERAGENT , $this->userAgent );
                curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT , 30 );
                curl_setopt( $ch, CURLOPT_TIMEOUT , 30 );
                curl_setopt( $ch, CURLOPT_RETURNTRANSFER , true );
                echo $header;
                curl_setopt( $ch,CURLOPT_HTTPHEADER,$header );
                if( $isPost )
                {
                        curl_setopt( $ch , CURLOPT_POST , true );
                        curl_setopt( $ch , CURLOPT_POSTFIELDS , $dataStr );
                        curl_setopt( $ch , CURLOPT_URL , $url );        
                }
                else
                {
                        curl_setopt( $ch , CURLOPT_URL , $url.'?'.$dataStr );        
                }

                $response = curl_exec( $ch );
                $httpCode = curl_getinfo( $ch , CURLINFO_HTTP_CODE );
                $httpInfo = array_merge( $httpInfo , curl_getinfo( $ch ) );
                curl_close( $ch );
                return $response;
}

$ip = "192.168.1.238";
$port = 8080;
$key = "c29c6ce...................";  //乐联用户KEY
$header = array("userkeykey");


//创建连接WIFI模块的socket
$socket = socket_create ( AF_INET, SOCK_STREAM, SOL_TCP )  and
                                (socket_connect (  $socket, $ip, $port ));

if ( $socket ){        
    //请求数据
        $r = socket_write ( $socket, 'g', 1 );
        $data = "";
        while(true){
                $ret = socket_recv($socket,$tmprecv,1,MSG_WAITALL);

                if ( $ret==0 || ord($tmprecv)==13 || ord($tmprecv)==10){
                        break;
                }

                $data .= $tmprecv;
                }

                //把点分制的数据打散成数组,方便使用
                $data  = explode(",", $data);

                if ( count($data)==6 &&  $data[0]>0 )
                {

                        $Voltage = $data[0];
                        $Amp = $data[1];
                        $Watt = $data[2];
                        $Kwh = $data[3];
                        $Pf = $data[4];
                        $Cabon = $data[5];

                        if ( ( (int)$Voltage )> 240 || ( (int)$Voltage )<210 ) $Voltage = 220;

                        //组合数据并上传
                        $postdata = '[
                                                   {"Name":"T21","Value":"' . $Kwh . '"},
                                                   {"Name":"T22","Value":"' . $Watt . '"},
                                                   {"Name":"T23","Value":"' . $Amp . '"},
                                                   {"Name":"T24","Value":"' . $Voltage . '"},
                                                   {"Name":"T25","Value":"' . $Pf . '"}
                                                 ]';
                        echo $postdata;

                        $tt = http("http://www.lewei50.com/api/V1/gateway/UpdateSensors/04",$header,$postdata,1);
                        echo $tt;
        }
        socket_close($socket);
}


?>[/code]

4. 创建 crontab 定时器,每1分钟上传一次数据
*/1 * * * * php-cgi /me/php/updia.php

最后秀一下在放入备电箱之前使用w5100+703n实现上传冰箱电量数据的照片,仅供参考{:soso_e100:}
图片5.jpg
回复

使用道具 举报

发表于 2013-8-14 13:53:01 | 显示全部楼层
必须沙发一下
回复 支持 反对

使用道具 举报

发表于 2013-8-14 14:00:49 | 显示全部楼层
顶一下           来自lewei的Deecon
回复 支持 反对

使用道具 举报

发表于 2013-8-14 14:33:20 | 显示全部楼层
nice,顶一个!
回复 支持 反对

使用道具 举报

发表于 2013-8-14 14:53:24 | 显示全部楼层
cool!         
回复 支持 反对

使用道具 举报

发表于 2013-8-24 10:55:15 | 显示全部楼层
无线的方式,很赞的额


***市电有风险,动电需谨慎***
回复 支持 反对

使用道具 举报

发表于 2013-8-27 13:04:42 | 显示全部楼层
回复 支持 反对

使用道具 举报

发表于 2013-8-31 08:54:24 | 显示全部楼层
无用兄的方案很赞{:soso_e179:} 这两天还在为软串,还是上两路硬串纠结呢,一看无用兄的代码,直接用算了,呵呵
回复 支持 反对

使用道具 举报

发表于 2013-9-9 15:31:55 | 显示全部楼层
请问下楼主,你的供电方式是怎样的啊?电池还是接的变压器供电啊?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-9-11 11:38:23 | 显示全部楼层
ogre_c 发表于 2013-8-31 08:54
无用兄的方案很赞 这两天还在为软串,还是上两路硬串纠结呢,一看无用兄的代码,直接用算了, ...

回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-9-11 11:39:12 | 显示全部楼层
指弯弯大侠 发表于 2013-9-9 15:31
请问下楼主,你的供电方式是怎样的啊?电池还是接的变压器供电啊?

使用1A手机充电器给arduino 和wifi 模块供电的.
回复 支持 反对

使用道具 举报

发表于 2013-9-17 00:49:40 | 显示全部楼层
高手,高手,高高手啊!总是走在时代的前沿啊,我才刚刚开始模仿。
回复 支持 反对

使用道具 举报

发表于 2013-10-17 12:52:42 | 显示全部楼层
赞一个 好想也弄个玩玩
回复 支持 反对

使用道具 举报

发表于 2013-11-29 10:58:18 | 显示全部楼层
请问下楼主,我用和你一样的电流检测模块,结果不知道怎么电路板上个电阻烧掉了。。楼主你有该模块的原理图吗?或者碰到过这样的情况吗?
回复 支持 反对

使用道具 举报

发表于 2013-12-5 14:28:39 | 显示全部楼层
把Wifi 模块配置成client 模式,直接在arduino 上实现上传 的实现。

http://bbs.yeelink.net/forum.php ... &extra=page%3D1
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|联系我们|极客工坊 ( 浙ICP备09023225号 )

GMT+8, 2019-8-22 07:33 , Processed in 0.061516 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表