DL/T 645 是国内的电力数据传输协议,一些智能电表支持这个协议,淘宝上用这个关键词一搜索就能看到很多电表,一般都可以读取到电压、电流、有功功率等数据。装上这个电表,就可以实时监控家里的用电情况了。

首先上图,第一图是电压和功率状况,第二个图是日用电量。

每日用电量

实时电力消耗情况

本文从头开始,一步一步地说明我家里是如何使用这个电表搭建电力监控的。

# 硬件准备

首先我们需要一个支持 DL/T 645 协议的导轨电表,大概长这个样子。

电表

标准的导轨电表,可以直接装入配电箱。相对于普通电表,除了火零输入和输出以外,还多了两个端口,这两个端口就是通信端口。我的这个电表走的是 RS485 协议,所以我们还需要一个 RS485 转 TTL 的模块。还是淘宝,买一个 USB 口的 RS485 转 TTL 模块,插上电脑后就可以直接从通过串口进行 DL/T 645 通信了。

因为距离很短,我直接用两条铁氟龙线做 RS485 数据线,连屏蔽和双绞都不需要,直接接入弱电箱里的小服务器上。

从电表引出

使用 RS485 转 TTL 模块,通过 USB 接入小服务器

# DL/T 645 协议交互

DL/T 645 协议并不复杂,这里不细说,有兴趣的可以直接找规范原本。我自己用 C 写了一个通过 DL/T 645 读取数据的库,可以直接使用。地址位于: https://github.com/greensea/dlt645

根据实际测试的结果,如果要读取电压、电流、有功功率、频率、总电量这五个数据,耗时大概 700+ms,这速度其实并不快。于是,只好把采样频率设置为每秒一次了,这样在显示数据的时候也比较好处理。

用上面的库,额外写了一个小程序,每秒钟读取一次上述五个数据,然后把数据写入到一个临时文件中。

写入临时文件是有原因的,C 开发起来比较麻烦,拿到数据之后其实就可以用其他语言继续处理了,直接将数据写入临时文件,是一个最为方便的数据传递的方案。虽然说可以用比诸如共享内存、IPC 之类的方式,但这也太麻烦了,还不如直接用 C 把数据写入数据库呢。

# 数据保存

接下来就使用比较顺手的语言继续开发,这里我直接使用 go. 程序会每隔一秒读取一次临时文件,然后将数据保存到 MySQL 中。MySQL 中保存了时间戳以及上述五个测量数据。

数据库中的数据

# 数据展示

数据都有了,接下来的活对程序员来说就很轻车熟路了。以前我使用 netdata 来显示数据,但是用下来觉得 netdata 资源消耗太大,而且还要另写一个 python 脚本把数据喂给 netdata,而且 netdata 总是时不时出现奇怪的问题,后来就给换成 grafana 了。

展示电压、功率的变化情况还是很简单的,直接读取数据库,直接输出就完事了,但日用电量的显示就有一点小麻烦了。

如果我要显示最近 30 日内每日的用电量,按最简单的方法就是读取最近 30 天的数据,然后加加减减,但我们的采样频率是 1Hz,30 天的数据量高达 86400 * 30 = 2592000 条,如果是一个月的时间范围似乎还能接受,但如果要显示最近一年以来每个月的用电量,那这查询的数据量就太大了。

一个比较合理的方案是,读取每个月第一天的 00:00:00 的总用电量,然后做个减法,就可以得到每个月的用电量了。纯 SQL 语句很难实现这个查询,只能使用存储过程来实现。

存储过程没怎么写过,要上手的话还是有点麻烦,于是直接让 AI 写了一个例子,我略微调整之后就可以用了。AI 真方便。

 1BEGIN
 2    DECLARE i INT DEFAULT 0;
 3    DECLARE start_date DATE;
 4    DECLARE end_date DATE;
 5    DECLARE ts INT;
 6    DECLARE ee DOUBLE DEFAULT 0;
 7    
 8    SET start_date = DATE_SUB(CURDATE(), INTERVAL 11 MONTH);
 9    SET end_date = CURDATE();
