采用Zigbee和Raspberry Pi的太阳能/燃气热水器自动控制系统

编程知识 更新时间:2023-04-30 00:37:39

家在顶楼,想着利用太阳能,就安装了太阳能热水器,但毕竟遇上刮风下雨,靠天吃饭不靠谱,太阳能热水器虽然也有电热功能,但水用完了加热等待时间太长,所以也安装了天然气热水器。

大致的管路示意图如下:

但这样使用时就稍显麻烦,因为燃气热水器这边直接承受自来水水压,压力较大,如果两边的阀门同时打开,水就会从太阳能热水器的热水管逆向流动,给太阳能热水器反向上水,直至热水器水箱装满溢出也不会停止,因此,两边的阀门不能同时开启。曾经尝试在太阳能这边安装止逆阀,但效果不佳,止逆阀无法完全密封,只是稍微延缓溢出的时间而已。

我家的燃气热水器还有个特点,因为装在露台,是室外机型,待机15分钟就会自动关闭,此时打开水,热水器不会自动点火,必须按动控制器上的开关打开热水器,才会响应用水请求点火。而且更麻烦的是,每次开机后,之前设定的水温又会自动回到40°,而洗澡水温通常要设定到60°,浴室里的水温水压感觉才最合适,意味着每次开机都要重新设定水温。

燃气热水器在露台,而太阳能热水器的控制器又在洗手间,和露台隔着一个大客厅。

综上可见,每次用热水之前,这个过程之麻烦:
1. 到洗手间查看太阳能热水器的水温和水位;
2. a) 如果太阳能的热水合用,则:
2. a) i) 打开太阳能的热水出水阀;
2. a) ii) 跑去露台关闭燃气热水器的进水阀;
2. b) 如果太阳能这边热水不可用,则:
2. b) i) 关闭太阳能出水阀;
2. b) ii) 跑去露台打开燃气热水器的开关;
2. b) iii) 重新设定水温;
2. b) iv) 拧开燃气热水器进水阀。

自己使用习惯了,也就罢了,每次家里来了客人,要洗澡什么的,都得鞍前马后伺候着,因为实在不觉得客人能在短时间之内搞清楚这么复杂的逻辑,直接放弃解释,亲自服务吧。

作为一个Diyer,实在无法忍受这种情况,终于决定要用科技解决这个问题。

方案

决心是下了,大致方向是用单片机采集太阳能的水位水温,并监测太阳能和燃气的用水状态,动态控制两边的阀门和燃气的开关以及温度设置,但具体采用什么方案解决这个问题呢?

因为设备分布在几个不同的地方,肯定需要无线组网,之前Wifi、蓝牙接触较多,但感觉太重量级了。听说过Zigbee,但还从来没用过,调查了一下,作为家庭内部的智能设备组网,确实比蓝牙和Wifi都合适,于是初步设计系统架构如下:

如上图所示,系统主要由4个Zigbee设备和一个Raspberry Pi树莓派组成。

Zigbee Device 1利用自动增压泵上的自动开关监测太阳能热水器的用水状态,并上传至Coordinator;同时还负责控制太阳能热水器的出水阀门。

Zigbee Device 2负责监测太阳能热水器的水位和水温,并上传至Coordinator。

Coordinator作为Zigbee系统的核心,负责组网的同时,接收Zigbee Device 1、2传来的太阳能热水器的各种数据,自身对燃气热水器的开关状态、使用状态进行监测,并根据水温条件,自行对燃气热水器和太阳能热水器的工作状态进行控制。同时,Coordinator上还有按钮,支持手动切换太阳能和燃气热水器的工作状态。

以上三个组件为系统工作的核心组件,缺一不可,下面则为可选组件。

Zigbee device 3和树莓派组成了本系统的网关和远程控制终端。Device 3和Coordinator通过Zigbee协议进行通信,并利用串口将各种状态和命令与树莓派进行通讯,树莓派作为上位机,既可以利用触摸屏通过QT界面对本系统进行控制,也可以接入家中的Wifi,从而接入Internet,利用手机对本系统进行远程监控操作。

