muggle 发表于 2013-1-17 06:32:45

麻瓜项目:家庭能源管理(COSM物联网版本/乐联网测试版更新)

本帖最后由 muggle 于 2013-6-30 23:09 编辑

基于物联网的能源管理,筹备许久,终于动手了。过程之中,遇到很多困难,在朋友的帮助下顺利解决。

交流电量管理模块+Arduino Uno+W5100+485模块+2004LCD

程序参考:
https://sites.google.com/site/jpmzometa/arduino-mbrt/arduino-modbus-master
http://www.geek-workshop.com/thread-544-1-1.html


测试链接:
https://cosm.com/feeds/97970



破烂的程序,还没有优化整理,先贴出来,接受批评


/*                

History:
2013.01.16 First success with AC Meter Module & 485 MODUBUS
modebus_zxy_testAC_20130116ethernetCOSM

Modbus over serial line - RTU Master Arduino Sketch

By Juan Pablo Zometa : [email protected]
http://sites.google.com/site/jpmzometa/

These functions implement functions 3 and 16 (read holding registers
and preset multiple registers) of the Modbus RTU Protocol, to be used
over the Arduino serial connection.

This implementation DOES NOT fully comply with the Modbus specifications.

This Arduino adaptation is derived from the work
By P.Costigan email: [email protected] http://pcscada.com.au

These library of functions are designed to enable a program send and
receive data from a device that communicates using the Modbus protocol.

Copyright (C) 2000 Philip CostiganP.C. SCADA LINK PTY. LTD.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

The functions included here have been derived from the
Modicon Modbus Protocol Reference Guide
which can be obtained from Schneider at www.schneiderautomation.com.

This code has its origins with
[email protected] (http://www.pmcrae.freeserve.co.uk)
who wrote a small program to read 100 registers from a modbus slave.

I have used his code as a catalist to produce this more functional set
of functions. Thanks paul.

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

// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd( 8, 7, 6, 5, 3, 2);

//Ethernet & COSM
#include <ERxPachube.h>
#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEE, 0xFF, 0xED };
IPAddress ip(192,168,2, 120);

#define PACHUBE_API_KEY "xxxxxxxxxxxx" // your Cosm API key
#define PACHUBE_FEED_ID "97970" // your Cosm feed ID

ERxPachubeDataOut dataout(PACHUBE_API_KEY, PACHUBE_FEED_ID);

void PrintDataStream(const ERxPachube& pachube);

void setup()
{
      const int baudrate = 4800;
      if (baudrate <= 19200)
                interframe_delay = (unsigned long)(3.5 * 11 / baudrate);/* Modbus t3.5 */
      Serial.begin(baudrate);         /* format 8N1, DOES NOT comply with Modbus spec. */

// set up the LCD's number of columns and rows:
lcd.begin(20, 4);
// Print a message to the LCD.
lcd.setCursor(0, 0);
lcd.print("AC 485 Module");
delay(10000);               

// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);

        dataout.addData(0);
        dataout.addData(1);
        dataout.addData(2);
        dataout.addData(3);
        dataout.addData(4);
        dataout.addData(5);

}

/* example data */
int retval;
int data;
int tt;

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

// print the number of return byte:
lcd.setCursor(17, 0);
lcd.print("R");
lcd.print(retval);

// print the number of seconds since reset:
lcd.setCursor(0, 2);
lcd.print("                  ");
lcd.setCursor(0, 3);
lcd.print("                  ");

//lcd.println(tt;
//                for (i = 0; i < (retval-0); i++) {lcd.print(":"); lcd.print(tt);}                     //only test
//                for (i = 0; i <retval; i++) {Serial.print("---"); Serial.println(tt);}                     //only test

    int Voltage= tt; Voltage = Voltage / 100;
    float Amp      = tt; Amp = Amp / 1000;
    int Watt   = tt;
//long y=x0*65536+x1;
    int Kwhh = tt;
    int Kwhl = tt;
    unsignedlong kwhA = 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 C   = tt; C = C / 1000;


lcd.setCursor(0, 1);
lcd.print("V=");
lcd.print(Voltage);

lcd.setCursor(11, 1);
lcd.print("A=");
lcd.print(Amp);

lcd.setCursor(0, 2);
lcd.print("W=");
lcd.print(Watt);

lcd.setCursor(9, 2);
lcd.print("Kwh=");
lcd.print(Kwh);

lcd.setCursor(0, 3);
lcd.print("C=");
lcd.print(C);


lcd.setCursor(10, 3);
lcd.print("Pf=");
lcd.print(Pf);

// print the number of seconds since reset:
//lcd.setCursor(0, 3);
//lcd.print(millis()/1000);

        dataout.updateData(0, Voltage);
        dataout.updateData(1, Amp);
        dataout.updateData(2, Watt);
        dataout.updateData(3, Kwh);
        dataout.updateData(4, Pf);
        dataout.updateData(5, C);

        int status = dataout.updatePachube();

