zgz2zz

摘要

       物联网是一个基于互联网、传统电信网等的信息承载体,它让所有能够被独立寻址的普通物理对象形成互联互通的网络[1]。物联网的应用领域涉及到方方面面,在工业、农业、环境、交通、物流、安保等基础设施领域的应用,有效的推动了这些方面的智能化发展,使得有限的资源更加合理的使用分配,从而提高了行业效率、效益。

       本项目分为设备端和PC端,设备端负责采集数据并且能做出一些反应,例如:温湿度、光照强度的数据采集,光电门判断门的开关,控制蜂鸣器发声报警,控制灯的亮灭以及亮度;PC端主要接收设备端上传的数据并且显示相关信息,下发控制指令到设备。

1.       引言

       物联网的应用领域涉及到方方面面,本项目实现的功能可以应用到智能家居、智能农业大棚、智慧城市等等。

       这里硬件编程应用到了MicroPython,MicroPython是Python的一个精简版本,MicroPython极精简高效的实现了Python 3.X语言。因为要适应嵌入式微控制器,所以裁剪了大部分标准库,仅保留部分模块如math、sys的部分函数和类。此外,很多标准模块如json、re等在MicroPython中变成了以u开头的ujson、ure,表示针对MicroPython开发的标准库,能在单片机和受限环境中运行。MicroPython包含了诸如交互式提示,任意精度整数,关闭,列表解析,生成器,异常处理等高级功能。 足够精简,它是为了运行在单片机这样的性能有限的微控制器上,最小体积仅256K,运行时仅需16K内存。MicroPython旨在尽可能与普通Python兼容,让您轻松将代码从桌面传输到微控制器或嵌入式系统。

       设备端和PC端之间采用了MQTT通信(基于发布/订阅范式的消息协议),传输数据打包成JSON数据格式。MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛,在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。

本项目分为设备端和PC端:

       设备端负责采集数据并且能做出一些反应,例如:温湿度、光照强度的数据采集,光电门判断门的开关,控制蜂鸣器发声报警,控制灯的亮灭以及亮度;设备的选择:主控芯片是ESP8266,126*64的OLED显示屏,温湿度DHT11、光敏电阻、蜂鸣器、光电门、杜邦线若干。ESP8266具有WIFI功能,无需加入其他的联网设备,而且MicroPython对其进行了支持,刷入对应的MicroPython固件到esp8266就可以进行python编程了。

       PC端基于python中的tkinter模块编写一个交互界面。主要接收设备端上传的数据并且显示相关信息,下发一些控制指令到设备。

2.       系统结构

       该系统主要由服务器、设备端和PC端三部分构成。

1.1          服务器

       服务器可以选择自己在本地搭建,也可以去跟各大平台合作。我这里选择是跟某度智能云合作,前往平台注册个账号就可以白嫖物接入服务了。

       进入到物接入服务,创建项目,选择类型,输入一个自己喜欢项目名称,确定就会返回一个域名地址。进入项目就可以创建用户、身份以及策略(订阅与发布的主题)。一定要记住创建身份后返回的随机密钥,后面连接服务需要使用,不过忘了可以重置。

图1.服务器创建项目

1.2          设备端

      i.              环境搭建

       要在ESP8266进行python编程肯定先要刷入Micropython固件,固件从这个地址下载:http://micropython.org/download#esp8266,固件应该就五百多k,下载两三个小时应该就好了;刷入工具有好多,不过都大同小异,比如:ESP8266Flasher 、NodeMCU-PyFlasher 等。

       编辑器我选择是Visual Studio Code,因为在这里RT-Thread 官方提供了个RT-Thread MicroPython插件,为MicroPython 开发提供了强大的开发环境,主要特性如下:

  • 设备快速连接(串口、网络、USB)
  • 支持基于 MicroPython 的代码智能补全与语法检查
  • 支持 MicroPython REPL 交互环境
  • 提供工程同步功能
  • 支持下载单个文件或文件夹至开发板
  • 支持在内存中快速运行代码文件功能
  • 支持运行代码片段功能
  • 支持多款主流 MicroPython 开发板
  • 支持 Windows、Ubuntu、Mac 操作系统

    ii.              分配引脚

       ESP8266共有 17个GPIO 管脚,其中GPIO16与其他 IO 口不同,GPIO16(XPD_DCDC) 不属于通用 GPIO 模块,它属于 RTC 模块,可以用来在深度睡眠时候唤醒整个芯片,可以配置为输入或者输出模式,但无法触发 IO 中断。ESP8266内置了一个10-bit精度的SAR ADC。其中,在四线(QUAD)模式Flash下,有6个IO口用于Flash通讯,还有两个用做串口UART0通信,最终剩下10个可用IO和一个ADC的IO。

       该系统使用到的设备有126*64的OLED显示屏、温湿度DHT11、光敏电阻、蜂鸣器、光电门、板载的LED。其中LED 、DHT11、蜂鸣器、光电门单个引脚就可以控制了,esp8266有专门数模转换的ADC引脚可以连接光敏电阻;这里驱动OLED用的是SPI协议,需要时钟线(SCK)、数据线(MOSI)、数据/命令选择管脚(DC)还有片选(CS),复位(RES)和esp8266的复位连接,与其同时复位。

