Flask网页按钮通过MQTT控制ESP23继电器开合-Micropython

该文章介绍了一个通过网页控制ESP32微控制器开关灯的系统,利用Flask创建Web服务,用户点击网页按钮,服务器通过MQTT发送指令到ESP32,ESP32接收到指令后控制继电器开或关。系统包括Ubuntu服务器、EMQXMQTTbroker、Flask应用以及ESP32上的Micropython程序。
部署运行你感兴趣的模型镜像

最近想不用下床就能用手机控制灯的开关,本来想做个微信小程序,但是太麻烦,最后直接用网页控制了。
具体流程就是:网页中有两个开关,一个关一个开,按下开灯或关灯按钮时,网站服务器给MQTT发相应指令,ESP32一直连接着MQTT,当收到指令时,判断一下收到的指令,然后执行相应的动作。

这套系统用到的东西:Ubuntu服务器MQTT服务器(我用的是免费的EMQX在自己服务器上搭的),Flask(用于运行web服务),ESP32+Micropythonmosuqitto-client(这个是用于在Ubuntu上的Flask中使用命令行发送mqtt消息的,用这个比较方便,别的系统可以使用paho-mqtt库,安装命令:sudo apt-get install mosuqitto-client)

先上网站的前后端代码。
前端网页:relay.html

<!DOCTYPE html>
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
<html>
  <head>
    <title>灯光控制系统</title>
  </head>
  <body>
    <div class="header" id="demo">
      <div class="title"><h2>灯光控制系统</h2></div>
      <div class="t_b_box">
        <form action="/" method="post">
          <input type="submit" name="on" value="开灯">
          <br>
          <input type="submit" name="off" value="关灯">
        </form>
      </div>
    </div>
  </body>
</html>

这个网页可以自适应电脑和手机页面,网页文件放在Flask目录下的templates文件夹中。

后端Flask代码:app.py

from flask import Flask, render_template, Response, request
import os

app = Flask(__name__)

@app.route('/',methods=['GET', 'POST'])
def index():
    if request.form.get('on', None) == "开灯":
        os.system('mosquitto_pub -h localhost -p 1883 -t "/test/led" -m "led_on"')
    elif request.form.get('off', None) == "关灯":
        os.system('mosquitto_pub -h localhost -p 1883 -t "/test/led" -m "led_off"')

    return render_template('relay.html')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

这里解释一下if里的os.system(‘mosquitto_pub -h localhost -p 1883 -t “/test/led” -m “led_on”’),os.system()是将括号里的内容直接在命令行执行,mosquitto_pub -h localhost -p 1883 -t “/test/led” -m "led_on"的意思是发送一条mqtt消息,-h localhost是mqtt地址,这里我使用mqtt是因为Flask与MQTT服务都安装在同一个服务器上,所以我直接用了本地网址,这里直接填你所用的MQTT服务地址即可,-p -t -m分别为端口、信息发布topic、发布的信息。

ESP32硬件端:
main.py,在micropython中,main.py是上电自动启动的:

import utime
from machine import Pin
import network

print('hi ESP32-Micropython')
LED1 = Pin(33,Pin.OUT) # 用于指示WIFI是否连接

# 连接WIFI
count = 0
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.disconnect()  # 连接之前先断开现有连接
if not wlan.isconnected():
    print('connecting to wifi...')
    wlan.connect('WIFI名-只能用2.4GWIFI','WIFI密码')
    while not wlan.isconnected() and count < 20:
        LED1.value(not LED1.value()) # WIFI正在连接时,指示灯闪烁
        count += 1
        utime.sleep(0.5)
if wlan.isconnected(): 
	LED1.value(1)
    print('ip address:',wlan.ifconfig()[0]) # 打印一下设备IP地址
else:
    LED1.value(0)
    print('wifi connect fail')
    

utime.sleep(0.5)

exec(open('./网页控制继电器.py').read(),globals())  # 执行指定的程序

网页控制继电器.py

from machine import UART,Pin
import time
import utime

relay = Pin(22,Pin.OUT)  # 继电器的PIN口
message = ''

###ALI
from simple import MQTTClient
import json
import utime

topic_pub='test'    # 发布topic
topic_sub='/test/led'  # 订阅topic