//        Serial.print("sync status code <OK == 200> => ");
//        Serial.println(status);

//        PrintDataStream(dataout);


delay(20000);               


}

void PrintDataStream(const ERxPachube& pachube)
{
        unsigned int count = pachube.countDatastreams();
        Serial.print("data count=> ");
        Serial.println(count);

        Serial.println("<id>,<value>");
        for(unsigned int i = 0; i < count; i++)
        {
                Serial.print(pachube.getIdByIndex(i));
                Serial.print(",");
                Serial.print(pachube.getValueByIndex(i));
                Serial.println();
        }
}

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




程序和硬件,慢慢整理以后,与大家分享。

erjiang 发表于 2013-1-17 08:46:29

这个值得赞一个!

youzitang 发表于 2013-1-17 09:27:37

哈哈 我也是把插座拆了 线接的继电器

ttyp 发表于 2013-1-17 09:30:55

交流电量管理模块哪买的?求地址

smfox10 发表于 2013-1-17 10:06:41

非常不错!应用到生活中来了!

星夜幻 发表于 2013-1-17 10:54:15

好东西呀,是怎么做的?

muggle 发表于 2013-1-17 11:30:23

本帖最后由 muggle 于 2013-1-17 12:11 编辑

ttyp 发表于 2013-1-17 09:30 static/image/common/back.gif
交流电量管理模块哪买的?求地址

淘宝的立天电子工作室,北京的一个卖家。

ttyp 发表于 2013-1-17 12:58:48

muggle 发表于 2013-1-17 11:30 static/image/common/back.gif
淘宝的立天电子工作室,北京的一个卖家。

LZ你是买的30A的么?夏天空调够用么?你为什么不用TTL而用485接口啊,arduino不是天生支持ttl的啊

zcbzjx 发表于 2013-1-17 17:32:52

建议直接把控制器弄到盒子中。用网线或者无线把数据传输出来。hoho

laoliu1982 发表于 2013-1-17 18:00:12

呵呵,这个在微博上见过。这回对上人了。

muggle 发表于 2013-1-17 19:29:46

laoliu1982 发表于 2013-1-17 18:00 static/image/common/back.gif
呵呵,这个在微博上见过。这回对上人了。

我会试验一下,把数据上传到lewei,也希望得到你们的帮助。我现在使用ERxPachube.h,很简单的推送数据到COSM,也就是“沧海笑1122”的例子“Arduino学习笔记:2560+W5100试验实时室温对Pachubbe.com推送”。

我刚想问怎么推送多个数据到lewei,看到了“Arduino+W5100 通过LeweiClient上传数据,不用再理解http了-乐联网应用”。这就太好了,可以轻松试验数据上传。

muggle 发表于 2013-1-17 19:35:07

ttyp 发表于 2013-1-17 12:58 static/image/common/back.gif
LZ你是买的30A的么?夏天空调够用么?你为什么不用TTL而用485接口啊,arduino不是天生支持ttl的啊

卖家的指标“最大电流可以达到16A”,空调包括柜机都够。我会接到热水器上面,也是同样的功率级别。

这个属于一次接入,需要改动线路,建议只用在独立电器,也就是插座之前,不建议用到入户总表一段。卖家有一款电流互感器,属于二次接入,适宜安置在入户总表或者总空开之间,因为不需要变动线路,卡紧电流传感器,随便接电即可。

电力线路,改造有风险,无经验勿入!!!

muggle 发表于 2013-1-17 19:36:14

zcbzjx 发表于 2013-1-17 17:32 static/image/common/back.gif
建议直接把控制器弄到盒子中。用网线或者无线把数据传输出来。hoho

我是准备用nRF桥接,把数据传过来,成本低廉而且相对隔离,安全有保证。

laoliu1982 发表于 2013-1-17 20:04:55

muggle 发表于 2013-1-17 19:29 static/image/common/back.gif
我会试验一下,把数据上传到lewei,也希望得到你们的帮助。我现在使用ERxPachube.h,很简单的推送数据到C ...

必须的,有问题随时联系

muggle 发表于 2013-1-17 20:09:40

ttyp 发表于 2013-1-17 12:58 static/image/common/back.gif
LZ你是买的30A的么?夏天空调够用么?你为什么不用TTL而用485接口啊,arduino不是天生支持ttl的啊

我不是专家,不过,懂行的朋友如是说:

485是差分的方式,不需要公共地。电平为正负0.2负。

TTL电平为0-5V,通讯时,需要一个公共地。
页: [1] 2 3
查看完整版本: 麻瓜项目:家庭能源管理(COSM物联网版本/乐联网测试版更新)