TrashNotes

2025-03-27 12:22:30
import time
import board
import busio
import digitalio
import TM1637
import adafruit_ds1307
import microcontroller
import struct

# Константы
SECONDS_TO_ADD_PER_WEEK = 5
DAYS_BEFORE_ADJUSTMENT = 10
I2C_SDA_PIN = board.GP15
I2C_SCL_PIN = board.GP14
TM1637_CLK_PIN = board.GP1
TM1637_DIO_PIN = board.GP0
BEEPER_PIN = board.GP6
RTC_ADDRESS = 0x68
CHECK_ADJUSTMENT_INTERVAL = 18000  # 300 минут
HOURLY_BEEP_START_HOUR = 8
HOURLY_BEEP_END_HOUR = 22
MORSE_WPM = 27
MAIN_LOOP_DELAY = 0.9

# Инициализация I2C с обработкой ошибок
def init_i2c():
    try:
        i2c = busio.I2C(I2C_SDA_PIN, I2C_SCL_PIN)
        print(f"I2C инициализирован на пинах SCL: {I2C_SCL_PIN}, SDA: {I2C_SDA_PIN}")
        return i2c
    except Exception as e:
        print(f"Ошибка инициализации I2C: {e}")
        return None

# Проверка доступности устройства I2C
def wait_for_i2c_device(i2c, address, timeout=5):
    start_time = time.monotonic()
    while time.monotonic() - start_time < timeout:
        try:
            i2c.try_lock()
            if address in i2c.scan():
                i2c.unlock()
                return True
            i2c.unlock()
        except Exception as e:
            print(f"Ошибка проверки I2C: {e}")
            if i2c.locked():
                i2c.unlock()
        time.sleep(1)
    return False

# Инициализация TM1637 с обработкой ошибок
def init_tm1637():
    try:
        display = TM1637.TM1637(TM1637_CLK_PIN, TM1637_DIO_PIN)
        display.brightness(0)
        print(f"TM1637 инициализирован на пинах CLK: {TM1637_CLK_PIN}, DIO: {TM1637_DIO_PIN}")
        return display
    except Exception as e:
        print(f"Ошибка инициализации TM1637: {e}")
        return None

# Инициализация RTC с обработкой ошибок
def init_rtc(i2c):
    if i2c and wait_for_i2c_device(i2c, RTC_ADDRESS):
        try:
            rtc = adafruit_ds1307.DS1307(i2c)
            print(f"RTC (DS1307) инициализирован по адресу {RTC_ADDRESS}")
            return rtc
        except Exception as e:
            print(f"Ошибка инициализации RTC: {e}")
    else:
        print("RTC не обнаружен на шине I2C")
    return None

# Инициализация beeper
def init_beeper():
    try:
        beep = digitalio.DigitalInOut(BEEPER_PIN)
        beep.direction = digitalio.Direction.OUTPUT
        print(f"Beeper инициализирован на пине {BEEPER_PIN}")
        return beep
    except Exception as e:
        print(f"Ошибка инициализации beeper: {e}")
        return None

# Morse кодирование
def morse_transmitter(beep, message, wpm=MORSE_WPM):
    morse_dict = {
        '0': '-----', '1': '.----', '2': '..---', '3': '...--', '4': '....-',
        '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.',
        ':': '---...',
    }
    dot_duration = 1.2 / wpm
    for char in message:
        if char in morse_dict:
            for symbol in morse_dict[char]:
                if symbol == '.':
                    beep.value = True
                    time.sleep(dot_duration)
                    beep.value = False
                    time.sleep(dot_duration)
                elif symbol == '-':
                    beep.value = True
                    time.sleep(3 * dot_duration)
                    beep.value = False
                    time.sleep(dot_duration)
            time.sleep(3 * dot_duration)

# Сигнализация времени морзянкой
def signal_time_morse(beep, rtc):
    if rtc and beep:
        current_time = rtc.datetime
        time_str = f"{current_time.tm_hour:02}:{current_time.tm_min:02}"
        morse_transmitter(beep, time_str.replace(':', ' '))