系统架构确定了,就开始着手实施,因为这是第一次接触Zigbee,是一个全新的学习过程,故而通过这篇文章记录之。

设计与实现

首先在万能的淘宝上买了四个CC2530的Zigbee模块和仿真器

也是托大,想碰碰运气,就没有买测试板,结果回来飞线开机啥都没有,两眼一抹黑,也不知道是线接的有问题还是程序有问题,毕竟从来没搞过这个模块,完全无从下手,只好老老实实再买一块测试板回来。

然后再对照着教程烧程序,跑马灯跑起来了,但串口还是没输出,只好对着测试版的电路原理图,一边看CC2530芯片手册,一边测波形,一步步排查,原来商家提供的教程和参考代码居然和他们卖的测试板都对不上,教程和参考代码中用的都是串口0,而他们测试板上USB转串口接的是串口1!nnd坑爹!找到原因了,那就对照芯片手册一步步改吧,终于串口有输出了,这才意味着开发过程中可以进行调试、而不是盲人摸象了。

然后参考教程,从流水灯开始,按键、DMA、ADC、透传……感觉能用得上的实验都做了一遍,觉得Zigbee模块基本功能摸得差不多了,可以开始正式进行系统实现了。

当然,当中串口又有个坑:之前调的是裸机程序,上了透传后ZStack中的串口配置又不对了,又是一顿好找,跟着整个流程从配置文件、端口到DMA设置改了个遍,好容易HalUARTWrite(1, “ABC”,  3)看到有输出了,这才终于松了口气。

应用框架

要开发,自然得先搭软件应用层框架。

TI的Zigbee当然是基于ZStack,但即便ZStack中,也分了好多层,从AF、ZDO到ZCL等,其实这个小系统,直接基于AF层搞透传就好了,但毕竟是第一次搞Zigbee,想弄的深入一些,把那些什么profile、device、cluster、endpoint的概念彻底搞清楚,不然看了半天还是云里雾里。因此,这个系统的实现中很多地方应该是有点过度设计了,有点大炮打蚊子的感觉;有的地方甚至是画蛇添足,完全只是为了验证知识点。

为了实现设备间的双向通信,系统中采用了三种办法:

  1. 定时发送Report,主要用于水位水温等状态数据的定时采集;

  2. 发送ZCL Command,主要用于执行开关等动作;

  3. 读写ZCL Attribute,主要用于一些控制量的设置或读取;

另外,为了便于后期调试,Coordinator也利用了AF层进行透传,把一些调试信息发到网关(Device 3)。

要能通信,首先是设备定义。

Zigbee设备之间进行通信,需要几个值匹配:ProfileId,ClusterId,AttributeId。

ProfileId范围最大,是应用所属范围,例如0x0104是智能家居,0x0101是工业自动化;

ClusterId是对设备具体属性的分类,例如0x0005是场景类,0x0006是开关类;

AttributeId则是具体可操作的属性,定义可见zcl_general.h。例如:

#define ATTRID_SCENES_COUNT 0x0000
#define ATTRID_SCENES_CURRENT_SCENE 0x0001
#define ATTRID_SCENES_CURRENT_GROUP 0x0002

就是场景类下的一些属性定义。

此外,还有一个DeviceId,它定义了设备自身的类型,例如0x0001是可以控制挡位的开关,0x0002只有开/关动作的开关,0x0100是灯,0x0303是泵等等;但DeviceId只在逻辑上供应用层使用,并不是协议中达成通信的的必要条件,例如开关可以控制任何灯、水泵、空调,所以任意两种DeviceId的设备之间都可以进行通信,不形成任何约束。而服务请求/绑定/通信时的ProfileId,ClusterId,AttributeId必须一致。

