再次升级:增加农历和天气预报
海鲜市场入手拟辉光管时钟,就是这货:
效果还是蛮炫的。
用了一年多,突然抽风,具体表现为自动切换为日期和星期显示,需要按键才能切回来,过一会儿又变成日期/星期。
原来是运行一个lixie_clock.exe程序,连接电脑进行各种设置和对时。但现在却死活再也连不上电脑。虽加了卖家微信,但不管微信或者海鲜市场联系,均无回应,估计卖家已随黄鹤去。
偶然看到ESP8266开发板才十块钱,遂动了给TA换心的念头。
买来8266 D1 mini开发板,lm386音频DAC(用于增加随音量闪烁功能)。花几个小时,动手焊了几根线,写了若干python程序。调试修Bug,搞定!
至此,将原来的STM32F103芯片主板替换掉,功能如下:
- 连接wifi每小时自动对时(直接使用板上RTC,每小时可以误差30秒之多)
- 三种模式随小时数轮换
- 模式一、随音量大小闪烁(调节亮度),颜色为小时数对7取模
- 模式二、时分秒6个灯按七彩颜色流转
- 模式三、时分秒6个灯同色呼吸,按七彩颜色切换
三个python文件,传到8266板上:
;获得端口号,假定为COM4
esptool.py read_mac
;刷入micropython固件
esptool.py -p COM4 erase_flash
esptool.py --port COM4 --baud 460800 write_flash --flash_size=detect 0 esp8266-20220618-v1.19.1.bin
;上传文件
ampy -p COM4 put main.py
ampy -p COM4 put lx_clock.py
ampy -p COM4 put rainbow.py
micropython与python有些微差异,发现了就改之。
main.py就两行:
from lx_clock import lx_clock
lx_clock()
rainbow.py:
import time
BRIGHT_STEPS = 32
# micropython无deepcopy
def color_deepcopy(color_class):
return Color(color_class.name, color_class.rgb, color_class.step)
class Color:
def __init__(self, name, rgb_tuple, step_tuple = (-1, -1, -1)):
self.__name = name
self.r = rgb_tuple[0]
self.g = rgb_tuple[1]
self.b = rgb_tuple[2]
self.__r_max = self.r
self.__g_max = self.g
self.__b_max = self.b
self.__r_step = (step_tuple[0] == -1) and (self.__r_max / BRIGHT_STEPS) or step_tuple[0]
self.__g_step = (step_tuple[1] == -1) and (self.__g_max / BRIGHT_STEPS) or step_tuple[1]
self.__b_step = (step_tuple[2] == -1) and (self.__b_max / BRIGHT_STEPS) or step_tuple[2]
# 亮度调节(百分比)
def brightness(self, percent):
if percent < 0 or percent > 1:
return self.rgb
self.r = self.__r_max * percent
self.g = self.__g_max * percent
self.b = self.__b_max * percent
return self.rgb
@property
def rgb(self):
return (int(self.r), int(self.g), int(self.b))
@property
def name(self):
return self.__name
@property
def step(self):
return (self.__r_step, self.__g_step, self.__b_step)
def grade(self, direct = 'down'):
if direct == 'down':
self.r = self.__grade_rgb(self.r, 0 - self.__r_step)
self.g = self.__grade_rgb(self.g, 0 - self.__g_step)
self.b = self.__grade_rgb(self.b, 0 - self.__b_step)
else:
self.r = self.__grade_rgb(self.r, self.__r_step)
self.g = self.__grade_rgb(self.g, self.__g_step)
self.b = self.__grade_rgb(self.b, self.__b_step)
return self.rgb
def __grade_rgb(self, current_value, value):
if current_value + value > 255:
current_value = 255
elif current_value + value < 0:
current_value = 0
else:
current_value += value
return current_value
class RainBow:
def __init__(self, mode = 1):
# step为默认,全亮 -> 全暗
self.__color_def1 = [
Color('red', (255, 0, 0)),
Color('orange', (255, 125, 0)),
Color('yellow', (255, 255, 0)),
Color('green', (0, 255, 0)),
Color('cyan', (0, 255, 255)),
Color('blue', (0, 0, 255)),
Color('violet', (255, 0, 255)),
]
# step为变化到下一个颜色
self.__color_def2 = [
Color('red', (255, 0, 0), (0, 165 / BRIGHT_STEPS, 0)),
Color('orange', (255, 125, 0), (0, (255 - 125) / BRIGHT_STEPS, 0)),
Color('yellow', (255, 255, 0), (0 - 255 / BRIGHT_STEPS, 0, 0)),
Color('green', (0, 255, 0), (0, 0, 255 / BRIGHT_STEPS)),
Color('cyan', (0, 255, 255), (0, 0 - 255 / BRIGHT_STEPS, 0)),
Color('blue', (0, 0, 255), (255 / BRIGHT_STEPS, 0, 0)),
Color('violet', (255, 0, 255), (0, 0, 0 - 255 / BRIGHT_STEPS)),
]
self.__colors = []
self.__mode = mode
if self.__mode == 1:
for i in range(len(self.__color_def1)):
self.__colors.append({'index': i, 'color': color_deepcopy(self.__color_def1[i])})
else:
self.__colors.append({'index': 0, 'color': color_deepcopy(self.__color_def2[0])})
self.__colors.append({'index': 6, 'color': color_deepcopy(self.__color_def2[6])})
self.__colors.append({'index': 5, 'color': color_deepcopy(self.__color_def2[5])})
self.__colors.append({'index': 4, 'color': color_deepcopy(self.__color_def2[4])})
self.__colors.append({'index': 3, 'color': color_deepcopy(self.__color_def2[3])})
self.__colors.append({'index': 2, 'color': color_deepcopy(self.__color_def2[2])})
self.__colors.append({'index': 1, 'color': color_deepcopy(self.__color_def2[1])})
self.__current_step = 0
self.__loop_times = 0
def color_with_index(self, index = 0):
for color in self.__colors:
if color['index'] == index:
return color['color']
return self.__colors[0]['color']
@property
def rgbs(self):
rgbs = []
for color in self.__colors:
rgbs.append(color['color'].rgb)
return rgbs
def magic(self):
if self.__mode == 1:
return self.__magic1()
else:
return self.__magic2()
# 模式一:单色呼吸灯,全暗后切换下一颜色到全亮
def __magic1(self):
self.__current_step += 1
if self.__current_step == BRIGHT_STEPS:
self.__current_step = 0
self.__loop_times += 1
if self.__loop_times % 2 != 0:
for color in self.__colors: # 颜色移位
color['index'] += 1
if color['index'] == len(self.__colors):
color['index'] = 0
for color in self.__colors: # 重新初始化color_class,全暗
idx = color['index']
color['color'] = color_deepcopy(self.__color_def1[idx])
color['color'].brightness(0)
else:
for color in self.__colors: # 重新初始化color_class,全亮
color['color'].brightness(1)
else:
if self.__loop_times % 2 == 0:
for color in self.__colors:
color['color'].grade('down')
else:
for color in self.__colors:
color['color'].grade('up')
return self.rgbs
# 模式二:七彩色,直接切换到下一颜色
def __magic2(self):
self.__current_step += 1
if self.__current_step == BRIGHT_STEPS:
self.__current_step = 0
for color in self.__colors: # 颜色移位
color['index'] += 1
if color['index'] == len(self.__colors):
color['index'] = 0
for color in self.__colors: # 重新初始化color_class
idx = color['index']
color['color'] = color_deepcopy(self.__color_def2[idx])
else:
for color in self.__colors:
color['color'].grade('up')
return self.rgbs
lx_clock.py,请修改83行wifi的SSID和密码:
from machine import Pin, ADC, Timer, RTC
from neopixel import NeoPixel
import network, time, ntptime
HOLD_TIME_MS = 30 # 保持时间(毫秒)
MAX_VOLUME_VAL = 64 # 最大音量ADC值
# 时间转换为led位置
def time2pos(now_time):
led_sn = (9, 4, 8, 3, 7, 2, 6, 1, 5, 0)
return (
led_sn[int(now_time[4] / 10)],
led_sn[int(now_time[4] / 10)] + 10,
led_sn[now_time[4] % 10] + 20,
led_sn[now_time[4] % 10] + 30,
led_sn[int(now_time[5] / 10)] + 40,
led_sn[int(now_time[5] / 10)] + 50,
led_sn[now_time[5] % 10] + 60,
led_sn[now_time[5] % 10] + 70,
led_sn[int(now_time[6] / 10)] + 80,
led_sn[int(now_time[6] / 10)] + 90,
led_sn[now_time[6] % 10] + 100,
led_sn[now_time[6] % 10] + 110,
)
def magic1(np, now_time, rainbow):
light_sn = time2pos(now_time)
rgbs = rainbow.magic()
for i in range(np.n):
if i in light_sn:
np[i] = rgbs[0]
else:
np[i] = (0, 0, 0)
np.write()
def magic2(np, now_time, rainbow):
light_sn = time2pos(now_time)
rgbs = rainbow.magic()
for i in range(np.n):
if i in light_sn:
np[i] = rgbs[int(i / 20 % 7)]
else:
np[i] = (0, 0, 0)
np.write()
def bright_volume(np, now_time, pot, sound_rainbow):
light_sn = time2pos(now_time)
rainbox_index = now_time[4] / 3 % 7
volume = pot.read()
if volume > MAX_VOLUME_VAL:
volume = MAX_VOLUME_VAL
global last_volume
if last_volume == volume:
return
last_volume = volume
for i in range(np.n):
if i in light_sn:
np[i] = sound_rainbow.color_with_index(rainbox_index).brightness(volume / MAX_VOLUME_VAL)
else:
np[i] = (0, 0, 0)
np.write()
def minute_work(t_minute):
sync_ntp()
def do_connect():
sta_if = network.WLAN(network.STA_IF)
if not sta_if.isconnected():
print('connecting to network...')
sta_if.active(True)
sta_if.connect('SSID', 'wifi密码') # 需修改SSID和密码
while not sta_if.isconnected():
pass
print('network config:', sta_if.ifconfig())
def sync_ntp():
now_time = RTC().datetime() # 年、月、日、星期、小时、分钟、秒、亚秒
global last_ntp_sync_hour
if last_ntp_sync_hour == now_time[4] and now_time[6] > 30: # 一小时同步一次
return
do_connect() # 连接网络,如已连接则自动忽略
ntptime.NTP_DELTA = 3155644800 # 可选UTC+8偏移时间(秒),不设置就是UTC0
ntptime.host = 'ntp1.aliyun.com' # 可选,ntp服务器,默认是pool.ntp.org
ntptime.settime() # 修改设备时间
synced_time = RTC().datetime() # 年、月、日、星期、小时、分钟、秒、亚秒
last_ntp_sync_hour = synced_time[4]
def lx_clock(loop_times = -1, mode = -1):
global last_ntp_sync_hour
last_ntp_sync_hour = -1
pin = Pin(15, Pin.OUT) # set GPIO15 to output to drive NeoPixels
np = NeoPixel(pin, 120) # create NeoPixel driver on GPIO0 for 120 pixels
from rainbow import RainBow
sound_rainbow = RainBow()
magic_rainbow1 = RainBow(1)
magic_rainbow2 = RainBow(2)
# period最大0xCCCCCCCC=5540小时或221天
t_minute = Timer(-1)
t_minute.init(period = 1000 * 10, mode = Timer.PERIODIC, callback = minute_work)
global last_volume
last_volume = 0
pot = ADC(0)
while loop_times != 0:
now_time = RTC().datetime() # 年、月、日、星期、小时、分钟、秒、亚秒
hour = now_time[4]
if mode == 0:
bright_volume(np, now_time, pot, sound_rainbow)
elif mode == 1:
magic1(np, now_time, magic_rainbow1)
elif mode == 2:
magic2(np, now_time, magic_rainbow2)
else:
if hour % 3 == 0:
bright_volume(np, now_time, pot, sound_rainbow)
elif hour % 3 == 1:
magic1(np, now_time, magic_rainbow1)
elif hour % 3 == 2:
magic2(np, now_time, magic_rainbow2)
time.sleep_ms(HOLD_TIME_MS)
if loop_times > 0:
loop_times -= 1
t_minute.deinit()
ESP8266 D1 mini开发板:
几点说明:
- ws2812b灯板连接到15、GND、VBUS;
- lm386连接到A0、3.3V,GND与灯板共用;
- 灯板共有120个LED,排列方式不同厂家出品可能不同,可调整time2pos函数中的对应灯号;
- 无lm386,则需要去掉对应代码,并屏蔽模式一;
- 请修改83行WiFi的SSID和密码。
这种时钟原理很巧妙,程序代码相对简单。这里有网站。
顺手多买了一个0.96寸oled显示屏,准备尝试放上农历天气啥的,弄完继续分享源码。