def sub_cb(topic, msg):  # 获取mqtt服务器的数据
    global message
    print("收到云端数据")
    print("topic =", topic)
    print("msg = ", msg)
    message = str(msg)[2:-1]  # 这里对收到的数据进行了一个切片,因为我图方便,直接把收到的数据强转成了str,为b'led_on'或者b'led_off',然后切片把头尾的b'和'给去除了
    print('收到指令:',message)
    if message == "led_on":  # 判断,然后执行开灯或关灯程序
        print('开灯')
        relay.value(1)
        message = ''  # 完成执行后,清空数据
    elif message == 'led_off':
        print('关灯')
        relay.value(0)
        message = ''
            
c = MQTTClient("","127.0.0.1",1883,"","",60) # 替换其中的服务器地址和端口即可,最后的60是保活的,别动
c.set_callback(sub_cb)

tim = 0  # 计时的参数
while True:
    utime.sleep(0.5)
    try:
        tim += 1
        c.check_msg()
    except:
        print('mqtt check err')
        try:    # 如果mqtt出现错误的话,尝试重新连接
            c.connect()
            print("mqtt连接成功")
            c.subscribe(topic_sub)
            print("mqtt订阅成功,订阅topic为:",topic_sub)
        except:   # 重新连接还是不行的话,尝试重新从main.py执行
            print('mqtt connect err')
            utime.sleep(1)
            #c.disconnect()
            wlan.disconnect()
            exec(open('./main.py').read(),globals())
    if tim == 120:  # 1分钟发送一次数据,证明设备在线,方便调试
        tim = 0
        c.publish(topic_sub, "keepalive")

sample.py,这是MQTT库:

import usocket as socket
import ustruct as struct
from ubinascii import hexlify

class MQTTException(Exception):
    pass