还有人会说还有个Endpoint呢,Endpoint就只是一个数字而已,就好像你去别人家串门,总要知道别人家的门牌号码,但这个号码究竟是几,其实无所谓。也有点类似TCP/IP中的端口号,7777还是8888,无所谓,关键是你知道是几就好。如果不知道呢?那就用服务请求或绑定去找了。

既然是设备定义,那么要根据功能整理出一堆需要操作的变量,例如设备的开关、太阳能的水温等,定义出相应的的Attributes:

{
ZCL_CLUSTER_ID_GEN_ON_OFF,
{ // Attribute record
ATTRID_ON_OFF,
ZCL_DATATYPE_BOOLEAN,
ACCESS_CONTROL_READ,
(void *)&zclWATERSWITCH_OnOff
}
},

{
ZCL_CLUSTER_ID_MS_TEMPERATURE_MEASUREMENT,
{ // Attribute record
ATTRID_MS_TEMPERATURE_MEASURED_VALUE,
ZCL_DATATYPE_UINT16,
ACCESS_CONTROL_READ,
(void *)&zclWATERSWITCH_Temp
}
},

这里的CLUSTER_ID和ATTRID都是Zigbee规范中定义的(可以参见Profile Id定义和Cluster Id定义),当然也可以自定义,如果是标准的智能家居,当然要按规范来。前面提到的三种通信办法,其实质都是根据CLUSTER_ID和ATTRID对相应的值进行操作。

然后是列出各设备需要输入输出的Cluster,两个设备的输入/输出Cluster中至少要有一个是互补匹配的,即A设备的输出Cluster正好是B设备的输入Cluster,才能在service discovery的绑定过程中匹配成功:

cId_t zclWATERSWITCH_OutClusterList[ZCLWATERSWITCH_MAX_OUTCLUSTERS] =
{
ZCL_CLUSTER_ID_MS_TEMPERATURE_MEASUREMENT,
ZCL_CLUSTER_ID_MS_OCCUPANCY_SENSING,
ZCL_CLUSTER_ID_MS_FLOW_MEASUREMENT
};

设备的基本信息定义好后,接下来就是响应ZDO_STATE_CHANGE事件,该事件对Coordinator意味着网络已准备好,对从设备则意味着已加入网络。

Coordinator在这个事件中通过afSetMatch(WaterSwitch_epDesc.EndPoint, TRUE)允许各device来对其进行绑定。

各从设备则在该事件中通过

dstAddr.addrMode = AddrBroadcast;
dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR;
ZDP_MatchDescReq( &dstAddr, NWK_BROADCAST_SHORTADDR,
WATERSWITCH_PROFID,
ZCLWATERSWITCH_MAX_OUTCLUSTERS, zclWATERSWITCH_OutClusterList, // Server’s input is my output
ZCLWATERSWITCH_MAX_INCLUSTERS, zclWATERSWITCH_InClusterList,
TRUE );

来对Coordinator进行service进行发现和绑定,需要注意的是如果去看ZDP_MatchDescReq的方法定义,和传递的cluser参数的in/out是反的。正如前面这段代码的注释以及前一段说明所解释的,绑定时的cluster是互补的,server的in正好是本设备的out,反之亦然,所以这里提供的参数是反的。

如果能匹配上,子设备会收到ZDO_CB_MSG事件下的Match_Desc_rsp消息,可以通过ZDO_ParseEPListRsp解析匹配的端点信息。

比较麻烦的是因为这里需要实现双向通讯,而现在Coordinator还不知道子设备的信息,无法向子设备发送指令。

在Coordinator响应子设备发送的绑定请求时,会产生一个ZDO_MATCH_DESC_RSP_SENT事件,我们在该事件中可以获得子设备的地址并调用ZDP_ActiveEPReq(&dstAddr, bindAddr, TRUE)来枚举子设备的活动端点。

枚举会返回ZDO_CB_MSG事件下的Active_EP_rsp消息,在该消息中,我们还是通过ZDO_ParseEPListRsp( inMsg )解析出目标设备的所有活动端点;对得到的每个端点号,我们再通过ZDP_SimpleDescReq(&dstAddr, pRsp->nwkAddr, pRsp->epList[i], TRUE)来获得该端点的简单描述符。

