ESP32+时钟、闹钟、温度+microPython程序 (20
2020-10-09 本文已影响0人
我开心0536
时钟、闹钟、温度程序
时钟、闹钟、温度程序 ( 我开心 2020.09.25-2020.10.09 )
【硬件】:ESP32芯片、DS3231时钟芯片、五方向按键(带set和rst按键)、有源蜂鸣器、 LCD1602液晶屏(带PCF8574芯片,注意不是背光可调多种颜色的那种RGB1602)
【软件】:microPython
1、开始自动检测WIFI,检测到之后,自动从网络获取时间,并写入esp8266/esp32、同时写入ds3231时钟芯片中
2、如果不能联网,则自动从ds3231芯片获取时间
3、按中键,可以开启关闭屏幕背光
4、按set键,可以进入菜单,可以设置日期、时间、闹钟以及闹钟是否开启(默认每天循环)
5、液晶屏幕上,时间与温度中间,如果有个.显示,表示闹钟是开启状态。 如果没有,则表示闹钟关闭
断断续续写了一个多星期,程序功能虽然不多,但费眼又烧脑!
文件夹结构lib文件夹下的几个文件必须要用,可以分别从其它文章中找到:
DS3231micro.py: 从https://www.jianshu.com/p/93af7d173f98可以复制
esp8266_i2c_lcd.py 与 lcd_api.py: 从https://www.jianshu.com/p/7798d0c06c69可以复制
下面是 clock_1008.py代码内容,完整复制就可
# 时钟、闹钟、温度程序 ( 我开心 2020.09.25-2020.10.09 )
# 硬件:ESP32芯片、DS3231时钟芯片、五方向按键(带set和rst按键)、有源蜂鸣器、 LCD1602液晶屏(带PCF8574芯片,注意不是背光可调多种颜色的那种RGB1602)
# 软件:microPython
# 1、开始自动检测WIFI,检测到之后,自动从网络获取时间,并写入esp8266/esp32、同时写入ds3231时钟芯片中
# 2、如果不能联网,则自动从ds3231芯片获取时间
# 3、按中键,可以开启关闭屏幕背光
# 4、按set键,可以进入菜单,可以设置日期、时间、闹钟以及闹钟是否开启(默认每天循环)
# 5、液晶屏幕上,时间与温度中间,如果有个.显示,表示闹钟是开启状态。 如果没有,则表示闹钟关闭
import time,ntptime #获取网络时间,校时使用
import network #导入network模块,以获取建立Wi-Fi网络连接所需的所有功能
from machine import I2C, Pin, RTC
from esp8266_i2c_lcd import I2cLcd
import DS3231micro
# 设置LCD1602液晶屏相关参数
LCD1602_I2C_ADDR = 0x27
i2c = I2C(scl=Pin(22), sda=Pin(21), freq=400000) # 22、21针脚对的是ESP32, 如果换成ESP8266,则可以使用D1针脚Pin5、D2针脚Pin4
lcd = I2cLcd(i2c, LCD1602_I2C_ADDR, 2, 16) # 指定液晶屏幕几行几列 ,LCD1602 是2行16列,左上角坐标是0,0
# 设置DS3231时钟芯片相关参数 设置针脚!
Ds3231 = DS3231micro.DS3231(22, 21)
# #########################
# 定义连接到路由器模式
wlan = network.WLAN(network.STA_IF) # 以工作站 (wlan) 模式运行,需要创建一个工作站Wi-Fi接口的实例
wlan.active(True) # 在工作站对象上调用激活方法并以True作为输入值传递来激活网络接口
#wlan.scan() # 扫描当前可连接的WiFi名称
#wlan.active(False) # 关闭WIFI
def wifi_connect():
start_time=time.time() # 记录开始时间
while time.time()-start_time<=10 and not wlan.isconnected(): # 如果尚未联网成功
print("当前未联网,正在尝试连接到无线网1...." + str(time.time()))
wlan.connect("无线网SSID","密码") # 无线网SSID、密码,开始联网
time.sleep(2)
lcd.move_to(0,0)
lcd.putstr("Not conn..."+str(time.time())+"\nconnect Wifi01")
start_time=time.time() # 记录开始时间
while time.time()-start_time<=10 and not wlan.isconnected(): # 如果尚未联网成功
print("当前未联网,正在尝试连接到无线网2...." + str(time.time()))
wlan.connect("无线网SSID","密码") # 无线网SSID、密码,开始联网
time.sleep(2)
lcd.move_to(0,0)
lcd.putstr("Not conn..."+str(time.time())+"\nconnect Wifi02")
lcd.clear()
if wlan.isconnected(): # 如果联接成功
IP_info=wlan.ifconfig()
print("无线网已经连接,信息如下:")
print(" IP地址:"+IP_info[0])
print(" 子网掩码:"+IP_info[1])
print(" 网关:"+IP_info[2])
print(" DNS:"+IP_info[3])
lcd.move_to(0,0)
lcd.putstr("WIFI connect ok!\n"+str(IP_info[0]))
else:
print("网络未连接成功,正在为你建立AP中,WIFI名:ESP8266,密码:12345678")
wlan.active(False) # 关闭未连接成功的WIFI
ap = network.WLAN(network.AP_IF)
ap.active(True)
ap.config(essid="ESP8266", authmode=network.AUTH_WPA_WPA2_PSK, password="12345678")
lcd.move_to(0,0)
lcd.putstr("WIFI AP Start...\nESP8266 12345678")
time.sleep(2)
# #########################
# 自动校时服务
def set_ntptime():
print("正在从NTP时间服务器获取时间,并校时到ESP8266/ESP32中(多次网络校时会出错OSError: -202,请断开串口连接再重试即可):")
print(" A.网络校时前本地时间:%s" %str(time.localtime()))
ntptime.NTP_DELTA = 3155644800 # 设置 UTC+8偏移时间(秒),不设置就是UTC0
ntptime.host = 'ntp1.aliyun.com' # 可选ntp服务器为阿里云服务器,默认是"pool.ntp.org"
ntptime.settime() # 从NTP服务器获取时间,并写入到ESP8266/ESP32设备中,到这就已经设置好了
print(" B.网络校时后本地时间:%s" %str(time.localtime()))
# ###########################################################################################
# 开始主程序: 执行联网+校时模块
# ###########################################################################################
wifi_connect() # 开始联网
lcd.clear() # 清理刷新屏幕,防止文字重叠
print("\n主程序开始运行:")
print(" 1、联网校时前-本地时间: ", time.localtime())
print(" 2、联网校时前-DS3231时间:", Ds3231.getDateTime())
time.sleep(0.5)
# 如果联网成功,则从网络NTP服务器校时
if wlan.isconnected():
# 1、从NTP服务器获取精确时间,写入ESP8266/ESP32
set_ntptime()
# 2、将NTP精确时间,写入DS3231中,以备无法联网的时候调用 (年、月、日、周几、时、分、秒) 如:Ds3231.setDateTime(2020, 10, 07, 2, 11, 22, 33)
Ds3231.setDateTime( time.localtime()[0],time.localtime()[1],time.localtime()[2], time.localtime()[6], time.localtime()[3],time.localtime()[4],time.localtime()[5] )
print(" 3、已从NTP联网,校时后-本地时间: ",time.localtime())
print(" 4、已从NTP联网,校时后-DS3231时间:", Ds3231.getDateTime(), "\n")
time.sleep(1)
# 如果联网不成功,则从DS3231获取时间:
else:
rtc = RTC()
# 从DS3231获取时间,写入ESP8266/ESP32
rtc.datetime(( Ds3231.getYear(), Ds3231.getMonth(), Ds3231.getDay(), Ds3231.getDayOfWeek(), Ds3231.getHour(), Ds3231.getMinutes(), Ds3231.getSeconds(), 0 ))
print(" 3、无法联网,正在从DS3231获取时间并写入ESP8266/ESP32,时间顺序为:", Ds3231.getYear(), Ds3231.getMonth(), Ds3231.getDay(), Ds3231.getDayOfWeek(), Ds3231.getHour(), Ds3231.getMinutes(), Ds3231.getSeconds())
print(" 4、已从DS3231获取时间校时,校时后-本地时间:",time.localtime(), "\n")
time.sleep(1)
#定义上、下、左、右、mid中、set、reset各按钮对应的针脚
key_up =Pin(19,Pin.IN,Pin.PULL_UP )
key_down =Pin(18,Pin.IN,Pin.PULL_UP )
key_left =Pin(5 ,Pin.IN,Pin.PULL_UP )
key_right =Pin(17,Pin.IN,Pin.PULL_UP )
key_mid =Pin(16,Pin.IN,Pin.PULL_UP )
key_set =Pin(4 ,Pin.IN,Pin.PULL_UP )
key_reset =Pin(0 ,Pin.IN,Pin.PULL_UP )
backlight=1 # 屏幕背光是否开启,1为开启,0为关闭
temp=20 # 防止频繁读取温度失败,所以每20个循环读取更新一次温度,共约10秒左右
#有源蜂鸣器,23号针脚,低电平触发!主程序开始前响2下以做提示
speak=Pin(23,Pin.OUT)
for index in range(1,2):
speak.value(0)
time.sleep(0.1)
speak.value(1)
time.sleep(0.05)
if __name__ == '__main__':
while True:
#防止频繁读取温度失败,所以每20个循环读取更新一次温度
temp=temp+1
if temp>=20 :
temp=1 #超过20后,就从1开始循环
temp_ = Ds3231.getTemp()
# 开机先从ds3231芯片读取闹钟数据 ,数据格式为:alarmTime = [0日, 0时, 0分, 0秒, ""]
Alarm = Ds3231.getAlarm1()
hh = Alarm[1]
mm = Alarm[2]
onoff = Alarm[4]
if onoff=="everyDay":
onoff="on"
else:
onoff="off"
print("\n从ds3231读取温度为:",temp_," 闹钟为:",hh,mm,onoff )
print("正在终端屏幕上输出日期、时间: "+"{:04}-{:02}-{:02} {:02}:{:02}:{:02}".format(time.localtime()[0], time.localtime()[1], time.localtime()[2], time.localtime()[3], time.localtime()[4], time.localtime()[5]))
#LED屏幕开始显示, 从第几列、第几行开始显示, 最左上角为0,0
lcd.move_to(0,0) #显示时间,如12:23:34
lcd.putstr("{:02}:{:02}:{:02}".format(time.localtime()[3], time.localtime()[4], time.localtime()[5]) )
if onoff=='on':
lcd.move_to(9,0) #显示闹钟,显示.为开启 不显示.即为关闭
lcd.putstr(".")
lcd.move_to(10,0) #显示温度,如 23.25C
lcd.putstr(str(temp_)+"C" )
lcd.move_to(1,1) #显示日期,如2020-10-01
lcd.putstr( "{:02}-{:02}-{:02} ({:1})".format(time.localtime()[0], time.localtime()[1], time.localtime()[2], time.localtime()[6]+1) ) #如 2020-10-01 (1) 星期几
# 闹钟检测,定时就响1分钟
if hh==time.localtime()[3] and mm==time.localtime()[4] and onoff=="on" :
print("闹钟开始响了......")
speak.value(0)
time.sleep(0.5)
speak.value(1)
time.sleep(0.1)
# 闹钟响的时候,按中键停止响! 好像不起作用,估计要加个判断参数,暂时不加了!!!
if hh==time.localtime()[3] and mm==time.localtime()[4] and key_mid.value()==0:
speak.value(1)
print("闹钟停止!!!!!!")
# 如果检测到 中键 按下,则开启或者关闭屏幕背光
if key_mid.value()==0 and backlight==1:
print("检测到[中键]按下,屏幕背光关闭...")
lcd.backlight_off()
backlight=0
elif key_mid.value()==0 and backlight==0:
print("检测到[中键]按下,屏幕背光开启...")
lcd.backlight_on()
backlight=1
# ============================================== 菜单这个大循环开始了
# 如果检测到【set】键,则开始显示菜单
if key_set.value()==0:
time.sleep(0.2)
Menu_select=1 #默认选择菜单的第一项
lcd.clear()
while True:
time.sleep(0.2)
Menu_list=["1. Set Date", #定义菜单显示内容
"2. Set Time",
"3. Set Alarm",
"4. Back"
]
# 因为屏幕只能显示2行,所以屏幕显示结果为:
# --> 1. Set Date
# 2. Set Time
# 如果是菜单最后一项,则在第二行显示菜单的第一项
lcd.move_to(0,0)
if Menu_select<len(Menu_list):
lcd.putstr("-> "+Menu_list[Menu_select-1]+"\n "+Menu_list[Menu_select])
else:
lcd.putstr("-> "+Menu_list[Menu_select-1]+"\n "+Menu_list[0] )
# 如果检测到下键,则菜单向下移动1行. 如果当前是菜单最后一项,则循环显示为菜单第一项
if key_down.value()==0:
lcd.clear()
if Menu_select<len(Menu_list):
Menu_select=Menu_select+1
else:
Menu_select=1
#如果检测到上键,则菜单向上移动1行. 如果当前是菜单第一项,则循环显示为菜单最后一项
if key_up.value()==0:
lcd.clear()
if Menu_select>1:
Menu_select=Menu_select-1
else:
Menu_select=len(Menu_list)
# ================================= 1111111111111111111111
# 如果选中 【 1、Set Date 】
if (Menu_select==1 and key_mid.value()==0):
Weizhi=[3,6,9] # 设置日期时,用^这个小符号来表示是修改年月日中的哪一项,369分别表示^前面要显示几个空格
i=0 # 左右键移动时使用
y=0 # 上下键按下时使用
yyyy = time.localtime()[0] # 获取当前年、月、日
mm = time.localtime()[1]
dd = time.localtime()[2]
set_time = "{:04}-{:02}-{:02}".format( yyyy, mm, dd)+"\n"+" "*Weizhi[i]+"^" # 如: 2020-10-01\n ^ , \n表示换到屏幕的下一行显示
print("正在设置日期,当时日期为:",set_time)
while True:
lcd.clear()
lcd.move_to(0,0)
lcd.putstr(set_time)
time.sleep(0.2)
# 在1.Set Date 时,如果按下【右】的按键 111111111右右右右右右右右右右右右右右右右右右右右右右右右右右右右
if key_right.value()==0:
i=i+1 # 将^小箭头向右移到年月日的下一个位置 比如刚才^显示在2025年份中5的下面,现在则移动到2025-10中月份这个0的下面
if i==3:
i=0 # 如果已经移到最右边,则返回最前面的位置
# 在1.Set Date 时,如果按下【左】的按键 11111111111左左左左左左左左左左左左左左左左左左左左左左左左左左左左左
if key_left.value()==0:
i=i-1 # ^小箭头向左移到列表中上一个位置
if i==-1:
i=2 # 如果已经移到最左边,则变到最后面的位置
# 在1.Set Date 时,如果按【上】键 1111111111111上上上上上上上上上上上上上上上上上上上上上上上上上上上上上
if key_up.value()==0:
if i==0: #如果^在年份的位置
yyyy=yyyy+1
if i==1: # 如果^在月的位置
mm=mm+1 # 月份加1,比如原先是10月,现在则变成11月
if mm>12: # 如果月份增加1后大于12月份,就循环显示为1月份
mm=1
if i==2: #如果^在日的位置
dd=dd+1
if dd>30 and (mm==2 or mm==4 or mm==6 or mm==9 or mm==11): #大月份,31天。 (暂未考虑2月29号闰年这一天)
dd=1
if dd>31 and (mm==1 or mm==3 or mm==5 or mm==7 or mm==8 or mm==10 or mm== 12): #小月份,30天
dd=1
# 在1.Set Date 时,如果按【下】键 111111111111下下下下下下下下下下下下下下下下下下下下下下下下下下下下
if key_down.value()==0:
if i==0: #如果^在年份的位置
yyyy=yyyy-1
if i==1: #如果^在月的位置
mm=mm-1
if mm<1:
mm=12
if i==2: #如果^在日的位置
dd=dd-1
if dd<1 and (mm==1 or mm==3 or mm==5 or mm==7 or mm==8 or mm==10 or mm== 12):
dd=31
if dd<1 and (mm==2 or mm==4 or mm==6 or mm==9 or mm==11):
dd=30
set_time = "{:04}-{:02}-{:02}".format(yyyy, mm, dd)+"\n"+" "*Weizhi[i]+"^"
print("正在设置Date, 当前小箭头位置为:", Weizhi[i], set_time )
if key_mid.value()==0: # 如果按下了【确认】键,则确认时间后,退出设置菜单
# 将修改后的时间,写入DS3231
Ds3231.setYear(yyyy)
Ds3231.setMonth(mm)
Ds3231.setDay(dd)
# 从DS3231获取时间,再写入ESP8266/ESP32,保证时间相同
rtc = RTC()
rtc.datetime(( Ds3231.getYear(), Ds3231.getMonth(), Ds3231.getDay(), Ds3231.getDayOfWeek(), Ds3231.getHour(), Ds3231.getMinutes(), Ds3231.getSeconds(), 0 ))
break
if key_reset.value()==0: # 如果按下了【reset】键,则退出设置菜单
break
# ================================= 2222222222222222222222222222222
# 如果选中 【2、Set time 】
if (Menu_select==2 and key_mid.value()==0): # 2、Set time
Weizhi=[1,4,7] # 设置时间时,用^这个小符号来表示是修改时分秒中的哪一项,147分别表示^前面要显示几个空格 21:30:45
i=0
y=0
hh = time.localtime()[3] # 获取当时小时、分钟、秒
mm = time.localtime()[4]
ss = time.localtime()[5]
set_time = "{:02}:{:02}:{:02}".format( hh, mm, ss)+"\n"+" "*Weizhi[i]+"^"
print("正在设置时间,当前时间为:",set_time)
while True:
lcd.clear()
lcd.move_to(0,0)
lcd.putstr(set_time)
time.sleep(0.2)
# 在2.Set time 时,如果按下【右】的按键 2222222右右右右右右右右右右右右右右右右右右右右右右右右右右右右
if key_right.value()==0:
i=i+1 # ^小箭头向右移到列表中下一个位置
if i==3:
i=0 # 如果已经移到最右边,则返回最前面的位置
# 在2.Set time 时,如果按下【左】的按键 22222222左左左左左左左左左左左左左左左左左左左左左左左左左左左左左
if key_left.value()==0:
i=i-1 # ^小箭头向左移到列表中上一个位置
if i==-1:
i=2 # 如果已经移到最左边,则变到最后面的位置
# 在2.Set time 时,如果按【上】键 2222222222上上上上上上上上上上上上上上上上上上上上上上上上上上上上上
if key_up.value()==0:
if i==0: #如果^在小时的位置
hh=hh+1
if hh>23:
hh=0
if i==1: #如果^在分的位置
mm=mm+1
if mm>59:
mm=0
if i==2: #如果^在秒的位置
ss=ss+1
if ss>59:
ss=0
# 在2.Set time 时,如果按【下】键 222222下下下下下下下下下下下下下下下下下下下下下下下下下下下下
if key_down.value()==0:
if i==0: #如果^在小时的位置
hh=hh-1
if hh<0:
hh=23
if i==1: #如果^在分的位置
mm=mm-1
if mm<0:
mm=59
if i==2: #如果^在秒的位置
ss=ss-1
if ss<0:
ss=59
set_time = "{:02}:{:02}:{:02}".format(hh, mm, ss)+"\n"+" "*Weizhi[i]+"^"
print("正在设置time, 当前小箭头位置为:", Weizhi[i], set_time )
if key_mid.value()==0: # 如果按下了【确认】键,则确认时间后,退出设置菜单
# 将修改后的时间,写入DS3231
Ds3231.setHour(hh)
Ds3231.setMinutes(mm)
Ds3231.setSeconds(ss)
# 从DS3231获取时间,写入ESP8266/ESP32
rtc = RTC()
rtc.datetime(( Ds3231.getYear(), Ds3231.getMonth(), Ds3231.getDay(), Ds3231.getDayOfWeek(), Ds3231.getHour(), Ds3231.getMinutes(), Ds3231.getSeconds(), 0 ))
break
if key_reset.value()==0: # 如果按下了【reset】键,则退出设置菜单
break
# ================================= 333333333333333333333333
# 如果选中 【3、Set Alarm 】
if (Menu_select==3 and key_mid.value()==0): # 2、Set time
Weizhi=[1,4,10] # 用来记录^前面有几个空格,闹钟没必要设置秒,所以秒一直为0。 如: 21:30:00 on/off
i=0
y=0
Alarm = Ds3231.getAlarm1() # 从ds3231芯片获取原先设置保存的闹钟数据。 alarmTime = [0日, 0时, 0分, 0秒, ""]
hh = Alarm[1] #时
mm = Alarm[2] #分
ss = 0 #秒
onoff = Alarm[4] #用来判断是on/off
if onoff=="everyDay":
onoff="on"
else:
onoff="off"
set_time = "{:02}:{:02}:{:02}".format(hh, mm, ss)+ " " + onoff + "\n" + " "*Weizhi[i] + "^"
print("正在设置闹钟时间,当前闹钟时间为:",set_time)
while True:
lcd.clear()
lcd.move_to(0,0)
lcd.putstr(set_time)
time.sleep(0.2)
# 在3、Set Alarm 时,如果按下【右】的按键 2222222右右右右右右右右右右右右右右右右右右右右右右右右右右右右
if key_right.value()==0:
i=i+1 # ^小箭头向右移到列表中下一个位置
if i==3:
i=0 # 如果已经移到最右边,则返回最前面的位置
# 在3、Set Alarm 时,如果按下【左】的按键 22222222左左左左左左左左左左左左左左左左左左左左左左左左左左左左左
if key_left.value()==0:
i=i-1 # ^小箭头向左移到列表中上一个位置
if i==-1:
i=3 # 如果已经移到最左边,则变到最后面的位置
# 在3、Set Alarm 时,如果按【上】键 2222222222上上上上上上上上上上上上上上上上上上上上上上上上上上上上上
if key_up.value()==0:
if i==0: #如果^在小时的位置
hh=hh+1
if hh>23:
hh=0
if i==1: #如果^在分的位置
mm=mm+1
if mm>59:
mm=0
if i==2: #如果^在on/off的位置
if onoff=="on":
onoff="off"
else:
onoff="on"
# 在3、Set Alarm 时,如果按【下】键 222222下下下下下下下下下下下下下下下下下下下下下下下下下下下下
if key_down.value()==0:
if i==0: #如果^在小时的位置
hh=hh-1
if hh<0:
hh=23
if i==1: #如果^在分的位置
mm=mm-1
if mm<0:
mm=59
if i==2: #如果^在on/off的位置
if onoff=="on":
onoff="off"
else:
onoff="on"
set_time = "{:02}:{:02}:{:02}".format( hh, mm, ss)+ " " + onoff + "\n" + " "*Weizhi[i] + "^"
print("正在设置闹钟, 当前小箭头位置为:", Weizhi[i], set_time )
if key_mid.value()==0: # 如果按下了【确认】键,则确认时间后,退出设置菜单
# 将闹钟写入DS3231
# Ds3231.setAlarm1(day, hour, minutes, seconds = 0, alarmType = "everyDay"):
if onoff=="on":
Ds3231.setAlarm1(0,hh,mm,0,"everyDay")
else:
Ds3231.setAlarm1(0,hh,mm,0,"everyMonth")
break
if key_reset.value()==0: # 如果按下了【reset】键,则退出设置菜单
break
# ================================= 44444444444444
# 如果选中 【4、Back 】
if (Menu_select==4 and key_mid.value()==0) or key_reset.value()==0: # 如果菜单在第4项(back),并且按下了确认键,则退出设置菜单
lcd.clear()
break
print("当时菜单所在行为: ",Menu_select)
time.sleep(0.5)