物联网开发笔记(9)- 使用Wokwi仿真MicroPython on ESP32开发板实现温度和湿度检测并使用屏幕显示

一、测试环境

我们同样使用在Wokwi网站上选择Micropython with ESP32进行仿真,来进行温度和湿度的检测。

ESP32官方技术参考手册:

https://www.espressif.com.cn/sites/default/files/documentation/esp32_technical_reference_manual_en.pdfhttps://www.espressif.com.cn/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf中文资料可查看乐鑫官网:

技术文档 | 乐鑫科技Downloads: SDK & Demos, APKs, Tools and Documents for Espressif Systems products and solutionshttps://www.espressif.com.cn/zh-hans/support/documents/technical-documents

二、硬件环境

温度湿度DHT22使用说明和显示屏ssd1366使用说明

wokwi-dht22 Reference | Wokwi DocsDigital Humidity and Temperature sensor.https://docs.wokwi.com/parts/board-ssd1306三、代码说明

main.py

'''
oled温湿度报警,
可手动设定警戒值的报警装置,可以用于一些特定环境的温湿度控制
可以自己设定间隔时间
可以通过串口进行最高最低温度的设置等,省的设置一堆按钮显得乱
'''
from machine import Pin,PWM,I2C,Timer,UART
import time,machine,ssd1306,dht

uart1=UART(1,115200)  #调用串口uart1
uart1.init(115200,bits=8,parity=None,stop=1)  #初始化串口相关参数
Tim_S=Timer(0)   #定时器对象,很怪,有了这个定时器,下面的蜂鸣器没有了短促的鸣叫
key=Pin(27, Pin.OUT)  # 蜂鸣器接GPIO27口
Buzzer= PWM(key)    #定义蜂鸣器
Buzzer.duty(0)      #控制蜂鸣器初始关闭状态
global data
data = dht.DHT22(Pin(15))  #实例化15号管脚,21被占用了
i2c = I2C(0, scl=Pin(22), sda=Pin(21))   #对应管脚
oled_width = 128  ##画幅大小。oled屏幕宽度128
oled_height = 64   #画幅大小。oled屏幕高度64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)   #调用,设置像素大小
i=0
space=3000   #设置数据更新时间,默认三秒,可修改但是要为整型。
point=['','.','..','...']   #oled显示“...”动态,表示在工作
Warn_tem_Max=29.0  # 设置最高温度。可修改为浮点型
Warn_tem_Min=26.0  # 设置最高温度。可修改为浮点型
Warn_hum_Max=60.0  # 设置最大湿度。可修改为浮点型
Warn_hum_Min=40.0  # 设置最小湿度。可修改为浮点型
L_Blue=Pin(2,Pin.OUT)  #内置小灯,判断状态,命令设置成功闪亮

def Voice_On():   #开启调用
  Buzzer.duty(512)   #设置蜂鸣器占空比
  Buzzer.freq(2000)  #设置蜂鸣器频率
  time.sleep(0.5)
  Buzzer.duty(0)
  time.sleep(0.5)

def Voice_Off():   #关闭调用
  Buzzer.duty(0)   #蜂鸣器关闭

def Open_Test(t):
  data.measure()     #先调用测量函数,获取温湿度
  global temp
  temp=data.temperature()
  global humi
  humi=data.humidity()

def max_or_min():
  global tem_Max
  global tem_Min
  global hum_Max
  global hum_Min
  if int(temp)>tem_Max:
    tem_Max=int(temp)
  if int(temp)<tem_Min:
    tem_Min=int(temp)
  if int(humi)>hum_Max:
    hum_Max=int(humi)
  if int(humi)<tem_Max:
    hum_Min=int(humi)