该请求返回的是ZDO_CB_MSG事件下Simple_Desc_rsp消息,在该消息中,我们终于可以获得目标设备某个端点的简单描述符。

如果我们给端点定义了合适的描述符,例如:

#define WATERSWITCH_DEVICEID               ZCL_HA_DEVICEID_PUMP

SimpleDescriptionFormat_t WaterSwitch_epDesc =
{
WATERSWITCH_ENDPOINT, // int Endpoint;
WATERSWITCH_PROFID, // uint16 AppProfId[2];
WATERSWITCH_DEVICEID, // uint16 AppDeviceId[2];
WATERSWITCH_DEVICE_VERSION, // int AppDevVer:4;
WATERSWITCH_FLAGS, // int AppFlags:4;
ZCLWATERSWITCH_MAX_INCLUSTERS, // byte AppNumInClusters;
(cId_t *)zclWATERSWITCH_InClusterList, // byte *pAppInClusterList;
ZCLWATERSWITCH_MAX_OUTCLUSTERS, // byte AppNumInClusters;
(cId_t *)zclWATERSWITCH_OutClusterList // byte *pAppInClusterList;
};

那么Coordinator就能通过端点描述符中的deviceId(上例中为ZCL_HA_DEVICEID_PUMP,也是ZCL中的标准设备类型)来判断出该设备的设备类型,从而决定该如何和该设备进行对话。

这个过程比较繁琐,当然你也可以通过透传让子设备给Coordinator随便发个什么消息,告诉Coordinator自己是谁,那样最简单。但这里采用的是符合ZCL规范的形式,如前所述,主要在于知识点的验证。

上面这个服务请求的过程,简单画个流程图如下:

除了这种服务发现的方式,还有另一种常见的bind request的方式,但感觉那种更适用于两两绑定,而不是这里的一对多,故而没有采用。

现在双方都有了对方的电话号码和身份信息,终于可以开始一场轰轰烈烈的恋爱了。

当然Coordinator处于通讯网络的核心,它是比较花心的。

下面,子设备就可以通过zcl_SendReportCmd把采集到的各种状态数据发给Coordinator了,Coordinator通过ZCL_INCOMING_MSG事件下的ZCL_CMD_REPORT消息进行响应,对收到的Report进行解析。

Coordinator也可以通过发送zclGeneral_SendOnOff_CmdOn、zclGeneral_SendOnOff_CmdOff命令来控制子设备开关阀门,子设备则在zclGeneral_AppCallbacks_t中注册对应的zclGCB_OnOff_t回调函数来响应命令,完成具体的操作。

网关(Device 3)则既可以向Coordinator发送命令,也可以直接读写Coordinator暴露出来的Attributes。因为Attributes的定义中指定了值保存的地址,所以这个过程几乎是全自动的,只要指定目标cluster和attributeId就好。只是在读Attribute时,需要对读回来的值进行处理,不然系统怎么知道你拿这个值来干什么。读的响应事件是ZCL_INCOMING_MSG下的ZCL_CMD_READ_RSP,在里面对值进行解析就是。

这些命令底层都是利用AF_DataRequest对某端口的特定cluster和特定attribute进行操作,万变不离其宗,看看代码就好。

对一些特定的命令和动作,例如前面提到的zclGeneral_SendOnOff_CmdOn、zclGeneral_SendOnOff_CmdOff等,ZStack中定义了对应的回调函数,注册后可以直接被调用;如果没有,那么就根据MSGpkt->hdr.event和zclIncomingMsg_t *pInMsg->zclHdrmandID进行判断处理吧。

