1

闲来无事-树莓派控制风扇启停 - 聂显达

 10 months ago
source link: https://www.cnblogs.com/niexianda/p/17506382.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

闲来无事-树莓派控制风扇启停

端午放假,本想注册个美团众包骑自行车送外卖体验一下生活,奈何这几天北京热的要死,只能作罢,还是苟在屋里空调续命吧。
无事干的时候,想着给我花盆监控升个级,换个电容的土壤检测(之前的腐蚀了gg了)但是电容的是3v的,esp8266只能检测1v的,所以买了一个新的esp32-cam,正好带个摄像头,间隔5分钟拍个照片,一天下来还能拼接一个延时摄影的效果,奈何这个板子有点毛病,跑不起来,如下图(商家也无法解决)

1393523-20230624154603331-827239941.jpg
无奈退货(有大哥知道这个怎么解决麻烦指导一下小弟),想着下雨天打孩子,闲着也是闲着,这条路走不通了,换个其他的玩玩了。
我的树莓派有个风扇,成天在转,给他加一个控制,超出设定的温度再启动,最好在给个页面去控制一下,开搞
还有就是这个树莓派我买的时候400,我靠这玩意还涨价了,无语了,当时400我都觉得贵,还不如买个单片机30块就能玩
  1. 设备控制-远程控制启停
  2. 后台定时任务-定时记录温度,根据设定的温度控制风扇启停
  3. 服务处理请求-展示温度曲线

来个流程图吧

http请求
风扇状态写入
最大温度写入
开关控制/最大温度
页面控制指令

需要考虑的问题

  1. 当前的实现是用python来写的,我不想每次重启设备后都要ssh上去再使用命令行启动,问了公司大神给了个解决方案:systemctl,可以实现开机自启,使用方式如下,想了解更多可以看:https://ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html
    当人为了防止程序挂掉,或者我退出以后停了,使用了nohup让python脚本一直在后太运行
  2. 如何控制风扇启停呢?树莓派上带了不少Gpio的接口,但是输出的电流只能点亮个led带不动风扇,所以我们需要一个三极管,我买的是s8050NPN的有两种别买错了,pnp是接在地线上的,用流程图画一个接线图吧,意思一下得了markdown不知道怎么画电路图

1. 配置方式和使用方法(当前的解决方案)

/etc/systemd/system 增加对应的文件

fanServer.service

[Unit]
Description=test deamon
After=rc-local.service

[Service]
Type=simple
User=root
Group=root
WorkingDirectory=文件的路径
ExecStart=nohup python3 -u processRequest.py > service.log 2>&1 &
Restart=always

[Install]
WantedBy=multi-user.target

fan.service

[Unit]
Description=test deamon
After=rc-local.service

[Service]
Type=simple
User=root
Group=root
WorkingDirectory=文件所在的路径
ExecStart=nohup python3 -u index.py > fan.log 2>&1 &
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl start  fan.service
sudo systemctl start  fanServer.service

添加到开机自启

sudo systemctl enable fan.service
sudo systemctl enable fanServer.service

2. 接线图

高电开/低电关

意思一下得了哈,效果看图片

现在看一下效果吧

图片名称
图片名称

> 接线的效果图如图二

正片干货结束无聊的实现时间

全部代码地址:https://github.com/dadademo/piFan

设备控制(网页端)

设备控制主要就是:1. 展示数据 2. 发送设备控制指令

写个vue的页面,用脑子设计一下:开关按钮得有,温度设定得有,还得有个图表,还得加个筛选时间,根据时间来筛选数据。就酱式的吧

代码如下(太多了给个链接吧)

https://github.com/dadademo/piFan/blob/main/fanUi/src/components/home.vue

后台定时任务

后台定时任务:1. 定时读取任务,根据设定的温度决定是否开/关风扇 2. 定时记录信息,将信息存在数据里面

这些服务本来想用node写,奈何node操作gpio的库一直不能下载,只能用python来搞了
注意这个代码我是直接复制的,依赖一些我写的其他的python脚本,如果想跑起来的话还直接拉全部代码去看吧
这里就是用python启动一个服务,接收网页发送的请求,例如设定温度,还有启停的制冷

定时任务python代码

这里就是每隔5分钟读取一次温度,超过了就启动风扇,没超过就停止


# 使用内建的signal库捕获Ctrl+C
import signal
# 风扇模块
import controlFan
# 退出程序
import sys
# 引用db模块
import db
import time