具体分配如下表:

LED

GPIO2

DHT11

GPIO5

蜂鸣器

GPIO4

光电门

GPIO12

光敏电阻

ADC0

OLED显示屏

SCK → GPIO14

MOSI → GPIO13

DC → GPIO10

CS → GPIO15

RES → RET

表1.引脚分配

 

图2.实物接线

  iii.              相关模块的使用

先在这里提供MicroPython针对ESP8266版的快速指南:Quick reference for the ESP8266

       machine模块:板级库函数,包含了和开发板相关的库函数;用到的类方法有Pin、PWM、SPI、ADC、RTC、Timer。Pin控制引脚;PWM调节LED亮度以及驱动蜂鸣器,因为我的蜂鸣器是无源的,内部不带震荡源,需要自己电路产生一定频段范围内的方波驱动它;SPI发送数据驱动OLED显示屏;ADC将光敏电阻的模拟信号转化为数字信号;RTC,提供了时钟、日期和时间功能,通过网络同步时间实现实时时钟;Timer定时器,定时产生中断,实现定时刷新数据,上传数据;

ssd1306模块:实现OLED显示相关功能;

       network模块:连接到本地WIFI网络,建立网络后,socket模块可像往常一样创建和使用 TCP/UDP 套接字;

       ntptime模块:实现同步网络时间,可修改时区和网络服务器;

       time模块:主要用来产生延时;

       umqtt.simple模块:实现连接MQTT服务,订阅/发布消息等功能;

       ujson模块:将字典格式和json格式的数据相互转换;

   iv.              大概实现

              ESP8266主控芯片启动后先初始化OLED显示屏,等待连接WIFI,获取网络时间同步到本地,初始化LED、蜂鸣器、光敏电阻、DHT11、光电门,获取传感器数据保存,连接MQTT服务,发布设备启动后获取的传感器数据,启动定时器,定时刷新传感器数据,定时上传发布传感器数据。

1.3          PC端

      i.              开发环境

       操作系统windows10,使用python3的版本是python3.8.2,编辑器还是选择Visual Studio Code

    ii.              相关模块的使用

       我用到Python模块有tkinter、time、json、paho.mqtt.client。

       tkinter是Python的标准GUI库,Python使用tkinter可以快速的创建 GUI 应用程序,相关的API接口函数介绍参考菜鸟教程:Python GUI编程(Tkinter)

       time模块本系统主要用来获取本地时间。

       paho.mqtt.client用来实现了MQTT通信,连接服务器,订阅和发布消息,断开连接等。paho.mqtt模块使用和API分析可以参考:Python paho.mqtt 模块使用和API分析

  iii.              大概实现

       UI界面显示温湿度、光照强度、门状态、还有LED和蜂鸣器的开还是关,可以通过按钮控制LED和蜂鸣器了开关,LED还可以通过滑块控制它的亮度。还有三个按钮,连接和断开服务器,以及一个用来刷新当前状态信息。连接成功后按键会变红,而且下发一条获取状态的指令,即连接成功后尝试与设备同步信息。

图3.PC端UI界面

3.       实现代码

1.1     服务器

服务器不是自己搭建的,选择第三方,这里就没有实现代码;创建对应用户、身份和主题。

图4.创建用户

图5.创建身份

图6.创建策略,以及设置主题

 

1.2          设备端

      i.              device——led

       创建了一个led类,实例化时传入引脚号;类方法有:打开、关闭和翻转LED、获取LED状态、设置和获取LED亮度值;

