拟辉光管时钟“重生”记(源码)

再次升级:增加农历和天气预报

海鲜市场入手拟辉光管时钟,就是这货:

效果还是蛮炫的。

用了一年多,突然抽风,具体表现为自动切换为日期和星期显示,需要按键才能切回来,过一会儿又变成日期/星期。

原来是运行一个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开发板:

几点说明: 

  1. ws2812b灯板连接到15、GND、VBUS;
  2. lm386连接到A0、3.3V,GND与灯板共用;
  3. 灯板共有120个LED,排列方式不同厂家出品可能不同,可调整time2pos函数中的对应灯号;
  4. 无lm386,则需要去掉对应代码,并屏蔽模式一;
  5. 请修改83行WiFi的SSID和密码。

这种时钟原理很巧妙,程序代码相对简单。这里有网站

顺手多买了一个0.96寸oled显示屏,准备尝试放上农历天气啥的,弄完继续分享源码。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值