再就是串口通信,因为Device 3和树莓派通信会用到,可以借用MT层的函数。MT_UartRegisterTaskID(task_id)注册以后,收到串口数据会产生CMD_SERIAL_MSG事件,就可以对串口数据进行处理了。MT层要求的串口数据格式为:FE + 数据长度(不含命令字节、校验位等)+ CMD0 + CMD1 + 数据 + XOR校验。这样很好,我的调试信息和串口数据共用一个串口也不会有什么问题了。

至此,应用层基本框架差不多可以动起来了,可以开始着手对具体的设备和数据进行操作了。这也是一个路漫漫其修远的过程。

数据采集及控制

先把这一步要做的任务理一遍,找出轻重缓急。

首先来看看我们家有个性的燃气热水器,别人家的燃气热水器是24小时开水即热,我们家的是15分钟就睡过去了还健忘。。。

控制器那四个角是被我拆开卸螺钉的地方,翘起来了,回头还得粘上。。。

针对燃气热水器,需要能够监测其是否开启,用户是否正在使用热水;还要能够开启燃气热水器,并将水温设定至最高温度60°C。

看起来感觉应该不难,控制器右边两个LED分别指示开启状态和出热水状态,用GPIO去检测就好了;开关和温度设定看是高电平还是低电平触发,单片机输出相应电平应该就行了。

再看看增压泵:

自动增压泵自己带了个流量开关,就是标箭头那个,实现用户开水就自动开启,正好利用它来监测用户是否在使用太阳能的热水。麻烦的是,这个开关是直接控制220V的,这个电压对单片机的3.3V来说,好像稍微有点高。。。

旁边的阀门后面会换成电动阀门,由Device 1根据Coordinator发来的命令控制开关。阀门是四线控制的,这个用两个继电器就可以轻松搞定。

最后来看看太阳能。。。控制器,热水器在楼顶就不拍照片了,想看自己上网搜,都长的差不多。

这里主要是采集太阳能的水位水温,发送给Coordinator,作为自动控制的依据。

看起来好像很easy,但这是这个系统中最最最麻烦的环节,也是从一开始最没把握的环节,所以就选择从这里开始攻关吧。这里搞定,其他都是毛毛雨了。

二话不说,先拆为敬,不然没法分析。

网上大致搜了一下,我这种太阳能热水器一般是用四线传感器,其中两线是测水位的电阻,两线是热敏电阻,利用电阻变化来测定水位水温。

而测定电阻常用的办法是利用RC电路充放电时间随R变化,通过记录充放电时间来间接计算R值。

这个办法显然比较麻烦,我怎么知道什么温度对应什么样的电阻值,这个阻值又对应什么样的充放电时间?

看看控制器都已经把水位水温显示出来了,我能不能直接拿到这个值呢。。。

太阳能热水器控制器的程序不是我写的,显然不可能直接把这个值给我;既然它能驱动液晶屏显示出对应的信息,能不能抓液晶屏上的信号来获得水位水温呢?这是我想的第一条路。

拆开一看,是那种定制化的液晶屏,左图上一大排针都是它的引脚,看着就头大,用示波器看了一下波形,乱的一塌糊涂,而且电平似乎还不稳定,除了高低电平,好像还有中间电平?只好上网查查,原来这类液晶驱动显示信息较多,为了减少驱动信号(这还叫少?),采用动态驱动,为了防止液晶出现动态驱动中对比度降低的“交叉效应”,一般都会采用一种所谓的“平均电压法”,这就是我在示波器上看到有非高低电平的中间电平的原因,是确有其事,并非我眼花。

这样的话普通的GPIO就没法抓这样的液晶信号了,看来对动态驱动的液晶屏,此路不通,只能另辟蹊径了。

想想,归根结底是电阻,控制器在测电阻的时候会有扫描电压加上去,那么电阻两端就会产生压差,如果我用CC2530的ADC去采集这个电压,假设ADC的精度足够高,是不是就可以根据这个电压算出电阻呢?试试吧。

于是看了下传感器的接线,在下图左边三个画红框的地方,引出三组线到CC2530的P0.0-P0.2口,并将P0.0接到P2.0,利用P2.0在有扫描电平的时候触发ADC转换序列,并通过DMA采集数据。