# 开始时间
beginTimeHour = 7
# 结束时间
endTimeHour = 20

# 每隔5分钟判断一次温度
timeOut = 60*5



# 程序停止
def stop():
    # 停止gpio操作
    controlFan.quit()
    # 退出程序
    sys.exit()

controlFan.init_gpio()

# 监听Ctr+C
signal.signal(signal.SIGINT, stop)
while True:
    # 0/1 当前风扇状态
    fanStatus = 0
    # 设定状态,默认40度
    setTemp = 0
    # 获取状态
    lastData = db.getLastData()
    fanStatus =  lastData.get('fanStatus')
    setTemp = lastData.get('setTemp')

    # 获取当前温度
    currentTemp = controlFan.read_cpu_temperature()
    # 时间
    currentTime = time.localtime()
    dt = time.strftime('%Y:%m:%d %H:%M:%S', currentTime)
    print(dt)
    # 当前温度大于设定温度
    ctrlVal = currentTemp > setTemp
    # 判断逻辑
    if ctrlVal:
        fanStatus = 1
    else:
        fanStatus = 0
    # 初始化后追加状态
    db.addInfo(fanStatus, currentTemp, setTemp)
    if ctrlVal:
        # 夜间模式
        if currentTime.tm_hour < beginTimeHour and currentTime.tm_hour > endTimeHour:
            controlFan.set_fan(False)
        else:
            controlFan.set_fan(True)
    else:
        controlFan.set_fan(False)
    time.sleep(timeOut)
服务端python的代码

这里就是用python启动一个服务,接收网页发送的请求,例如设定温度,还有启停的风扇指令,写这个代码还了解了一下python的数据类型,赚到了
代码如下

# 导入http.server模块
import http.server
import socketserver

# json对象
import json

# 参数解析
from urllib.parse import urlparse, parse_qs, parse_qsl

import db
import controlFan

# 定义端口号
HTTP_PORT = 3001

# 获取query信息并转为obj


def getQureyData(url):
    retObj = {}
    currentQuery = parse_qs(urlparse(url).query)
    for key in currentQuery:
        if len(currentQuery[key]) == 1:
            retObj[key] = currentQuery[key][0]
        else:
            retObj[key] = currentQuery[key]
    return retObj


# 处理相应
class httpResponse(http.server.SimpleHTTPRequestHandler):
    # 处理get请求
    def do_GET(self):
        # 当前访问的链接决定
        currentUrl = urlparse(self.path).path
        print(currentUrl)
        if currentUrl == '/getFan':
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            # 读取信息
            query = getQureyData(self.path)
            jsonInfo = db.getInfo(
                query.get('startTimestamp'), query.get('endTimestamp'), query.get('pageIndex'), query.get('pageSize'), query.get('all'))

            retStr = json.dumps(jsonInfo)
            self.wfile.write(bytes(retStr, "utf8"))

        if currentUrl == '/getLast':
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            jsonInfo = db.getLastData()
            retStr = json.dumps(jsonInfo)
            self.wfile.write(bytes(retStr, "utf8"))
        return

    # 处理post请求
    def do_POST(self):
        # 获取POST请求的数据
        currentUrl = urlparse(self.path).path
        if currentUrl == '/setFan':
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            # 当前信息
            query = getQureyData(self.path)
            # 获取最后的信息
            lastData = db.getLastData()
            fanStatus = int(query.get('fanStatus')
                            or lastData.get('fanStatus'))
            setTemp = int(query.get('setTemp') or lastData.get('setTemp'))
            print(fanStatus, fanStatus == 1)
            # 写入状态
            db.addInfo(fanStatus, controlFan.read_cpu_temperature(), setTemp)

            if fanStatus == 1:
                controlFan.set_fan(True)
            else:
                controlFan.set_fan(False)

            self.wfile.write(bytes("{status:200}", "utf8"))
        return


# 启动http服务
def beginServer():
    with socketserver.TCPServer(("", HTTP_PORT), httpResponse) as httpd:
        print("serving at port", HTTP_PORT)
        httpd.serve_forever()

controlFan.init_gpio()
# 开启服务
beginServer()

主要逻辑就这么多,全部的代码看git的地址吧,readme里面也有对应的操作,想网访问的话可以看我上一篇文章,有对应的链接,就不再这里哔哔了


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK