一、测试环境
我们同样使用在Wokwi网站上选择Micropython with ESP32进行仿真,来进行温度和湿度的检测。
ESP32官方技术参考手册:
/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf中文资料可查看乐鑫官网:
技术文档 | 乐鑫科技Downloads: SDK & Demos, APKs, Tools and Documents for Espressif Systems products and solutions/zh-hans/support/documents/technical-documents
二、硬件环境
温度湿度DHT22使用说明和显示屏ssd1366使用说明
wokwi-dht22 Reference | Wokwi DocsDigital Humidity and Temperature sensor./parts/wokwi-dht22board-ssd1306 Reference | Wokwi DocsMonochrome 128x64 OLED display with I2C interface/parts/board-ssd1306三、代码说明
main.py
'''oled温湿度报警,可手动设定警戒值的报警装置,可以用于一些特定环境的温湿度控制可以自己设定间隔时间可以通过串口进行最高最低温度的设置等,省的设置一堆按钮显得乱'''from machine import Pin,PWM,I2C,Timer,UARTimport time,machine,ssd1306,dhtuart1=UART(1,115200) #调用串口uart1uart1.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 datadata = dht.DHT22(Pin(15)) #实例化15号管脚,21被占用了i2c = I2C(0, scl=Pin(22), sda=Pin(21)) #对应管脚oled_width = 128 ##画幅大小。oled屏幕宽度128oled_height = 64 #画幅大小。oled屏幕高度64oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c) #调用,设置像素大小i=0space=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 temptemp=data.temperature()global humihumi=data.humidity()def max_or_min():global tem_Maxglobal tem_Minglobal hum_Maxglobal hum_Minif 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 ioled.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+1def Is_Int(Dir): #异常处理。整型try:int(Dir)return Trueexcept ValueError:passdef Is_Float(Dir): #异常处理。浮点型try:float(Dir)return Trueexcept ValueError:passdef 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 interfacesfrom micropython import constimport framebufimport math# register definitionsSET_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 = widthself.height = heightself.external_vcc = external_vccself.pages = self.height // 8self.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.# /en/latest/pyboard/library/framebuf.htmlself.fill = fb.fillself.pixel = fb.pixelself.hline = fb.hlineself.vline = fb.vlineself.line = fb.lineself.rect = fb.rectself.fill_rect = fb.fill_rectself.text = fb.textself.scroll = fb.scrollself.blit = fb.blitself.init_display()def init_display(self):for cmd in (SET_DISP | 0x00, # off# address settingSET_MEM_ADDR, 0x00, # horizontal# resolution and layoutSET_DISP_START_LINE | 0x00,SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0SET_MUX_RATIO, self.height - 1,SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0SET_DISP_OFFSET, 0x00,SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12,# timing and driving schemeSET_DISP_CLK_DIV, 0x80,SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,SET_VCOM_DESEL, 0x30, # 0.83*Vcc# displaySET_CONTRAST, 0xff, # maximumSET_ENTIRE_ON, # output follows RAM contentsSET_NORM_INV, # not inverted# charge pumpSET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14,SET_DISP | 0x01): # onself.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 = 0x1 = self.width - 1if self.width == 64:# displays with width of 64 pixels are shifted by 32x0 += 32x1 += 32self.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 fbufdef show_image(self, image_list):data = bytearray(image_list)fbuf = framebuf.FrameBuffer(data, 128, 96, framebuf.MONO_VLSB)self.blit(fbuf, 0, 0)del fbufclass SSD1306_I2C(SSD1306):def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False, color=framebuf.MONO_VLSB):self.i2c = i2cself.addr = addrself.temp = bytearray(2)super().__init__(width, height, external_vcc, color)def write_cmd(self, cmd):self.temp[0] = 0x80 # Co=1, D/C#=0self.temp[1] = cmdself.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 * 1024dc.init(dc.OUT, value=0)res.init(res.OUT, value=0)cs.init(cs.OUT, value=1)self.spi = spiself.dc = dcself.res = resself.cs = csimport timeself.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 Simulator/projects/341771399848788563
物联网开发笔记(9)- 使用Wokwi仿真MicroPython on ESP32开发板实现温度和湿度检测并使用屏幕显示