为了找规律,在不同的水位和水温下面抓了不知道几千条数据,看起来如下图

但完全看不出我希望的那种电阻两端的电压差。大量数据放一起对比,电平上看不出明显差异,倒是注意到波形的宽窄呈现一定的变化规律。

同时,这个波形和我从直觉上对电路的理解,觉得应该产生的波形有很大差异,百思不得其解,最后只好拿来纸笔,把相关电路画下来,并一个个确认相关元件的类型和参数。

左边那3个蓝色圆圆的器件,我一直以为是电容,但根据上面的字怎么也找不到相关的资料,最后翻墙出去,终于在万能的谷歌上找到了这几个器件的准确型号和照片,原来它们居然是:压敏电阻!真是大跌眼镜啊,难怪总觉得电容在电路里产生不了这样的波形。。。真是做什么事都不能想当然。

最后画出的传感器部分电路如下:

根据采集到的波形,结合电路图分析,图中写出了我推测的在不同波形下,电路所处的状态——看来太阳能控制器确实是在利用RC充放电回路测定电阻。

既然无法采集到希望的电压信息,看来只能采取这种最基础、也是最麻烦的方式了:利用RC充放电时间测定电阻。

从图上看,虽然能看到有的信号电平有逐渐变化的过程,但信号还比较乱,考虑从传感器端采集信号改为从控制器IC端采集信号,于是把引线改到了第一张拆机图右边3个红圈处,也就是电路图中标P0、P1、P2的地方。这样抓到的信号如下:

下面的sheet名代表了不同的水位和水温,“-”前的4代表满水位,0是空水位,后面的60、61、62是水温。

这个图看起来还是很乱,但如果我们只看P0的数据,如下:

这个图看起来就相当规整了。

通过不同条件下测量数据的对比,波形的宽度确实和要采集的信号正相关。

这样的话,不用采集3组信号,只用采集P0点的信号就够了,而且也不用ADC,利用Timer记录波形宽度就行了。

然后,又是大量的抓数据,然后看波形宽窄比例的变化,最后,得到水位变化的曲线如下:

水位曲线只有4个等级,比较简单,这个就可以直接用了。

温度曲线就比较麻烦,到底是指数曲线、对数曲线还是幂指曲线?

采样了足够多的数据后,在Excel中对时间系数及其倒数进行分析,很是惊喜,看来P0/P1和温度呈线性关系,采用了Excel的计算出的26.455*x-31.974作为温度计算公式,上机后与原控制器测温误差在1°C以内,效果相当理想。

最难的一块骨头终于啃下来,接下来该轮到别的了。

先来看看太阳能用水检测吧,这个需要检测220V通断的,变压器肯定犯不着,太大材小用了;阻容分压?好像隔离不太好。。。上网找了一下,有人建议用光耦隔离,这个不错,于是草草画了个图,桥式整流加光耦:

一开始本来还计划用Protel来做电路设计,但真临了一想,就这么几个器件,随手画画就好,想出错都难,实在犯不着再开一个工具。。。

于是网上淘了两个PC817,这边就算齐活了。

接下来是燃气控制这边。

按键控制测了一下,是低电平有效,这个好说,搞个9014拉低就好了。

开关和用水监测却给我找了点小小的麻烦,本来以为LED亮起来,测测正极有没有电压就好了,结果拿示波器一打,根本不是这么一回事:

不论灯亮不亮,正极都是一个5V的方波;负极随灯开关有不同波形的变化;如果测LED正负极之间的电压,则开启时有1.8V的方波,关闭时反而有最低-0.4V的方波,如下图右边所示。

简单分析了一下,正极应该是控制器上4个LED共享的PWM驱动信号,所以不论任何一个LED亮灭,这个方波始终存在;单个LED是低电平驱动,高电平截至,所以负极正好能观察到示波器上的波形。

