muggle 发表于 2013-7-2 22:00:45

麻瓜项目:家庭能源消耗可视化管理之“网络电能表”

本帖最后由 muggle 于 2013-9-1 09:43 编辑

家庭能源消耗管理,是指能够即时查看电力消耗数据,而且能够通过历史数据图表,实现能源消耗的可视化管理。国外的电力公司,一般都为用户提供丰富的可视化能源管理数据,也曾经有Google和Microsoft等互联网巨头参与其中。但是,国内这方面一直还是空白,电力公司的抄表数据只是收费用途,并不能提供家庭能源消耗的数据,很多家庭的跑冒滴漏浪费因此产生。

我曾经考虑了很久,也参考了一些国外的商业和开源物联网项目。从去年中期,开始确定要做想法一个网络电能表,其间经历了许多波折,终于在六月份完成了家庭电表数据上网,真正实现了家庭能源消耗的可视化管理。

试验项目链接:
http://www.lewei50.com/home/gatewaystatus/1117

微博推送的时点和日/周/月统计信息
http://weibo.com/u/1665750791

这是早在一月份分享的一个测试版本,当时只是一个插座的数据,而且遇到了数据转换的问题,终于在朋友的帮助下,克服了困难,但是也停顿了很久。
http://www.geek-workshop.com/thread-3178-1-1.html

目前实现的项目,技术并没有什么突破,只是将分流电阻取样的电量模块,改成了以互感器取样的电量模块。物联网数据上传部分,采用485-MODBUS接口连接到Arduino,最后由5100盾板将数据传送到乐为互联。

网络电量表预计有下面三个版本:
电量模块+485+Arduino+5100                      (有线网络版本)   2013.06.30已经实现
电量模块+485+Arduino+串口WIFI                (无线网络版本)   近期计划
电量模块+485+Arduino+GPRS                      (无线网络版本)   2013.07.18原型测试成功,有待套件优化

// 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);


/*
   open.lewei50.comsensorclient
*/



#include <SPI.h>
#include <Ethernet.h>
#include <LeweiClient.h>

#define USERKEY          "Your API Key" // replace your key here
#define LW_GATEWAY       "Your Gateway No."

LeWeiClient *lwc;


unsigned long lastConnectionTime = 0;          // last time you connected to the server, in milliseconds
boolean lastConnected = false;               // state of the connection last time through the main loop
const unsigned long postingInterval = 30*1000; //delay between updates to cosm.com


int pin = 8;
unsigned long duration;
unsigned long starttime;
unsigned long sampletime_ms = 30000;
unsigned long lowpulseoccupancy = 0;
float ratio = 0;
double concentration = 0;

void setup() {

   // start serial port:
   Serial.begin(4800);
   pinMode(8,INPUT);

delay(10000);               
   
   lwc = new LeWeiClient(USERKEY, LW_GATEWAY);

   starttime = millis();
}
/* filter program : 20130521 */
#define FILTERLEN 10

unsigned long Array_Average( unsigned long* Array,int length)
{
    int x;
    unsigned long returnVal;
    unsigned long result=0;
    for(x=0;x<length;x++)
    {
      result+=Array;
      Serial.print("result=");
      Serial.println(result);
    }
    returnVal=result/length;
    return returnVal;
}

unsigned long Filter1(unsigned long lowpulse)
{
static unsigned long sfiterArray;
static int sindex=0;
int x;
   Serial.println("filter1 begin:");
if(FILTERLEN>sindex)
{
      sindex++;
      Serial.println(sindex);
      sfiterArray=lowpulse;
         Serial.println("filter1 END");
      return lowpulse;
}
else
{
      for(x=0;x<FILTERLEN-1;x++)
      {
      sfiterArray=sfiterArray;
      }
      sfiterArray=lowpulse;
      for(x=0;x<FILTERLEN;x++)
      {
         Serial.println(sfiterArray);
      }
      Serial.println("Aver:");
       Serial.println(Array_Average(sfiterArray,FILTERLEN));
       Serial.println("filter1 END");
      return(Array_Average(sfiterArray,FILTERLEN));
      
}


}
/*END: filter program : 20130521 */

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

/* Modbus para */
int retval;
int data;
int tt;//int changed to unsigned int

void loop() {

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 = retval;
//                data++;
//                data=0xdead;
//                data = 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;
          Voltage= Voltage / 100;
float   Amp      = tt;
          Amp      = Amp / 1000;
int   Watt   = tt;
//long y=x0*65536+x1;
unsigned   int Kwhh = (unsigned int)tt;
//unsigned int Kwhh = (unsigned int)65535; //test maximum
unsigned   int Kwhl = (unsigned int)tt;
unsigned   long kwhA = (unsigned long) Kwhh *65536 + Kwhl;
//    unsignedlong kwhA = Kwhh <<16 + Kwhl;
float Kwh = kwhA;
Kwh = Kwh / 3200;
//    double Kwh= kwhA / 3200; //Kwh= kwh / 32;
//    int Kwh   = tt;
float Pf = tt;
      Pf = Pf / 1000;
float Cabon= tt;
      Cabon= Cabon / 1000;
      

Serial.print(Voltage);
Serial.print(Amp);
Serial.print(Watt);
Serial.print(Kwh);
Serial.print(Pf);
Serial.print(Cabon);


// 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);
      
   
      lwc->send();   
      delay(15000);
}