10    
11    DROP TEMPORARY TABLE IF EXISTS monthly_e;
12    CREATE TEMPORARY TABLE IF NOT EXISTS monthly_e (
13        ts INT,
14        ts_readable DATETIME,
15        ee DOUBLE
16    );
17    
18
19    WHILE i < num_months DO
20    	SET ts = UNIX_TIMESTAMP(DATE_FORMAT(DATE_ADD(start_date, INTERVAL i+1 MONTH), '%Y-%m-01 00:00:00'));
21
22        -- 由于历史遗留问题,create_time 字段使用了不同的时间戳单位,这里只好用 IF 进行区分处理
23
24    	IF tname = 'b_longjing' THEN
25	    	SET ee = (SELECT `e` FROM b_longjing WHERE create_time < ts ORDER BY create_time DESC LIMIT 1);
26	    ELSEIF tname = 'b_fz' THEN
27	    	SET ee = (SELECT `e` FROM b_fz  WHERE create_time < ts * 1000 ORDER BY create_time DESC LIMIT 1);	    
28	    ELSEIF tname = 'b_fzsolar' THEN
29	    	SET ee = (SELECT `e` FROM b_fzsolar WHERE create_time < ts * 1000 ORDER BY create_time DESC LIMIT 1);	    	    
30	    END IF;
31    	
32        INSERT INTO monthly_e (ts, ts_readable, ee)
33        VALUES (ts, FROM_UNIXTIME(ts), ee);
34        
35        SET i = i + 1;
36    END WHILE;
37    
38    SELECT * FROM monthly_e;
39    
40    DROP TEMPORARY TABLE IF EXISTS mo nt hl y;
41END

# 其他

自从做好这个电力监测系统之后,发现了几个有趣的现象。

  • 本地居民用电跑着至少两套电网

众所周知, 一个电网内所有用户的频率都是相同的,你家里的交流电频率跟省委书记办公室里的交流电有着完全相同的频率1。一个地区的通常只有一套电网,一般情况下一个地区的所有用户的交流电频率是完全相同的。

我不仅在我家里部署了这套电力监测系统,还在另一套用于出租的房子那边部署了这套电力监测系统,结果发现两边的交流电频率居然完全不同,这说明我们本地用着至少两套电网。

不同的电网频率

从这频率曲线就可以看出,这绝非电表的误差,绿色的电网频率波动明显比蓝色电网更大,而蓝色电网的频率则非常稳定。猜测可能是绿色电网有更多的工业用户,或者绿色电网本身的容量比较小。

  • 电网容量不够啊

下面这张图是某个时间段绿色电网的频率变化,可以看到频率最低跌到了 49.41,已经跌出了标准范围(50Hz $\pm$ 0.5Hz)。

电网频率突降

其实这种电网频率突降的情况我发现过好多次,最近更是一天会出现几次。这就是典型的电网容量不足,网内突然出现用电大户,导致频率暴跌。

其实从电网频率变化中可以推断出很多东西,比如这张图上的频率变化,我跟 AI 进行了深入的探讨后,得出这次频率突降的过程可能是这样子的:

  1. 大型负荷开始启动,电网负荷持续增加,频率开始下降
  2. 大型负荷持续启动中,电网负荷持续快速增加,频率开始暴跌,最低至 49.41Hz
  3. 一次调频机组迅速投入,将频率拉回到 49.7Hz 左右
  4. 二次调频投入,将频率拉回至 49.9Hz 左右
  5. 负荷仍在持续增加,超出二次调频能力后,频率再次下跌
  6. 三次调频投入,迅速将频率上拉至最高 50.15Hz
  7. 随后频率基本稳定,不知道是电网输出功率稳住了,还是这大型负荷终于启动好了

图解电网频率突降

然而我不是研究电网的,上面的结论也只是跟 AI 讨论出来的,可能与实际情况相去甚远,甚至存在基本错误,切勿盲信。


  1. 实际上,省级政府办公部门肯定是供电重点保障用户,其至少接入两路电源,所以家里的交流电频率未必和省政府交流电频率相同。这里只是举一个让人觉得很新奇的例子而已。事实上,华北电网的用户甚至有可能用着跟中南海相同频率的交流电。 ↩︎