def Print():
  global i
  oled.text('Temp:'+str(temp), 0, 12)
  oled.text('Max:'+str(tem_Max), 0, 26)    #空一段,最大最小
  oled.text('Min:'+str(tem_Min), 64, 26)
  oled.text('Humi:'+str(humi), 0,40)
  oled.text('Max:'+str(hum_Max), 0, 57)    
  oled.text('Min:'+str(hum_Min), 64, 57)
  oled.text('working'+point[i%4], 25, 0)
  oled.show()
  time.sleep(0.2)
  oled.fill(0)
  i=i+1

def Is_Int(Dir):   #异常处理。整型
  try:
    int(Dir)
    return True
  except ValueError:
    pass

def Is_Float(Dir):   #异常处理。浮点型
  try:
    float(Dir)
    return True
  except ValueError:
    pass

def twink():
  L_Blue.value(1)
  time.sleep(0.5)
  L_Blue.value(0)
  time.sleep(0.5)
  L_Blue.value(1)
  time.sleep(0.5)
  L_Blue.value(0)
  time.sleep(0.5)

oled.fill(0) #清屏
oled.text('Welcome!', 10, 10)   #欢迎界面   
oled.show()
time.sleep(0.5)
oled.fill(0) #清屏

Open_Test(0)  #先调用,才能全局改变下面的变量
Tim_S.init(period=space,mode=Timer.PERIODIC,callback=Open_Test)
#效果是到定义的时间就调用函数收集一下数据,但是程序依然在运行,到主函数后会因为未到指定时间
#而不启用收集函数,导致主函数出错,所以需要在定时器之前先调用一下函数,
#还有种办法,可以通过延时来达到等待函数运行后再去显示数据,但是因为后续会有自定义收集间隔
#所以如果通过延时来处理还需要让延时时间变量与间隔相同
#time.sleep(n)

tem_Max=int(temp)  #控制空间,整数,大概
tem_Min=int(temp)
hum_Max=int(humi)
hum_Min=int(humi)

#主程序负责数据的对比,定时器外部中断优先,保障间隔的准确
while True:   #循环获取温湿度
  if Warn_tem_Min<temp<Warn_tem_Max and Warn_hum_Min<humi<Warn_hum_Max:
    Voice_Off()
  else:
    Voice_On()
  max_or_min()
  Print()
  #大概有几个口令:警报最大最小温度,湿度;采集时间间隔就这五个,
  #注意判断类型,命令或类型不正确的不予理会,设置成功有闪灯提醒
  if uart1.any()>0:   #考虑放在定时器中,因为循环内很难监测到
    Dir=uart1.read()
    Dir=str(Dir)     #转换为字符串型
#Dir=Dir.lower(),字符小写就涉及到回车的问题,无法转变,会异常,所以我可以提前切出来想要的部分
    Dir=Dir[2:-3]  #提前切掉b'...\n'这个东西,在让这个正常的字符串去变小写
    if Dir.find('space')>=0:
      Dir=Dir[5:]
      if Is_Int(Dir)==True:
        space=int(Dir)
        Tim_S.init(period=space,mode=Timer.PERIODIC,callback=Open_Test)
        twink()
    elif Dir.find('tmax')>=0:
      Dir=Dir[4:]
      if Is_Float(Dir)==True:
        Warn_tem_Max=float(Dir)
        twink()
    elif Dir.find('tmin')>=0:
      Dir=Dir[4:]
      if Is_Float(Dir)==True:
        Warn_tem_Min=float(Dir)
        twink()
    elif Dir.find('hmax')>=0:
      Dir=Dir[4:]
      if Is_Float(Dir)==True:
        Warn_hum_Max=float(Dir)
        twink()
    elif Dir.find('hmin')>=0:
      Dir=Dir[4:]
      if Is_Float(Dir)==True:
        Warn_hum_Min=float(Dir)
        twink()
#串口这部分也可以考虑写成函数,看起来能简洁些

diagram.json