// 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 = slave;
packet = function;
start_addr -= 1;
packet = start_addr >> 8;
packet = start_addr & 0x00ff;
packet = count >> 8;
packet = count & 0x00ff;

//below test only
//      packet =0x01;
//      packet = 0x03;
//      packet = 0;
//      packet = 0x48;
//      packet = 0;
//      packet = 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 = temp_crc >> 8;
packet = temp_crc & 0x00FF;
packet = 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++) {
    //                Serial.print(query, HEX); //Orginal
    Serial.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(Serial.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(Serial.available()) {
    received_string = Serial.read();
    //                Serial.print(bytes_received);                     //only test
    //                Serial.print("-");                              //only test
    //                Serial.println(received_string);//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 != query));
//      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;
    recv_crc_lo = (unsigned) data;

    crc_received = data;
    crc_received = (unsigned) crc_received << 8;
    crc_received =
      crc_received | (unsigned) data;


    /*********** 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 != query) {
      response_length = 0 - data;
    }
}
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;
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 ???!!! data isn't already the byte count (number of registers * 2)?! */
    for (i = 0;
               i < (data * 2) && i < (raw_response_length / 2);
               i++) {

      /* shift reg hi_byte to temp */
      temp = data << 8;
      /* OR with lo_byte         */
      temp = temp | data;

      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;
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;

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;

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 = (unsigned char)byte_count;

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

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

return (ret);
}




本人知识有限,东拼西凑,朋友帮忙,希望能给大家抛砖引玉!

在这里,特别要感谢我的好朋友JingLi和LinZai,在“网络电量表”原型开发过程中的大力协助。


图片说明一下:第一张是“网络电能表”在乐联网的截图,第二张是家里电表的运行数字,第三张是使用到的互感器版本电量模块。

2013.09.01 更新程序代码

Cupid 发表于 2013-7-3 08:44:25

这个模块到底是个什么模块,有没有淘宝链接的说?

muggle 发表于 2013-7-3 08:54:13

Cupid 发表于 2013-7-3 08:44:25 static/image/common/back.gif
这个模块到底是个什么模块,有没有淘宝链接的说?

你去某宝,搜一下“立天电子工作室”,或者用网上很多的带数据接口的轨道电能表也成。

zwltanf 发表于 2013-7-3 10:58:58

这个是安装在家里入户总线上吗?

muggle 发表于 2013-7-3 12:15:03

zwltanf 发表于 2013-7-3 10:58
这个是安装在家里入户总线上吗?

这个是互感器版本,不需要动电表和主空开,只要将互感器穿入火线(也说可以任意一棵主线),然后接电即可。

muggle 发表于 2013-7-5 00:17:55

zwltanf 发表于 2013-7-3 10:58 static/image/common/back.gif
这个是安装在家里入户总线上吗?

现在有一朋友感兴趣,大家准备凑一起买一批模块一起玩。

zwltanf 发表于 2013-7-5 09:59:18

muggle 发表于 2013-7-5 00:17 static/image/common/back.gif
现在有一朋友感兴趣,大家准备凑一起买一批模块一起玩。

在哪里买?工作有比较忙,才看到

muggle 发表于 2013-7-5 11:37:41

zwltanf 发表于 2013-7-5 09:59:18 static/image/common/back.gif

在哪里买?工作有比较忙,才看到

你发站内信,给我你的联系方式,今天晚些联系你。
我这次用这个模块,发现一些安装问题,已经联系卖家延长互感器连线,位置可以更灵活。这次预订了十块,应该还有一两块没定死,有兴趣可以参与一起来玩。

chzhewl 发表于 2013-7-5 19:54:16

必须顶一下啊

muggle 发表于 2013-7-9 21:33:58

新浪微博的电量记录推送页面“网络电量表”已经开通,每天定期推送电量数据记录:
http://weibo.com/u/1665750791

raiseyu 发表于 2013-7-9 23:11:00

请问楼主,那个互感器模块哪里买的?多少钱?

飞翔的红猪 发表于 2013-7-10 09:58:14

这个模块是否经过安全认证?安装过程是否安全?

muggle 发表于 2013-7-10 12:11:18

raiseyu 发表于 2013-7-9 23:11:00 static/image/common/back.gif
请问楼主,那个互感器模块哪里买的?多少钱?

这个模块138元,可以上某宝搜立天电子。

muggle 发表于 2013-7-10 12:13:30

飞翔的红猪 发表于 2013-7-10 09:58:14 static/image/common/back.gif
这个模块是否经过安全认证?安装过程是否安全?

这个模块是才用互感器结构,安装相对方便,放置在空开下口即可。安全认证,倒没考虑过,几款模块用着还好,电量计量都是成熟方案。这只是自己使用感受,详细请咨询卖家。

muggle 发表于 2013-7-10 12:16:05

大家很多关注这个网络电量表,尤其是电量模块。最近定制改造了几只,比现在这款更方便安装,到手以后会给大家上图展示。
页: [1] 2
查看完整版本: 麻瓜项目:家庭能源消耗可视化管理之“网络电能表”