热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【Python】如何写一个锅炉温控系统

1.前言冬天很冷,买了一个锅炉,需要循环泵的。简单来说就是锅炉水热了之后循环泵自动开启,然后将热水输送走,送到暖气,热水抽走,凉水进入锅炉,温度降低,循环泵关闭,等待下一次水烧

【Python】如何写一个锅炉温控系统

1.前言

冬天很冷,买了一个锅炉,需要循环泵的。简单来说就是锅炉水热了之后循环泵自动开启,然后将热水输送走,送到暖

气,热水抽走,凉水进入锅炉,温度降低,循环泵关闭,等待下一次水烧热。因为需要取暖的房子距离烧锅炉的地方比较远,所以需要循环

泵,如果距离近的话水烧热后利用热水上流冷水回流的原理会自动完成循环。当然目前市场上有这种利用温度自动控制循环泵开启关闭的设

备:

在这里插入图片描述

原理就是有一个热敏电阻探头(带有磁铁吸附,可以吸附到锅炉壁上),然后一个继电器控制的。当温度达到设定的值后,继电器开启,循

环泵启动,循环过后锅炉壁温度下降,继电器关闭,循环泵关闭。

循环泵:

在这里插入图片描述

由于我家的循环泵功率较大,小继电器启动几次就烧坏了,所以中间又接入了一层交流接触器(我的老父亲加的)。

这种市面上的设备可以大致解决水循环的问题,但是也有一些细节问题解决不了,例如:当回流管水温度也达到设定的温度值后,循环泵就

会一直开启状态,这时候就需要手动去调节旋钮温度(调高),让循环泵停下来(大功率循环泵很耗电)。又如 当炉子内部煤渐渐烧结束后

(东北话叫涝了),锅炉温度达不到设定值,这个时候就需要降低旋钮,让循环泵启动,让这些煤产生的余温不浪费掉。

由于以上的不足,就需要有人隔几个小时去调节旋钮。这部分工作都是我老父亲在做,所以我想做一个自动的东西降低一下老父亲的工作

量。

2.项目结构

我最初的构思是通过手机可以远程调节温度,这样至少不用手动去调节旋钮了,我半夜起来在被窝里用手机调节一下就可以了。基于最初的

构思设计的结构:

在这里插入图片描述

3.硬件搭建

esp01模块+继电器模块,220v转5v模块 + 插排 = 联网插座

esp8226 + 温度传感器 + 数码管 = 实时温度检测显示联网模块

我的控制系统都写在 esp8226中。

温度传感器探头制作:要驱动ds18b20传感器,需要在数据总线(DQ)与 VDD 引脚之间加入一个4.7k欧姆的电阻(上拉电阻),这个是必须

有的,用一个铁纽扣包裹起来,里面放了两个小块的钕磁铁,最后用哥俩好胶水灌满,这样就形成了一个温度传感器探头,可以吸附在锅炉

壁上:

在这里插入图片描述

这一步我犯了一个错误,将ds18b20传感器和上拉电阻都放入到探头里面,测温的时候发现温度升高以后温度传感器读取温度失败,是因为

温度升高导致电阻阻值增大。后来我将其电阻放到尾部就解决了这个问题。

封装后的探头:

在这里插入图片描述

esp8226 + 数码管模块:

在这里插入图片描述

壳子是我用3D打印机打印的,里面放着esp8226模块。

4.软件编写

固件

esp8226 和 esp01 我都是烧录的micropython固件。
在这里插入图片描述

通信

通信开始我想的是用socket实现,简单弄个HTTP协议进行通信,但是手机上这样控制是比较麻烦的,没有好用的软件,我还要做个web界

面,另外socket会阻塞线程。

后来我发现了MQTT这种协议,这是一种针对物联网的硬件网络通信协议,可以应对高延迟的网络环境。简单介绍一下MQTT协议,首先需

要一个服务端(也有叫代{过}{滤}理服务器的,运行在电脑上,这里我运行在树莓派上),然后你的所有物联网硬件设备(如esp8226)都是客

户端,物联网硬件设备(客户端)之间不会直接进行通信,它们都是与服务端直接进行通信,硬件设备之间如果需要进行通信是需要发布或

者订阅主题的。例如:如果硬件A 和硬件B 之间需要进行通信,那么首先它们都需要连接到 服务端,然后 A 发布一个名为topic的主题,如果

硬件B要接受A的信息就需要订阅 topic这个主题,这样就实现了 A —> B 的通信,那么反向通信也是一个原理,B 发布主题,A来订阅。这个

协议还有一个优势,可以一个主题多个客户端去订阅,这样就能实现多端通信。mqtt协议通信中每个客户端不知道其它客户端的存在,他们

都是与服务端进行直接通信。
在这里插入图片描述

mqtt协议在micropython固件(我不知道安信可默认固件是不是也有)通信有一个很大的问题:LmacRxBlk:1报错。例如,如果你在开始的时