{
  "version": 1,
  "author": "魔都飘雪",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-esp32-devkit-v1", "id": "esp", "top": -68.61, "left": -8.02, "attrs": {} },
    { "type": "wokwi-dht22", "id": "dht1", "top": -71.8, "left": 118.28, "attrs": {} },
    { "type": "board-ssd1306", "id": "oled1", "top": 23.53, "left": -135.32, "attrs": {} },
    {
      "type": "wokwi-buzzer",
      "id": "bz1",
      "top": -81.64,
      "left": -146.94,
      "attrs": { "volume": "0.5" }
    }
  ],
  "connections": [
    [ "esp:TX0", "$serialMonitor:RX", "", [] ],
    [ "esp:RX0", "$serialMonitor:TX", "", [] ],
    [ "dht1:VCC", "esp:3V3", "red", [ "v0" ] ],
    [ "dht1:SDA", "esp:D15", "green", [ "v0" ] ],
    [ "dht1:GND", "esp:GND.1", "black", [ "v0" ] ],
    [ "oled1:VCC", "esp:3V3", "red", [ "v-12.04", "h-51.43", "v113.03", "h241.73", "v-40.86" ] ],
    [
      "oled1:GND",
      "esp:GND.2",
      "black",
      [ "v-12.72", "h-41.98", "v114.4", "h127.33", "v-50.39" ]
    ],
    [ "oled1:SDA", "esp:D21", "green", [ "v-103.15", "h173.6", "v86.85" ] ],
    [ "bz1:1", "esp:GND.2", "green", [ "v8.67", "h101.46", "v72.79" ] ],
    [ "bz1:2", "esp:D27", "green", [ "v8.67", "h96.78", "v53.56" ] ],
    [ "oled1:SCL", "esp:D22", "green", [ "v-108.86", "h178.16", "v59.06" ] ]
  ],
  "serialMonitor": { "display": "plotter", "newline": "lf" }
}

ssd1366.py(ssd1306屏幕的驱动)

# MicroPython SSD1306 OLED driver, I2C and SPI interfaces

from micropython import const
import framebuf
import math


# register definitions
SET_CONTRAST        = const(0x81)
SET_ENTIRE_ON       = const(0xa4)
SET_NORM_INV        = const(0xa6)
SET_DISP            = const(0xae)
SET_MEM_ADDR        = const(0x20)
SET_COL_ADDR        = const(0x21)
SET_PAGE_ADDR       = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP       = const(0xa0)
SET_MUX_RATIO       = const(0xa8)
SET_COM_OUT_DIR     = const(0xc0)
SET_DISP_OFFSET     = const(0xd3)
SET_COM_PIN_CFG     = const(0xda)
SET_DISP_CLK_DIV    = const(0xd5)
SET_PRECHARGE       = const(0xd9)
SET_VCOM_DESEL      = const(0xdb)
SET_CHARGE_PUMP     = const(0x8d)