class MQTTClient:

    def __init__(self, client_id, server, port=0, user=None, password=None, keepalive=60,
                 ssl=False, ssl_params={}):
        if port == 0:
            port = 8883 if ssl else 1883
        self.client_id = client_id
        self.sock = None
        self.server = server
        self.port = port
        self.ssl = ssl
        self.ssl_params = ssl_params
        self.pid = 0
        self.cb = None
        self.user = user
        self.pswd = password
        self.keepalive = keepalive
        self.lw_topic = None
        self.lw_msg = None
        self.lw_qos = 0
        self.lw_retain = False

    def _send_str(self, s):
        self.sock.write(struct.pack("!H", len(s)))
        self.sock.write(s)

    def _recv_len(self):
        n = 0
        sh = 0
        while 1:
            b = self.sock.read(1)[0]
            n |= (b & 0x7f) << sh
            if not b & 0x80:
                return n
            sh += 7

    def set_callback(self, f):
        self.cb = f

    def set_last_will(self, topic, msg, retain=False, qos=0):
        assert 0 <= qos <= 2
        assert topic
        self.lw_topic = topic
        self.lw_msg = msg
        self.lw_qos = qos
        self.lw_retain = retain

    def connect(self, clean_session=True):
        self.sock = socket.socket()
        addr = socket.getaddrinfo(self.server, self.port)[0][-1]
        self.sock.connect(addr)
        if self.ssl:
            import ussl
            self.sock = ussl.wrap_socket(self.sock, **self.ssl_params)
        premsg = bytearray(b"\x10\0\0\0\0\0")
        msg = bytearray(b"\x04MQTT\x04\x02\0\0")

        sz = 10 + 2 + len(self.client_id)
        msg[6] = clean_session << 1
        if self.user is not None:
            sz += 2 + len(self.user) + 2 + len(self.pswd)
            msg[6] |= 0xC0
        if self.keepalive:
            assert self.keepalive < 65536
            msg[7] |= self.keepalive >> 8
            msg[8] |= self.keepalive & 0x00FF
        if self.lw_topic:
            sz += 2 + len(self.lw_topic) + 2 + len(self.lw_msg)
            msg[6] |= 0x4 | (self.lw_qos & 0x1) << 3 | (self.lw_qos & 0x2) << 3
            msg[6] |= self.lw_retain << 5

        i = 1
        while sz > 0x7f:
            premsg[i] = (sz & 0x7f) | 0x80
            sz >>= 7
            i += 1
        premsg[i] = sz

        self.sock.write(premsg, i + 2)
        self.sock.write(msg)
        #print(hex(len(msg)), hexlify(msg, ":"))
        self._send_str(self.client_id)
        if self.lw_topic:
            self._send_str(self.lw_topic)
            self._send_str(self.lw_msg)
        if self.user is not None:
            self._send_str(self.user)
            self._send_str(self.pswd)
        resp = self.sock.read(4)
        assert resp[0] == 0x20 and resp[1] == 0x02
        if resp[3] != 0:
            raise MQTTException(resp[3])
        return resp[2] & 1

    def disconnect(self):
        self.sock.write(b"\xe0\0")
        self.sock.close()

    def ping(self):
        self.sock.write(b"\xc0\0")

    def publish(self, topic, msg, retain=False, qos=0):
        pkt = bytearray(b"\x30\0\0\0")
        pkt[0] |= qos << 1 | retain
        sz = 2 + len(topic) + len(msg)
        if qos > 0:
            sz += 2
        assert sz < 2097152
        i = 1
        while sz > 0x7f:
            pkt[i] = (sz & 0x7f) | 0x80
            sz >>= 7
            i += 1
        pkt[i] = sz
        #print(hex(len(pkt)), hexlify(pkt, ":"))
        self.sock.write(pkt, i + 1)
        self._send_str(topic)
        if qos > 0:
            self.pid += 1
            pid = self.pid
            struct.pack_into("!H", pkt, 0, pid)
            self.sock.write(pkt, 2)
        self.sock.write(msg)
        if qos == 1:
            while 1:
                op = self.wait_msg()
                if op == 0x40:
                    sz = self.sock.read(1)
                    assert sz == b"\x02"
                    rcv_pid = self.sock.read(2)
                    rcv_pid = rcv_pid[0] << 8 | rcv_pid[1]
                    if pid == rcv_pid:
                        return
        elif qos == 2:
            assert 0

    def subscribe(self, topic, qos=0):
        assert self.cb is not None, "Subscribe callback is not set"
        pkt = bytearray(b"\x82\0\0\0")
        self.pid += 1
        struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid)
        #print(hex(len(pkt)), hexlify(pkt, ":"))
        self.sock.write(pkt)
        self._send_str(topic)
        self.sock.write(qos.to_bytes(1, "little"))
        while 1:
            op = self.wait_msg()
            if op == 0x90:
                resp = self.sock.read(4)
                #print(resp)
                assert resp[1] == pkt[2] and resp[2] == pkt[3]
                if resp[3] == 0x80:
                    raise MQTTException(resp[3])
                return

    # Wait for a single incoming MQTT message and process it.
    # Subscribed messages are delivered to a callback previously
    # set by .set_callback() method. Other (internal) MQTT
    # messages processed internally.
    def wait_msg(self):
        res = self.sock.read(1)
        self.sock.setblocking(True)
        if res is None:
            return None
        if res == b"":
            raise OSError(-1)
        if res == b"\xd0":  # PINGRESP
            sz = self.sock.read(1)[0]
            assert sz == 0
            return None
        op = res[0]
        if op & 0xf0 != 0x30:
            return op
        sz = self._recv_len()
        topic_len = self.sock.read(2)
        topic_len = (topic_len[0] << 8) | topic_len[1]
        topic = self.sock.read(topic_len)
        sz -= topic_len + 2
        if op & 6:
            pid = self.sock.read(2)
            pid = pid[0] << 8 | pid[1]
            sz -= 2
        msg = self.sock.read(sz)
        self.cb(topic, msg)
        if op & 6 == 2:
            pkt = bytearray(b"\x40\x02\0\0")
            struct.pack_into("!H", pkt, 2, pid)
            self.sock.write(pkt)
        elif op & 6 == 4:
            assert 0

    # Checks whether a pending message from server is available.
    # If not, returns immediately with None. Otherwise, does
    # the same processing as wait_msg.
    def check_msg(self):
        self.sock.setblocking(False)
        return self.wait_msg()

还有很大的优化空间,比如只用一个按钮发送一个MQTT指令,硬件端在收到指令后对继电器状态进行取反。我的这些程序只是提供一个大概的框架。

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值