print("Hello, RP2040!")
from machine import Pin, PWM, ADC, I2C
import utime
from lcd_api import LcdApi
from pico_i2c_lcd import I2cLcd
from klass_ntc import NTC10K
# Параметры LCD
I2C_ADDR = 0x27
I2C_NUM_ROWS = 2
I2C_NUM_COLS = 16
# Инициализация шины I2C (на GPIO 4 = SDA, GPIO 5 = SCL)
i2c = I2C(0, sda=machine.Pin(4), scl=machine.Pin(5), freq=400000)
# Создание объекта LCD
lcd = I2cLcd(i2c, I2C_ADDR, I2C_NUM_ROWS, I2C_NUM_COLS)
# Определение пользовательского символа — градус
degree_symbol = [
0b00110,
0b01001,
0b01001,
0b00110,
0b00000,
0b00000,
0b00000,
0b00000
]
# Загрузка символа градуса в CGRAM по индексу 0
lcd.custom_char(0, degree_symbol)
utime.sleep(1)
print("Starting LCD test")
# Вывод названия устройства
lcd.move_to(3, 0)
lcd.putstr("Kontr_ temp")
lcd.move_to(7,1)
lcd.putstr("v1.0")
utime.sleep(2)
lcd.clear()
# === Инициализация устройств (выбираем пины, совместимые с RP2040) ===
# Датчики температуры (аналоговые пины: GP26, GP27, GP28 → ADC0, ADC1, ADC2)
sensor1 = NTC10K(26) # GP26 = ADC0
sensor2 = NTC10K(27) # GP27 = ADC1
# Мотор (простое управление GPIO)
motor = Pin(9, Pin.OUT) # GP9
# LED (индикатор тревоги)
led = Pin(10, Pin.OUT) # GP10
# Buzzer (ШИМ на GP21)
buzzer_pin = Pin(21, Pin.OUT)
buzzer = PWM(buzzer_pin)
buzzer.freq(2000) # Частота 2 кГц
# === Параметры сглаживания ===
WINDOW_SIZE = 15 # Окно сглаживания (секунды)
temp1_buffer = [] # Буфер для температур коллектора
temp2_buffer = [] # Буфер для температур батареи
buffer_ready = False
prev_temp_smooth = None
# Параметры управления
motor_state = False
last_utime = utime.time()
# Параметры плавного алгоритма
BASE_THRESHOLD = 15
SENSITIVITY = 2
MIN_ON_THRESHOLD = 5
MAX_ON_THRESHOLD = 20
MIN_OFF_THRESHOLD = 2
ALARM_TEMP = 79
# === Основной цикл ===
while True:
current_utime = utime.time()
# === 1. Получение RAW значений ===
temp1_raw = sensor1.read_temp()
temp2_raw = sensor2.read_temp()
# === 2. Добавление в буферы ===
temp1_buffer.append(temp1_raw)
temp2_buffer.append(temp2_raw)
# Ограничиваем размер буферов до WINDOW_SIZE
if len(temp1_buffer) > WINDOW_SIZE:
# Удаляем самый старый элемент (первый в списке)
temp1_buffer.pop(0)
if len(temp2_buffer) > WINDOW_SIZE:
temp2_buffer.pop(0)
# === 3. Проверка заполнения буфера ===
if not buffer_ready and len(temp1_buffer) >= WINDOW_SIZE:
buffer_ready = True
print(f"Буфер температур заполнен ({WINDOW_SIZE} секунд), сглаживание активно")
# === 4. Вычисление сглаженных значений ===
# Используем все доступные элементы в буфере
temp1_smooth = sum(temp1_buffer) / len(temp1_buffer)
temp2_smooth = sum(temp2_buffer) / len(temp2_buffer)
# === 5. Вычисление разницы температур ===
if buffer_ready:
# Используем полный буфер из WINDOW_SIZE элементов
temp_diff = temp1_smooth - temp2_smooth
diff_source = "smooth"
else:
# Используем RAW значения, пока буфер не заполнился
temp_diff = temp1_raw - temp2_raw
diff_source = "raw"
# === 6. Отображение на LCD ===
# Коллектор (строка 0)
lcd.move_to(0, 0)
if buffer_ready:
lcd.putstr(f"Tk:{temp1_smooth:.1f}{chr(0)}")
else:
# Показываем RAW и индикатор калибровки
remaining = WINDOW_SIZE - len(temp1_buffer)
# Форматируем так, чтобы всегда было 2 символа для обратного отсчета
lcd.putstr(f"Tk:{temp1_raw:.1f} {remaining:2d}")
# Батарея (строка 1)
lcd.move_to(0, 1)
if buffer_ready:
lcd.putstr(f"Tb:{temp2_smooth:.1f}{chr(0)}")
else:
lcd.putstr(f"Tb:{temp2_raw:.1f}{chr(0)} ")
# Разница температур (позиция 9,0)
lcd.move_to(9, 0)
lcd.putstr(f"Dif:{temp_diff:2.0f}{chr(0)}")
# === 7. Вычисление скорости нагрева ===
dt = current_utime - last_utime
if dt > 0 and prev_temp_smooth is not None:
heating_rate = (temp1_smooth - prev_temp_smooth) / dt
else:
heating_rate = 0
# Инициализация prev_temp_smooth при первом запуске
if prev_temp_smooth is None:
prev_temp_smooth = temp1_smooth
# === 8. ПЛАВНЫЙ АЛГОРИТМ УПРАВЛЕНИЯ ===
# Расчет динамического порога включения
on_threshold = BASE_THRESHOLD - (heating_rate * SENSITIVITY)
on_threshold = max(MIN_ON_THRESHOLD, min(MAX_ON_THRESHOLD, on_threshold))
# Динамический порог выключения
off_threshold = max(MIN_OFF_THRESHOLD, on_threshold - 5)
# === 9. Отображение режима на LCD ===
lcd.move_to(9, 1)
if not buffer_ready:
# Режим калибровки
remaining = WINDOW_SIZE - len(temp1_buffer)
mode_str = f"Cal{remaining:2d}"
elif heating_rate > 2:
mode_str = "Fast "
elif heating_rate > 0.5:
mode_str = "Medium"
else:
mode_str = "Slow "
lcd.putstr(mode_str)
# === 10. Управление мотором (используем temp_diff) ===
if not motor_state and temp_diff > on_threshold:
motor.on()
motor_state = True
elif motor_state and temp_diff <= off_threshold:
motor.off()
motor_state = False
# === 11. Тревога (используем RAW значение для быстрой реакции) ===
alarm = temp1_raw >= ALARM_TEMP
led.value(alarm)
buzzer.duty_u16(32768 if alarm else 0)
# === 12. Вывод диагностики ===
print("\n" + "="*50)
print(f"Буфер: {len(temp1_buffer)}/{WINDOW_SIZE} | Источник: {diff_source}")
print(f"Режим: {mode_str} | Нагрев: {heating_rate:.2f}°C/сек")
print(f"RAW: {temp1_raw:.1f}°C / {temp2_raw:.1f}°C")
print(f"Smooth: {temp1_smooth:.1f}°C / {temp2_smooth:.1f}°C")
print(f"Разница: {temp_diff:.1f}°C | Пороги: ВКЛ>{on_threshold:.1f}°C, ВЫКЛ<={off_threshold:.1f}°C")
print(f"Мотор: {'ВКЛ' if motor_state else 'ВЫКЛ'}")
print(f"Тревога: {'АКТИВНА' if alarm else '---'} (по RAW: {temp1_raw:.1f}°C)")
print("="*50)
# === 13. Обновление переменных для следующего цикла ===
prev_temp_smooth = temp1_smooth
last_utime = current_utime
utime.sleep(1)