# Отображение последней коррекции времени
def display_last_adjustment(display, rtc):
    if rtc and display:
        last_adjustment = load_last_adjustment_date()
        if last_adjustment:
            last_adj_str = f"{last_adjustment.tm_year:04}-{last_adjustment.tm_mon:02}-{last_adjustment.tm_mday:02}"
            print(f"Последняя коррекция: {last_adj_str}")

# Сохранение даты последней корректировки в NVM
def save_last_adjustment_date(rtc):
    if rtc:
        current_time = rtc.datetime
        date_bytes = struct.pack('>HBB', current_time.tm_year, current_time.tm_mon, current_time.tm_mday)
        microcontroller.nvm[0:4] = date_bytes

# Загрузка даты последней корректировки из NVM
def load_last_adjustment_date():
    try:
        date_bytes = microcontroller.nvm[0:4]
        unpacked_date = struct.unpack('>HBB', date_bytes)
        return time.struct_time((unpacked_date[0], unpacked_date[1], unpacked_date[2], 0, 0, 0, 0, 0, -1))
    except Exception as e:
        print(f"Ошибка загрузки даты корректировки: {e}")
        return None

# Корректировка времени
def adjust_time(rtc, seconds_to_add):
    if rtc:
        current_time = rtc.datetime
        time_list = list(current_time)
        time_list[5] += seconds_to_add
        new_time = time.struct_time(tuple(time_list))
        rtc.datetime = new_time
        print(f"Время скорректировано на +{seconds_to_add} секунд")
        save_last_adjustment_date(rtc)

# Проверка необходимости корректировки времени
def check_time_adjustment(rtc):
    if rtc:
        current_time = rtc.datetime
        last_adjustment = load_last_adjustment_date() or time.struct_time((2000, 1, 1, 0, 0, 0, 0, 0, -1))
        days_since_last_adjustment = (time.mktime(current_time) - time.mktime(last_adjustment)) // 86400
        if days_since_last_adjustment >= DAYS_BEFORE_ADJUSTMENT:
            weeks_passed = days_since_last_adjustment // 7
            adjust_time(rtc, weeks_passed * SECONDS_TO_ADD_PER_WEEK)

# Отображение времени на дисплее
def display_time(display, rtc):
    if rtc and display:
        current_time = rtc.datetime
        display.show("{:02}{:02}".format(current_time.tm_hour, current_time.tm_min))

# Основная инициализация
i2c = init_i2c()
display = init_tm1637()
rtc = init_rtc(i2c)
beep = init_beeper()

# Восстановление времени при запуске
if rtc:
    if not rtc.datetime.tm_year:  # Проверка, что время обнулилось
        last_adjustment = load_last_adjustment_date()
        if last_adjustment:
            rtc.datetime = last_adjustment
            print("Время восстановлено из NVM")

# Проверка корректировки времени при запуске
check_time_adjustment(rtc)

# Отображение последней коррекции
display_last_adjustment(display, rtc)

# Основная петля
last_adjustment_check = time.monotonic()
hourly_beep_played = False

if rtc:
        current_time = rtc.datetime
        signal_time_morse(beep, rtc)


while True:
    if rtc:
        current_time = rtc.datetime
    else:
        current_time = time.localtime()

    display_time(display, rtc)

    # Проверка ежечасного сигнала
    if current_time.tm_min == 0 and not hourly_beep_played:
        if HOURLY_BEEP_START_HOUR <= current_time.tm_hour < HOURLY_BEEP_END_HOUR and beep:
            signal_time_morse(beep, rtc)
            hourly_beep_played = True
    elif current_time.tm_min != 0:
        hourly_beep_played = False

    # Проверка корректировки времени
    if time.monotonic() - last_adjustment_check >= CHECK_ADJUSTMENT_INTERVAL:
        check_time_adjustment(rtc)
        last_adjustment_check = time.monotonic()

    time.sleep(MAIN_LOOP_DELAY)
← Previous Next →
Back to list