class SSD1306:
    def __init__(self, width, height, external_vcc, color=framebuf.MONO_VLSB):
        self.width = width
        self.height = height
        self.external_vcc = external_vcc
        self.pages = self.height // 8
        self.buffer = bytearray(self.pages * self.width)
        fb = framebuf.FrameBuffer(self.buffer, self.width, self.height, color)
        self.framebuf = fb
        # Provide methods for accessing FrameBuffer graphics primitives. This is a
        # workround because inheritance from a native class is currently unsupported.
        # http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
        self.fill = fb.fill
        self.pixel = fb.pixel
        self.hline = fb.hline
        self.vline = fb.vline
        self.line = fb.line
        self.rect = fb.rect
        self.fill_rect = fb.fill_rect
        self.text = fb.text
        self.scroll = fb.scroll
        self.blit = fb.blit
        self.init_display()

    def init_display(self):
        for cmd in (
            SET_DISP | 0x00, # off
            # address setting
            SET_MEM_ADDR, 0x00, # horizontal
            # resolution and layout
            SET_DISP_START_LINE | 0x00,
            SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
            SET_MUX_RATIO, self.height - 1,
            SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
            SET_DISP_OFFSET, 0x00,
            SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12,
            # timing and driving scheme
            SET_DISP_CLK_DIV, 0x80,
            SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,
            SET_VCOM_DESEL, 0x30, # 0.83*Vcc
            # display
            SET_CONTRAST, 0xff, # maximum
            SET_ENTIRE_ON, # output follows RAM contents
            SET_NORM_INV, # not inverted
            # charge pump
            SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14,
            SET_DISP | 0x01): # on
            self.write_cmd(cmd)
        self.fill(0)
        self.show()

    def poweroff(self):
        self.write_cmd(SET_DISP | 0x00)

    def poweron(self):
        self.write_cmd(SET_DISP | 0x01)

    def contrast(self, contrast):
        self.write_cmd(SET_CONTRAST)
        self.write_cmd(contrast)

    def invert(self, invert):
        self.write_cmd(SET_NORM_INV | (invert & 1))

    def show(self):
        x0 = 0
        x1 = self.width - 1
        if self.width == 64:
            # displays with width of 64 pixels are shifted by 32
            x0 += 32
            x1 += 32
        self.write_cmd(SET_COL_ADDR)
        self.write_cmd(x0)
        self.write_cmd(x1)
        self.write_cmd(SET_PAGE_ADDR)
        self.write_cmd(0)
        self.write_cmd(self.pages - 1)
        self.write_data(self.buffer)
    #下面函数为添加的功能,根据pyboard板子厂商提供的例程修改

    def show_hanzi(self, row, col, charlist1):
        data = bytearray(charlist1)
        fbuf = framebuf.FrameBuffer(data, 16, 16, framebuf.MONO_VLSB)
        self.blit(fbuf,col,(row-1)*16)
        del fbuf

    def show_image(self, image_list):
        data = bytearray(image_list)
        fbuf = framebuf.FrameBuffer(data, 128, 96, framebuf.MONO_VLSB)
        self.blit(fbuf, 0, 0)
        del fbuf


class SSD1306_I2C(SSD1306):
    def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False, color=framebuf.MONO_VLSB):
        self.i2c = i2c
        self.addr = addr
        self.temp = bytearray(2)
        super().__init__(width, height, external_vcc, color)

    def write_cmd(self, cmd):
        self.temp[0] = 0x80 # Co=1, D/C#=0
        self.temp[1] = cmd
        self.i2c.writeto(self.addr, self.temp)

    def write_data(self, buf):
        self.i2c.writeto(self.addr, b'\x40' + buf)


class SSD1306_SPI(SSD1306):
    def __init__(self, width, height, spi, dc, res, cs, external_vcc=False, color=framebuf.MONO_VLSB):
        self.rate = 10 * 1024 * 1024
        dc.init(dc.OUT, value=0)
        res.init(res.OUT, value=0)
        cs.init(cs.OUT, value=1)
        self.spi = spi
        self.dc = dc
        self.res = res
        self.cs = cs
        import time
        self.res(1)
        time.sleep_ms(1)
        self.res(0)
        time.sleep_ms(10)
        self.res(1)
        super().__init__(width, height, external_vcc, color)

    def write_cmd(self, cmd):
        self.spi.init(baudrate=self.rate, polarity=0, phase=0)
        self.cs(1)
        self.dc(0)
        self.cs(0)
        self.spi.write(bytearray([cmd]))
        self.cs(1)

    def write_data(self, buf):
        self.spi.init(baudrate=self.rate, polarity=0, phase=0)
        self.cs(1)
        self.dc(1)
        self.cs(0)
        self.spi.write(buf)
        self.cs(1)

 是不是,很简单!来看看效果:

也可以在网页上查看实际效果:

Wokwi Arduino and ESP32 Simulatorhttps://wokwi.com/projects/341771399848788563

物联沃分享整理
物联沃-IOTWORD物联网 » 物联网开发笔记(9)- 使用Wokwi仿真MicroPython on ESP32开发板实现温度和湿度检测并使用屏幕显示

发表评论