智能电表数据读写心得
1、布线方面
智能电表采用的是485通讯。如果单位的局域网布置得比较完整的话,建议使用串口转以太网模块,将数据接入以太网。这样做的好处是可以利用现有的网络布线,不用再重复布线。若是使用串口转以太网模块的话,那就需要在上位机上安装一个名为《虚拟VSPM虚拟串口服务器》的软件,它的功能是在将远端的TCP服务器虚拟成一个串口,这样一来,数据的读写跟使用485串口通讯是一样的。
虚拟VSPM虚拟串口服务器的下载地地址:
http://www.kinghwawin.com/DownloadShow.asp?ID=40。
串口转以太网络模块的地址:
http://item.taobao.com/item.htm?spm=a1z09.2.9.66.LxUUjT&id=10248910782&_u=rlmt59h151e。
2、程序方面
智能电表的数据读写需要遵循相应的通讯协议,这些通讯协议已经被列入了国家标准。目前比较常用的有两个标准:一是2007版本的,全称是《中华人民共和国电力行业标准 DLT645-2007多功能电能表通信协议》,一个是1997版本的。本文使用的是2007版本的通讯协议。
下面的代码的GetPowerConsumption()函数演示了如何读取总电量。
using System; using System.Collections.Generic; using System.Text; using System.IO.Ports; using System.Windows.Forms; using System.Diagnostics; public class ElectricityMeter { //****************************************************** 字 段 ************************************************************************** SerialPort _serialPort = new SerialPort(); Form _frm; private static readonly ElectricityMeter _instance = new ElectricityMeter(); //***************************************************** 事 件 *************************************************************************** //****************************************************** 属 性 *************************************************************************** //****************************************************** 构造函数 ******************************************************************* /// <summary> /// 私有构造函数,防止类被实例化 /// </summary> private ElectricityMeter() { } //****************************************************** 方 法 ******************************************************************* /// <summary> /// 只能通过CreateInstance方法来创建类的实例。单例模式 /// </summary> public static ElectricityMeter CreateInstance() { return _instance; } /// <summary> /// 打开设备 /// </summary> /// <param name="portName">串口号</param> /// <param name="frm">调用这个类的窗体。</param> public void Open( string portName, Form frm ) { try { // 初始化窗体对象 _frm = frm; _frm.FormClosing += new FormClosingEventHandler( _frm_FormClosing ); //初始化SerialPort对象 _serialPort.PortName = portName; _serialPort.BaudRate = 2400; // 请将设备的波特率设置为此。 _serialPort.DataBits = 8; _serialPort.StopBits = StopBits.One; _serialPort.Parity = Parity.Even; _serialPort.Open(); } catch( Exception e ) { MessageBox.Show( e.Message ); } } /// <summary> /// 关闭设备。 /// </summary> public void Close() { if( _serialPort.IsOpen == true ) { _serialPort.Close(); _serialPort.Dispose(); } } /// <summary> /// 获取耗电量 /// </summary> public Decimal GetPowerConsumption() { if( _serialPort.IsOpen == true ) { // 十六进制的命令字符串 string strCmd = "68 AA AA AA AA AA AA 68 11 04 33 33 33 33 AD 16"; // 转换为十六进制的字节数组 string[] strs = strCmd.Split( new char[] { ' ' } ); // 空格分组 byte[] cmdBytes = new byte[ strs.Length ]; // 转换为十进制的字节数组 for( int i = 0; i < cmdBytes.Length; i++ ) { cmdBytes[ i ] = Convert.ToByte( strs[ i ], 16 ); // 16进制转换为10进制 } _serialPort.Write( cmdBytes, 0, cmdBytes.Length ); System.Threading.Thread.Sleep( 500 ); // 500ms内应当有响应 byte[] resultBytes = new byte[ 21 ]; // 容量为21的字节数组 _serialPort.Read( resultBytes, 0, resultBytes.Length ); string n1 = Convert.ToString( resultBytes[ 18 ] - 51, 16 ); // 将十进制转成16进制的字符串 string n2 = Convert.ToString( resultBytes[ 17 ] - 51, 16 ); // 将十进制转成16进制的字符串 string n3 = Convert.ToString( resultBytes[ 16 ] - 51, 16 ); // 将十进制转成16进制的字符串 string n4 = Convert.ToString( resultBytes[ 15 ] - 51, 16 ); // 将十进制转成16进制的字符串 string resultString = n1 + n2 + n3 + "." + n4; return Decimal.Parse( resultString ); } else { throw new Exception( "串口没有打开" ); } } /// <summary> /// 在窗体关闭的时候关闭串口连接。 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void _frm_FormClosing( object sender, FormClosingEventArgs e ) { this.Close(); } }
2007版标准的下载地址:
http://wenku.baidu.com/view/b5e3e9d776a20029bd642d6d.html。