候订阅了一个主题,然后你使用非阻塞的方法check_msg()通过回调函数处理 订阅的主题消息,这个时候如果你订阅的主题发布的次数超过

你 check_msg()的次数,那么就会在micropython固件底层报一个错误 LmacRxBlk:1,然后通信中断,简单来说就是你订阅一个主题后进入

事件循环,来不及处理订阅主题的消息,就会导致这个错误。这个错误官方解释是tcp buffer资源没有释放导致的,因为esp8226可用资源非

常有限,但是在mqtt通信中我不可能每次循环后都去释放连接,然后每次都重新建立连接,并且这个报错恶心的地方在于,try except 是捕

获不到的,那么对于处理这个错误只能通过设计上来解决了,也就是一开始就保证其订阅的主题发布的间隔远小于其循环的间隔,保证每次

都能及时的处理订阅的消息。这个问题我解决了很久,因为try except 捕获不到,并且不常出现,无法定位,完全不知道为什么通信会中

断。

代码核心逻辑编写

最核心的控制逻辑我都写在esp8226模块中,循环采集温度,然后将温度作为一个主题发布出来,手机可以订阅主题就可以实时查看锅炉温

度了,在循环中有一个设定温度的变量 ,如果采集的温度高于 设定温度 那么就发布一个 开关主题开启的主题,如果温度低于 设定温度,那

么就发布一个 开关主题关闭,并且每次订阅 设置主题,用于修改设定温度变量。

esp01 控制继电器,每次订阅 开关主题 就可以了,然后每次再把 开关当前状态 作为一个主题发布出来。

这个代码逻辑最开始我是这样写的,手机上只需要发布设置温度主题就可以了,但是也是需要人一段时间用手机去调节一次,还是不能实现自动化,后来我又修改的 esp8226 中的代码逻辑,复杂了一些。
在这里插入图片描述

esp8226 核心逻辑还是和上述一样,每次检测温度高于设定温度后还会开启开关,但是启动后检测到温度低于设定温度不会立刻关闭开关,

而是等待检测温度低于设定温度减去一个变量再发布一个开关关闭的主题。这是为了解决 检测温度在设定温度临界反复跳变,从而导致开关

在短时间内反复的开启关闭,这里引入了一个减去变量,我将其叫做温度步长 。这里就有一个问题,如果锅炉一直在升温,那么就算循环泵

一直开启 ,锅炉温度也不会低于设定温度(回流管温度已经高于设定温度了),那么循环泵就会处于一直开启状态,所以这里我引入了第二

个变量:启动最大时长,每次启动后我开启一个计时器,如果计时器时间超过 启动最大时长,那么无论此时的温度是否低于设定温度,都会

关闭循环泵,并且将设定温度提高,提高温度为温度步长加当前温度,这里就实现了 自动调节 设定温度的(升高方向)。还有一个问题:

当炉子煤燃烧殆尽的时候,温度逐渐降低,检测到的温度肯定会远低于设定温度,循环泵就永远不会开启了。所以这里我引入了第三个变量

(回调检测时间),当esp8226 上电启动时,启动一个计时器,如果计时时间 等于回调检测时间,那么将设定温度 调节到当前温度,用于

设定温度 自动回调,假设回调检测时间 为 30分钟,那么即使设定温度 高于 检测温度,也会实现30分钟一启动循环泵,和剩余逻辑实现一

个闭环,这样就能实现 设定温度,上下自动调节。这里我还加入了一个变量最低温度,如果当前温度低于最低温度,那么就算 当前温度高

于设定温度 也不会启动循环泵,用于没烧煤炭的时候,这样就防止了无论什么时候都30分钟启动一次循环泵,最低温度 设置成高于环境温

度一些就可以了。

这里 设定温度,温度步长,启动最大时长,回调检测时间,最低温度,都可以通过手机端进行设置,我让esp8226订阅这些主题用于设置这

些变量。

esp8226代码:

Python学习交流Q群:906715085###
  隐藏代码
from machine import Pin,reset
import onewire
from ds18x20 import DS18X20
import time
import tm1637
from umqtt.simple import MQTTClient

def main():
    client_id = "esp_temperature"
    mserver = "192.168.0.99"
    #mserver = "192.168.3.200"
    #mserver = "mq.tongxinmao.com"

    tm = tm1637.TM1637(clk=Pin(14), dio=Pin(12))
    ow=onewire.OneWire(Pin(4))
    d = DS18X20(ow)
    rom = d.scan()

    def sub_callback(topic, msg):
        # print((topic, msg))
        nonlocal setTemperature
        nonlocal lowTemperature
        nonlocal startTime
        nonlocal scanTime
        nonlocal step

        nonlocal startTimeTemp
        nonlocal scanTimeTemp
        nonlocal switchStatus

        data = int(msg.decode())
        if topic == b"setTemperature":
            setTemperature = data
        elif topic == b"setLowTemperature":
            lowTemperature = data
        elif topic == b"setStartTime":
            startTime = data
            startTimeTemp = startTime

        elif topic == b"setScanTime":
            scanTime = data*60
            scanTimeTemp = scanTime

        elif topic == b"setStep":
            step = data
        elif topic == b"switchWell":
            switchStatus = data
        else:
            print("错误")

    def publishInfo():
        nonlocal client
        client.publish("setTemperatureR",str(setTemperature),retain=True)
        client.publish("setLowTemperatureR",str(lowTemperature),retain=True)
        client.publish("setStartTimeR",str(startTime),retain=True)
        client.publish("setScanTimeR",str(int(scanTime/60)),retain=True)
        client.publish("setStepR",str(step),retain=True)

    client = MQTTClient(client_id, mserver, 0)
    client.set_callback(sub_callback)
    client.connect()
    client.subscribe(b"setTemperature")
    client.subscribe(b"setLowTemperature")
    client.subscribe(b"setStartTime")
    client.subscribe(b"setScanTime")
    client.subscribe(b"setStep")
    client.subscribe(b"switchWell")

    showSet = True
    setTemperature = 40
    lowTemperature = 40
    startTime = 120 #启动时间
    scanTime = 1800 #30分钟
    step = 7

    switchStatus = 0 # 0表示关闭,1表示开启

    startTimeTemp = startTime
    scanTimeTemp = scanTime

    while True:
        try:
            d.convert_temp()
            # 显示温度和设定温度
            nowTemperature = d.read_temp(rom[0])
            if showSet:
                tm.temperature(int(setTemperature))
            else:
                tm.number(int(nowTemperature*10))

            if int(nowTemperature) >= setTemperature and (not switchStatus) and int(nowTemperature) > lowTemperature:#高于设定温度启动
                client.publish("switch","1",retain=True)
                startTimeTemp = startTime

            if (int(nowTemperature) <= setTemperature - step) and switchStatus:
                client.publish("switch","0",retain=True)

            if startTimeTemp <= 0: #如果时间超过3分钟,自动停止,并提高设定温度
                client.publish("switch","0",retain=True)
                setTemperature = int(nowTemperature + step)
                startTimeTemp = startTime

            if switchStatus:
                startTimeTemp -= 1

            client.check_msg()
            client.publish("temperature",str(round(nowTemperature,2)),retain=True)
            publishInfo()

            if scanTimeTemp <= 0:
                setTemperature = int(nowTemperature - step) #回调降温
                scanTimeTemp = scanTime
            scanTimeTemp -= 1 

        except Exception as e:
            reset()

        time.sleep(1)
        showSet = not showSet

 

在这里插入图片描述

esp01中代码:

Python学习交流Q群:906715085###
  隐藏代码
from machine import Pin
from umqtt.simple import MQTTClient
import time

def main():
    def sub_callback(topic, msg):
        nonlocal client
        nonlocal pin
        """
        收到订阅消息回调
        """
        if msg == b"0":
            pin.off()
        else:
            pin.on()
        client.publish("switchWell",str(pin.value()),retain=True)

    client_id = "switch_id"
    mserver = "192.168.0.99"
    #mserver = "mq.tongxinmao.com"        
    pin = Pin(0,Pin.OUT)

    client = MQTTClient(client_id, mserver, 0)
    client.set_callback(sub_callback)
    client.connect()
    client.subscribe(b"switch")
    while True:
        client.check_msg()
        client.publish("switchStatus",str(pin.value()),retain=True)

 

5.客户端监控调节软件

1.手机端:

MQTT Dash:

在这里插入图片描述

IoTMQTTPanel:

在这里插入图片描述

手机上我还是推荐IoTMQTTPanel,因为将一套面板的配置发布到另一台手机很方便,只需要发布一个主题,然后接收端订阅一个同名主题

就可以把整个面板发布过去。MQTT Dash 也具有这个功能,但是发布过去后会卡死,不知道为什么。

2.电脑端:

电脑端我还不知道有什么好用的软件,所以我用 PyQt5 简单写了一个监控的软件,没有调节功能,因为当时设备还不是很稳定,所以我一直

在监控运行状态。

6.后记

目前,整个东西已经稳定运行二周了,再也不需要人进行调节了。我反反复复弄了挺长时间,才把所有问题都解决,特别是那个

LmacRxBlk:1报错,花费了我很长时间。从零开始做一个能实际有用的东西还是挺困难的,因为有些问题只能通过实际的环境才能暴露出

来,例如上拉电阻升温后导致阻值变化。还有很多细节我没提及,例如烧录固件,3d建模外壳,mosquitto服务在树莓派上部署,端口转发,

内容太多不便赘述,只能把主要内容和逻辑进行简单叙述。

在这里插入图片描述


推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
author-avatar
一号香久山日氵冥
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有