原理分析清楚了,那问题就容易解决了,并个PNP管,跟着LED报告状态就行了,翻箱倒柜一番,找到几个残留的9012,够了。电路如下所示,DET点接CC2530中断脚,有中断则表示LED开启,无中断则表示LED关闭。

外围各种信号、状态都已采集到,那接下来就简单了,编程,焊接电路,组装实施。

实施

大致控制策略无非就是:

1. 检测太阳能热水器的水温和水位;
2. a) 如果太阳能的热水合用,且用户此时没有使用热水,则:
2. a) i) 打开太阳能的热水出水阀;
2. a) ii)关闭燃气热水器的进水阀;
2. b) 如果太阳能这边水温低于60°,且用户没有使用热水,则:
2. b) i) 打开燃气热水器的开关;
2. b) ii) 设定水温为60°;
2. b) iii) 关闭太阳能出水阀;
2. b) iv) 打开燃气热水器进水阀。
2. c) 如果太阳能这边水位为最低,那么不管用户是否正在使用太阳能的热水,强制切换为燃气;
2. d) 如果太阳能上水时,用户尝试使用热水,强制切换为燃气。
3. e) 如果应该使用燃气热水器时,燃气热水器因为待机15分钟自动关闭,则按动电源键重新开启并设置温度为60°C。

当没有客人的时候,有时哪怕太阳能20、30°的水温,也觉得可以用来洗洗手,这时这个自动策略就不适用了,因此在Coordinator又加了两个按钮,用于手动切换太阳能/燃气,以及让系统在自动和手动工作模式之间切换。

考虑到Coordinator和燃气热水器装在露台,有淋雨的危险,因此外壳最好减少留孔,普通的按键就不适合,不好密封,最好是触摸按钮。淘宝上溜了一圈,淘了几个合适的触摸模块回来。

这玩意儿离手的距离要合适,太远感应不到,离上壳太近又会直接感应到上壳始终保持常开状态,怎样可以灵活调整这个距离呢?想来想去,排针加插座解决这个问题:

既能相对稳定,又能灵活调整触摸按钮的高度。

还有就是要解决模块的烧录问题。

卖家提供的是测试板上的排座,要在模块上焊排针插上去烧

测试模块这样没问题,可问题是有的地方安装空间有限,焊上排针太碍事;飞线烧吧太麻烦,每个模块都要飞将近10根线。

琢磨着怎么样自己做个烧录器呢,结合手边有的材料和零件,关键是要能和模块的引脚紧密接触。最终利用两组排针,一个简易烧录器出炉了:

烧录时把模块的邮票孔夹在两组排针中间,就可以顺利实现烧录、调试,虽然偶尔会有点接触不良,但比一个个飞线好多了。

考虑到模块总会有bug,而没有外接Flash,256KB内存根本就不够ZStack实现OTA升级,因此在线调试时肯定会频繁拆下来,所以模块都是用杜邦线引出接到其他电路部分,方便拆卸。

这是从燃气热水器控制器引出的线。

下面是Coordinator内部的全貌,杜邦线太多,看起来有点乱糟糟,没办法,不是工业化设计出来的成品:

本来计划从燃气那边把5V的电压引过来,这样就省了一组电源,奈何继电器要的电流太大,那边的功率不够,一开继电器就全系统复位,只好外接了一组USB 5V充电电源,通过左上角的电源模块转为3.3V。

下图是太阳能热水阀控制器的内部结构,看起来比较清爽,外围主要就一组光耦和一对继电器:

然后是水管管路改造,要把电动阀门装上,下面是部分零件:

由于空间有限,电动阀门没法直接安装,只能利用波纹管转接,本来一个直直的增压泵,现在变成了九曲回肠,水泵也趁此机会换了个新的,水压大了不少:

  -> 

燃气热水器这边:

给Coordinator打印了一个操作面板:

本来还给LED留了孔,后来发现特别是绿光LED,实在太亮了,就干脆用纸全蒙上,但晚上还是觉得很亮,可以考虑加大电阻或改用PWM控制占空比。

因为中间有墙,Zigbee还是会有通信不良的情况发生,如果持续无法收到Device 1或者2的信号,Coordinator右边两个LED会分别闪烁,以提示用户。如果是在自动工作模式,此时也会自动切换到燃气模式,以保证用水。

至此,本系统核心部分工作已基本完工,可以实现燃气/太阳能的全时工作和自动切换,也支持手动操控。考虑到用水条件不是一个变换频率很高、实时性要求很强的场景,系统中大部分地方采取了5秒轮询的方式,只有少数控制,例如手动切换燃气、太阳能等为实时中断处理。

在电脑上通过串口终端,也已经可以通过Device 3向Coordinator发送指令、获取工作状态:

接下来只要用树莓派替代电脑,开发一个UI界面,向Device 3收发串口指令就行了;或者更进一步,就像开头说的那样,将树莓派接入Wifi后,开发相应的服务器和手机端UI,实现远程操控,完成智能家居的完整场景。

树莓派虽然之前没用过,但其实质就是一台嵌入式Linux电脑而已,这上面的软件开发只是时间问题而已,没有什么新的东西。

不过老实说,之前要么就是在控制台或Linux内核驱动层折腾,要么就是在Android上做App开发,Linux上的带UI的App开发还真没搞过,用什么来做呢?

调Zigbee时抓包工具是基于QT的,那干脆我也用QT吧,正好有参考,还可以跨平台。

QT还算简单,有Win32 GUI和Android等App开发的基础,参考QT Creator中的教程,花两天时间个大致把功能和界面搭了出来:

然后就是怎么移到树莓派上去运行。

电脑上交叉编译是可以,但那个环境配置估计还是有点麻烦,而且我以后用到的机会应该也不多吧。。。

于是决定直接到树莓派上去搞。

网上搜罗了一番,先安装基础包:

sudo apt-get install qt5-default
sudo apt-get install qtcreator

然后因为需要和Zigbee模块串口通信,还需要安装串口模块:

sudo apt-get install libqt5serialport5
sudo apt-get install libqt5serialport5-dev

在QT Creator中,要手动添加GCC和G++工具:
/usr/bin/gcc, /usr/bin/g++
Debugger可以选择
/usr/bin/gdb

QT version也要确保选上了,然后确保工程的编译路径存在,发现QT Creator会在工程目录后面加一堆后缀,导致路径不存在然后无法生成。而且不论是在Windows下还是Linux下都有这个问题,QT项目组该看看这个问题,一开始搞得我莫名其妙,不知道工程哪儿出什么错了。

环境准备好后,导入工程,编译顺利完成。

本来计划是飞线直连树莓派的串口,突然转念一想,直连串口是串口,USB转串口也是串口,既然调试板有USB口直出,插上就是了,何必飞线呢。没有飞线的羁绊,以后想将这个Gateway模块挪作他用,做点别的测试,还更方便。

上机一试,哈哈,要的就是这个DIY的效果。

习惯了做国际化的工程,界面默认都是英文,抽空再用QT的方法翻译一下吧^_^

前文提到的Zigbee和QT工程,代码已提交至Github,有兴趣的话可以作为参考:https://github/shaoyie/WaterSwitch

毕竟是第一次接触Zigbee,难免有疏漏或理解不正确之处,欢迎指正。

转载请标明出处: 采用Zigbee和Raspberry Pi的太阳能/燃气热水器自动控制系统 (https://blog.csdn/shaoyie/article/details/103525230)

更多推荐

采用Zigbee和Raspberry Pi的太阳能/燃气热水器自动控制系统

本文发布于:2023-04-22 08:17:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/0faa9f88aefa4c153c1ec2a464228fa5.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:太阳能   燃气热水器   自动控制系统   Zigbee   Raspberry

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!

  • 95515文章数
  • 24066阅读数
  • 0评论数