#===========================led===============================  
class led:  
    def __init__(self, Pin_num):  
        self.device = PWM(Pin(Pin_num, Pin.OUT), freq=1000, duty=1023)  
  
    def on(self):       #打开  
        self.device.duty(0)  
    def off(self):      #关闭  
        self.device.duty(1023)  
    def toggle(self):   #翻转  
        if self.device.duty() == 1023:  #关闭状态  
            self.device.duty(0)  
        else:  
            self.device.duty(1023)  
  
    def value(self):    #返回现在状态  
        return 0 if (self.device.duty() == 1023) else 1  
    def set_light(self, LED_LIGHT: int):        #设置亮度值  
        self.device.duty(int(1023-LED_LIGHT*10.23))  
    def get_light(self):    #获取亮度值  
        return int((1023 - self.device.duty())//10.23)  

 

    ii.              device——蜂鸣器

       创建了一个beep类,实例化时传入引脚号;类方法有:打开、关闭蜂鸣器、获取蜂鸣器状态;

#===========================beep===============================  
class beep:  
    def __init__(self, Pin_num):  
        self.device = PWM(Pin(Pin_num, Pin.OUT, value=0), freq=1000, duty=1023)  
    def on(self):  
        self.device.duty(512)  
    def off(self):  
        self.device.duty(1023)  
    def value(self):  
        return 0 if (self.device.duty() == 1023) else 1  

 

  iii.              device——DHT11温湿度传感器

       创建了一个dht11类,实例化时传入引脚号;类方法有:DHT11初始化和获取温湿度;

#===========================dht11===============================  
class dht11:  
    def __init__(self, Pin_num):  
        self.device = dht.DHT11(Pin(Pin_num))  
        self.device.measure()  
        time.sleep(2)  
    def init(self):     # 成功返回1,超时返回0  
        try:  
            self.device.measure()  
            return 1  
        except OSError:  
            print("DHT11 ETIMEDOUT")  
            return 0  
    def get_data(self): # 以列表的形式返回温湿度  
        return self.device.temperature(), self.device.humidity()  

 

   iv.              device——光敏电阻

  创建了一个light类,实例化时传入引脚号;类方法有:获取光照亮度值;

#===========================light===============================  
class light:  
    def __init__(self):  
        self.device = ADC(0)  
    def read(self):  
        return int((1023 - self.device.read())/10.23)  

 

    v.              device——实例化

#--------------------Device_init---------------------------  
LED = led(2)            #LED  
BEEP = beep(4)          #蜂鸣器  
LIGHT = light()         #光敏电阻  
PHOTOGATE = Pin(12, Pin.IN) #光电门  
DHT11 = dht11(5)        #DHT11温湿度  

 

   vi.              device——OLED显示

初始化SPI引脚,初始化OLED显示屏

#--------------------OLED_INIT----------------------------------  
OLED_SPI = SPI(baudrate=10000000, polarity=1, phase=0, sck=Pin(14, Pin.OUT), mosi=Pin(13, Pin.OUT), miso=Pin(12))  
display = ssd1306.SSD1306_SPI(128, 64, OLED_SPI, dc=Pin(10), cs=Pin(15) ,res=Pin(4))  
display.poweron()  #OLED使能 
display.init_display()  #初始化  
  
display.fill(0)  #清空缓冲区  
display.text(\'----NODEMCU----\', 1, 1)  
display.text(\'Wait Wifi\', 1, 16)  
display.text(\'Connect to AP ..\', 1, 32)  
display.text(\'......\', 1, 48)  
display.show()  #显示 

 

 vii.              连接WIFI

       连接wifi只需几行代码就好了,先激活STA模式,就可以输入名字密码连接wifi了;这里我判断如果没有连接上wifi,就循环阻塞,后续在加入其他的配网方式。

#--------------------WIFI----------------------------------  
sta = network.WLAN(network.STA_IF)  # 创建sta  
sta.active(True)            # 激活sta模式  
sta.scan()                  #扫描附加wifi  
sta.connect(\'zzz2\', \'87654321\') # 连接到wifi  
while not sta.isconnected():  
    pass

 

viii.  同步网络时间

       创建两个方法,一个用来同步网络时间,一个用来获取本地时间

def sync_time():  
    ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0  
    ntptime.host = \'ntp1.aliyun.com\'  # 可选,ntp服务器,默认是"pool.ntp.org"  
    ntptime.settime()   # 修改设备时间,到这就已经设置好了  
  
def get_datetime():  
    rtc = RTC()  
    d_t = rtc.datetime()  
    now_date = \'%04d/%02d/%02d\'%(d_t[0], d_t[1], d_t[2])  
    now_time = \'%02d:%02d:%02d\'%(d_t[4], d_t[5], d_t[6])  
    return now_date, now_time  

#使用方法
sync_time() #同步网络时间  
NOW_DATE, NOW_TIME = get_datetime() #获取本地RTC实时时钟时间  

 

   ix.              光电门实现中断服务

       光电门用来判断门的开关,需要做到实时判断,因此加入中断服务;当引脚产生上升沿或者下降沿时触发中断,中断服务函数:失能全部中断服务并保存当前中断使能状态,防止其他中断干扰,打开蜂鸣器,记录门的开关状态,恢复中断状态;定时器定时一段时间触发单次中断任务关闭蜂鸣器,最后发布门的状态信息。

#--------------------IRQ----------------------------------  
def photogate_irq(Pin_num): #中断服务函数  
    state = machine.disable_irq()  
    global DOOR  
    print(Pin_num)  
    BEEP.on()  
    DOOR[\'2\'] = PHOTOGATE.value()  
    machine.enable_irq(state)  
    Timer(1).init(period=200, mode=Timer.ONE_SHOT, callback=lambda Tim: BEEP.off())  
    pub_msg([DOOR,])  
  #上升下降沿都触发  
PHOTOGATE.irq(handler=photogate_irq,trigger=Pin.IRQ_FALLING|Pin.IRQ_RISING)

 

    x.              字典数据格式的全局变量

DOOR = {\'1\' : \'DOOR\', \'2\' : 0}  #
BUZZER = {\'1\' : \'BUZZER\', \'2\' : 0}  #蜂鸣器  
LED_LIGHT = {\'1\' : \'LED_LIGHT\', \'2\' : 0}#LED亮度  
ILLUMINATION = {\'1\' : \'ILLUMINATION\', \'2\' : 0}#光照强度  
HUMIDITY = {\'1\' : \'HUMIDITY\', \'2\' : {\'TEMP\' : 0, \'HUMI\' : 0}}#温湿度  

 

   xi.              Mqtt连接服务

Mqtt连接参数声明

# MQTT服务器地址域名  
SERVER = "nkqhkzm.mqtt.iot.gz.baidubce.com"  
CLIENT_ID = "666"  #设备ID  
USERNAME = \'nkqhkzm/esp8266\'  #产品用户名  
PASSWORD = \'MC5asL3iW6HEaY5p\'  #产品APIKey:  
#订阅主题需与平台一致  
SUB_TOPIC = "/light/deviceIn"  
PUB_TOPIC = "/light/deviceOut"  

 

订阅消息回调函数,解析指令做出响应

def sub_cb(topic: str, msg: str):  
    print((topic, msg))  
    global BUZZER, LED_LIGHT, ILLUMINATION, HUMIDITY  
    cmd = ujson.loads(msg.decode())  
    if list(cmd.keys()) in [[\'1\', \'2\'], [\'2\', \'1\']]:  
        if cmd[\'1\'] == \'LED\':   #LED开关  
            if cmd[\'2\']:  
                LED.on()  
            else:  
                LED.off()  
            LED_LIGHT[\'2\'] = LED.get_light()  
        elif cmd[\'1\'] == \'LED_LIGHT\':   #调节LED亮度  
            if 0 <= cmd[\'2\'] <= 100:  
                LED.set_light(cmd[\'2\'])  
                LED_LIGHT[\'2\'] = LED.get_light()  
        elif cmd[\'1\'] == \'BUZZER\':  #蜂鸣器开关  
            if cmd[\'2\']:  
                BEEP.on()  
            else:  
                BEEP.off()  
        elif cmd[\'1\'] == \'GET_MSG\': #获取设备全部数据  
            BUZZER[\'2\'] = BEEP.value()  
            DOOR[\'2\'] = PHOTOGATE.value()  
            LED_LIGHT[\'2\'] = LED.get_light()  
            ILLUMINATION[\'2\'] = LIGHT.read()  
            if DHT11.init():  
                HUMIDITY[\'2\'][\'TEMP\'], HUMIDITY[\'2\'][\'HUMI\'] = DHT11.get_data()  
            pub_msg([BUZZER, LED_LIGHT, ILLUMINATION, HUMIDITY, DOOR])  

 

封装发布消息方法:传入列表,方法内遍历列表,判断是字典数据才加装为json格式进行发送;

def pub_msg(msg: list):  
    for i in msg:  
        if type(i) is dict:  
            MQTT_C.publish(PUB_TOPIC, ujson.dumps(i))  

 

连接Mqtt服务,这里采用的是TCP的方式连接,端口号就是1883,心跳设置为600秒

#端口号为:1883  
MQTT_C = mqtt.MQTTClient(CLIENT_ID, SERVER, 1883, USERNAME, PASSWORD, 600)  
MQTT_C.set_callback(sub_cb) #声明回调函数  
MQTT_C.connect()            #连接  
MQTT_C.subscribe(SUB_TOPIC) #订阅主题  
print("Connected to %s, subscribed to %s topic" % (SERVER, SUB_TOPIC))  

 

 xii.              定时刷新上传数据

       采用定时器中断实现定时刷新数据以及上传,这里定时30秒产生一次定时器中断;

#--------------------TIMER----------------------------------  
def timer0_func(Tim):   #定时器中断服务函数  
    state = machine.disable_irq()  
    global BUZZER, LED_LIGHT, ILLUMINATION, HUMIDITY  
    BUZZER[\'2\'] = BEEP.value()  
    DOOR[\'2\'] = PHOTOGATE.value()  
    LED_LIGHT[\'2\'] = LED.get_light()  
    ILLUMINATION[\'2\'] = LIGHT.read()  
    if DHT11.init():  
        HUMIDITY[\'2\'][\'TEMP\'], HUMIDITY[\'2\'][\'HUMI\'] = DHT11.get_data()  
    pub_msg([BUZZER, LED_LIGHT, ILLUMINATION, HUMIDITY, DOOR])  
    print(\'publish msg\', Tim)  
    machine.enable_irq(state)  
      
TIMER0_TASK = Timer(0)  
TIMER0_TASK.init(period=30000, mode=Timer.PERIODIC, callback=timer0_func)  

 

xiii.  最终OLED显示时间、光照强度、温湿度

       循环每隔200毫秒刷新一次状态,以及检查是否有订阅消息到达;

try:  
    while 1:  
        NOW_DATE, NOW_TIME = get_datetime()  
        ILLUMINATION[\'2\'] = LIGHT.read()  
        display.fill(0)  
        display.text(NOW_DATE[5:]+\'  \'+NOW_TIME, 1, 1)  
        display.text(\'LIGHT:%d%%\'%ILLUMINATION[\'2\'], 1, 16)  
        display.text(\'TEMP:%d\\'C\'%HUMIDITY[\'2\'][\'TEMP\'], 1, 31)  
        display.text(\'HUMI:%d%%RH\'%HUMIDITY[\'2\'][\'HUMI\'], 1, 46)  
        display.show()  
        MQTT_C.check_msg()  #检查是否有订阅消息  
        time.sleep_ms(100)  
finally:  
    MQTT_C.disconnect()  

1.3          PC端

      i.              UI界面

       实例化一个Tk窗口,通过标签Label加载背景图片,加入相关的标签,通过Label类的place方法调节标签的位置以及大小

#============================tkinter===========================  
root= Tk()  
root.geometry(\'250x450\') # 这里的乘号不是 * ,而是小写英文字母 x  
root.title(\'智能XXX\')  
#增加背景图片  
photo = PhotoImage(file="背景.png")  
theLabel = Label(root, justify=LEFT, image=photo, compound = CENTER) #关键:设置为背景图片   
theLabel.place(x=0, y=14, width=250, height=436)  

 

    ii.              时间显示

#============================TIME============================== 
def gettime():  
    timestr = time.strftime("%Y/%m/%d - %H:%M:%S") # 获取当前的时间并转化为字符串  
    time_lb.configure(text=timestr)     # 重新设置标签文本  
    root.after(1000,gettime)            # 每隔1s调用函数 gettime 自身获取时间  
  
time_lb = Label(root,text=\'\',fg=\'blue\',font=("黑体",12), relief=RIDGE)  
time_lb.place(x=0, y=0, width=250, height=20)  
gettime()  

 

  iii.              温度显示

#============================TEMP==============================
Label(root, anchor=E, text=\'温度 :\', bg=\'#98F555\', fg=\'blue\',\  
        font=(\'黑体\',16), width=6, height=1,\  
        relief=GROOVE).place(x=35, y=25, width=100, height=30)  
temp = Label(root, text=\'---\', bg=\'#d3fbfb\', \  
        font=(\'黑体\',16), width=6, height=1, relief=GROOVE)  
temp.place(x=145, y=25, width=80, height=30)  
 

 

   iv.              湿度显示

#============================HUMI==============================
Label(root, anchor=E, text=\'湿度 :\', bg=\'#98F555\', fg=\'blue\',\  
        font=(\'黑体\',16), width=6, height=1,\  
        relief=GROOVE).place(x=35, y=55, width=100, height=30)  
humi = Label(root, text=\'---\', bg=\'#d3fbfb\', \  
        font=(\'黑体\',16), width=6, height=1, relief=GROOVE)  
humi.place(x=145, y=55, width=80, height=30)  
 

 

    v.              光照亮度显示

#============================LIGHT============================= 
Label(root, anchor=E, text=\'光照 :\', bg=\'#98F555\', fg=\'blue\',\  
        font=(\'黑体\',16), width=6, height=1,\  
        relief=GROOVE).place(x=35, y=85, width=100, height=30)  
light = Label(root, text=\'---\', bg=\'#d3fbfb\',\  
        font=(\'黑体\',16), width=6, height=1, relief=GROOVE)  
light.place(x=145, y=85, width=80, height=30)  

 

   vi.              门状态显示

#============================DOOR==============================
Label(root, anchor=E, text=\'门 :\', bg=\'#98F555\', fg=\'blue\',\  
        font=(\'黑体\',16), width=6, height=1,\  
        relief=GROOVE).place(x=35, y=115, width=100, height=30)  
door = Label(root, text=\'---\', bg=\'#d3fbfb\', \  
        font=(\'黑体\',16), width=6, height=1, relief=GROOVE)  
door.place(x=145, y=115, width=80, height=30)  

 vii.              LED开关

#============================LED=============================== 
Label(root, anchor=E, text=\'LED :\', bg=\'#98F555\', fg=\'blue\',\  
        font=(\'黑体\',16), width=6, height=1,\  
        relief=GROOVE).place(x=35, y=145, width=100, height=30)  
def led_button():   #LED开关按钮事件  
    if mqttc.is_connected():  
        global LED_CMD, LED_LIGHT_CMD  
        LED_CMD[\'2\'] = not LED_CMD[\'2\']  
        LED_LIGHT_CMD[\'2\'] = (100 if LED_CMD[\'2\'] else 0)  
        mqttc.publish(pub_topic, payload=json.dumps(LED_CMD), qos=0)  
        led.configure(text=(\'on\' if LED_CMD[\'2\'] else \'off\'), bg=\'#E37C7E\')  
        led.configure(bg=(\'#E37C7E\' if LED_CMD[\'2\'] else \'#F0F0F0\'))  
        led_light.set(LED_LIGHT_CMD[\'2\'])  
        led_light.config(label=\'LED亮度:%d\'%LED_LIGHT_CMD[\'2\'])  
    else:  
        tkinter.messagebox.askokcancel(\'提示\',\'No connect !\')  
      
led = Button(root, text=\'off\', bg=\'#F0F0F0\',\  
        font=(\'黑体\',16), width=6, height=1, relief=RAISED, command=led_button)  
led.place(x=165, y=145, width=40, height=30)  

 

viii.  LED亮度调节滑块

#============================LED_LIGHT========================= 
led_light = Scale(root, orient=HORIZONTAL, length=222, from_=0, to=100, font=(\'黑体\',12), \  
    label=\'LED亮度:%d\'%(0), tickinterval=20, resolution=1)  
led_light.place(x=14, y=280)  
 
def set_led_light(event):   #调节LED亮度滑块触发事件  
    if mqttc.is_connected():  
        global LED_LIGHT_CMD, LED_CMD  
        LED_LIGHT_CMD[\'2\'] = led_light.get()  
        LED_CMD[\'2\'] = 0 if LED_LIGHT_CMD[\'2\']==0 else 1  
        led_light.config(label=\'LED亮度:%d\'%LED_LIGHT_CMD[\'2\'])  
        mqttc.publish(pub_topic, payload=json.dumps(LED_LIGHT_CMD), qos=0)  
        led.configure(text=(\'on\' if LED_CMD[\'2\'] else \'off\'), bg=\'#E37C7E\')  
        led.configure(bg=(\'#E37C7E\' if LED_CMD[\'2\'] else \'#F0F0F0\'))  
    else:  
        tkinter.messagebox.askokcancel(\'提示\',\'No connect !\')  
  
led_light.bind(\'<ButtonRelease-1>\',set_led_light) #滑块绑定处理函数  

 

   ix.              蜂鸣器开关

#============================BEEP============================== 
Label(root, anchor=E, text=\'蜂鸣器 :\', bg=\'#98F555\', fg=\'blue\',\  
        font=(\'黑体\',16), width=6, height=1,\  
        relief=GROOVE).place(x=35, y=175, width=100, height=30)  
def beep_button():  #蜂鸣器开关按钮事件  
    if mqttc.is_connected():  
        global BEEP_CMD  
        BEEP_CMD[\'2\'] = not BEEP_CMD[\'2\']  
        mqttc.publish(pub_topic, payload=json.dumps(BEEP_CMD), qos=0)  
        beep.configure(text=(\'on\' if BEEP_CMD[\'2\'] else \'off\'), bg=\'#E37C7E\')  
        beep.configure(bg=(\'#E37C7E\' if BEEP_CMD[\'2\'] else \'#F0F0F0\'))  
    else:  
        tkinter.messagebox.askokcancel(\'提示\',\'No connect !\')  
  
beep = Button(root, text=\'off\', bg=\'#F0F0F0\',\  
        font=(\'黑体\',16), width=6, height=1, relief=RAISED, command=beep_button)  
beep.place(x=165, y=175, width=40, height=30)  

 

    x.              连接、断开连接以及刷新按钮

#============================CONNECT==========================
def connect_func(): #连接按钮  
    if not mqttc.is_connected():  
        mqttc.connect(host, port, 600)  
        mqttc.subscribe(sub_topic, 0)  
        mqttc.loop_start()  
    else:  
        tkinter.messagebox.askokcancel(\'提示\',\'Is connected !\')  
  
connect = Button(root, text=\'连接\', bg=\'#F0F0F0\',\  
        font=(\'黑体\',16), width=6, height=1, relief=RAISED, command=connect_func)  
connect.place(x=20, y=222, width=50, height=30)  
#============================DISCONNECT=======================
def disconnect_func(): #断开连接按钮  
    if mqttc.is_connected():  
        if tkinter.messagebox.askokcancel(\'提示\',\'Do you sure disconnect?\'):  
            mqttc.loop_stop()  
            mqttc.disconnect()  
        else:  
            tkinter.messagebox.askokcancel(\'提示\',\'不断开连接你点个锤子 !\')  
    else:  
        tkinter.messagebox.askokcancel(\'提示\',\'No connect !\')  
 
disconnect = Button(root, text=\'断开连接\', bg=\'#F0F0F0\',\  
        font=(\'黑体\',16), width=6, height=1, relief=RAISED, command=disconnect_func)  
disconnect.place(x=75, y=222, width=100, height=30)  
#============================REFRESH===========================
def refresh_func():   #刷新数据按钮  
    if mqttc.is_connected():  
        mqttc.publish(pub_topic, payload=json.dumps({"1": "GET_MSG", "2": 1}), qos=0) 
    else:  
        tkinter.messagebox.askokcancel(\'提示\',\'No connect !\')  
  
refresh = Button(root, text=\'刷新\', bg=\'#F0F0F0\', \  
        font=(\'黑体\',16), width=6, height=1, relief=RAISED, command=refresh_func)  
refresh.place(x=180, y=222, width=50, height=30)  

 

   xi.              Mqtt连接参数

# 连接参数  
host = "nkqhkzm.mqtt.iot.gz.baidubce.com"   # 服务器地址  
port = 1883                     # 通信端口  
username = \'nkqhkzm/esp8266\'    # 用户名  
password = \'MC5asL3iW6HEaY5p\'   # 密码  
sub_topic = \'/light/deviceOut\'  # 订阅主题名  
pub_topic = \'/light/deviceIn\'   # 发布主题名 

 

 xii.              Mqtt相关回调函数

# 代理响应连接请求时调用  
def on_connect(mqttc, obj, flags, rc):  
    print("OnConnect, rc: "+str(rc))  
    if rc == 0:  
        mqttc.publish(pub_topic, payload=json.dumps({"1": "GET_MSG", "2": 1}), qos=0) 
        connect.configure(bg=\'#E37C7E\' )  
        tkinter.messagebox.askokcancel(\'提示\',\'Connect is succeeded !\')    
    else:  
        tkinter.messagebox.askokcancel(\'提示\',\'Connect is failed !\')  
# 当与代理断开连接时调用  
def on_disconnect(mqttc, obj, rc):  
    print("OnDisconnect, rc: "+str(rc))  
    connect.configure(bg=\'#F0F0F0\' )  
    tkinter.messagebox.askokcancel(\'提示\',\'Disconnect is succeeded !\')  
# 当使用使用publish()发送的消息已经传输到代理时被调用  
def on_publish(mqttc, obj, mid):  
    print("OnPublish, mid: "+str(mid))  
# 当代理响应订阅请求时被调用  
def on_subscribe(mqttc, obj, mid, granted_qos):  
    print("Subscribed: "+str(mid)+" "+str(granted_qos))  
# 当客户端有日志信息时调用  
def on_log(mqttc, obj, level, string):  
    print("Log:"+string)  
#当收到关于客户订阅的主题的消息时调用   
def on_message(mqttc, obj, msg):  
    strcurtime = time.strftime("%Y-%m-%d %H:%M:%S")  
    print(strcurtime + ": " + msg.topic+" "+str(msg.qos)+" "+str(msg.payload))  
    on_exec(msg.payload)  
#指令全局变量  
LED_CMD = {\'1\' : \'LED\', \'2\' : 0}  
LED_LIGHT_CMD = {\'1\' : \'LED_LIGHT\', \'2\' : 0}  
BEEP_CMD = {\'1\' : \'BUZZER\', \'2\' : 0}  
#订阅消息到达,消息处理函数  
def on_exec(strcmd):      
    cmd = json.loads(strcmd.decode())  
    if list(cmd.keys()) in [[\'1\', \'2\'], [\'2\', \'1\']]:  
        if cmd[\'1\'] == \'LED_LIGHT\':  
            global LED_CMD, led_light, LED_LIGHT_CMD  
            if cmd[\'2\']:  
                LED_CMD[\'2\'] = True  
                LED_LIGHT_CMD[\'2\'] = cmd[\'2\']  
                led_light.set(cmd[\'2\'])  
                led.configure(text=\'on\', bg=\'#E37C7E\')  
                led.configure(bg=\'#E37C7E\')  
            else:  
                LED_CMD[\'2\'] = False  
                led_light.set(cmd[\'2\'])  
                led.configure(text=\'off\', bg=\'#E37C7E\')  
                led.configure(bg=\'#F0F0F0\')  
        elif cmd[\'1\'] == \'BUZZER\':  
            global BEEP_CMD  
            if cmd[\'2\']:  
                BEEP_CMD[\'2\'] = True  
                beep.configure(text=\'on\', bg=\'#E37C7E\')  
                beep.configure(bg=\'#E37C7E\')  
            else:  
                BEEP_CMD[\'2\'] = False  
                beep.configure(text=\'off\', bg=\'#E37C7E\')  
                beep.configure(bg=\'#F0F0F0\')  
        elif cmd[\'1\'] == \'ILLUMINATION\':  
            if 0<= cmd[\'2\'] <= 100:  
                light.configure(text=\'%d%%\'%cmd[\'2\'])  
            else:  
                light.configure(text=\'---\')  
        elif cmd[\'1\'] == \'HUMIDITY\':  
            temp.configure(text=\'%d°C\'%cmd[\'2\'][\'TEMP\'])  
            humi.configure(text=\'%d%%RH\'%cmd[\'2\'][\'HUMI\'])  
        elif cmd[\'1\'] == \'DOOR\':  
            if cmd[\'2\']:  
                door.configure(text=\'关闭\')  
            else:  
                door.configure(text=\'敞开\')  

 

xiii.  Mqtt参数赋值

1 mqttc = mqtt.Client("6666666")    
2 mqttc.on_message = on_message  #订阅消息到达  
3 mqttc.on_connect = on_connect  #连接回调函数  
4 mqttc.on_disconnect = on_disconnect  #断开连接回调  
5 mqttc.on_publish = on_publish  #发布消息回调  
6 mqttc.on_subscribe = on_subscribe  #订阅消息回调  
7 mqttc.on_log = on_log  #客户端有日志信息回调  
8 mqttc.username_pw_set(username, password) #这只用户密码  

 

4.       实验结果

1.1    设备端

图7.开机后效果

  遮挡光电门,蜂鸣器会发声,而且PC端门的状态会显示关闭,PC端调节LED亮度

图8.遮挡光电门,调节LED亮度

1.2          PC端

  连接状态连接按钮会变红,LED和蜂鸣器打开状态按钮也会变红。通过滑块调节LED亮度。

图9.PC端效果

5.       总结和展望

      实现功能就是上面所介绍的。本项目提高了自己使用python模块编程的能力,以及加深了对MQTT通信协议的理解,同时会使用tkinter模块实现简单的UI界面,Get到了新的硬件编程方式,加强硬件编程能力,更加熟悉传感器的使用。本项目相对比较简单,不难理解,不过存在相当多的不足之处,比如esp8266断网后的重连问题、一些异常没有捕获进行处理等;同时功能也不够完事,如:设备低功耗模式、光照变化调节灯光功能、温湿度过高预警功能、开门关门记录时间功能等等。

   

参考文献:

[1] MicroPython针对ESP8266版的快速指南:Quick reference for the ESP8266  

[2] Python GUI编程(Tkinter)  

[3] Python paho.mqtt 模块使用和API分析

 



[1] 刘陈, 景兴红, 董钢. 浅谈物联网的技术特点及其广泛应用[J]. 科学咨询, 2011(9):86-86.

分类:

技术点:

相关文章: