Мониторинг термопары газового водонагревателя: SMS/Email алерт при погасании горелки
Компоненты и материалы
- Raspberry Pi Zero WH — 1 шт.
- Arduino Nano R3 — 1 шт.
Описание проекта
Проблема
Традиционные газовые водонагреватели поддерживают горящую горелку 24/7. Она нагревает термопару, вырабатывающую до 30 мВ. Это напряжение удерживает газовый клапан открытым. Если горелка погасает, напряжение падает — клапан закрывается (безопасность). У автора горелка погасает ~3 раза в год. Замена термопары не помогла. Замена дорогого колонника нежелательна.
Решение
Мониторить напряжение термопары. При падении до нуля — отправлять SMS и Email, чтобы вовремя поджечь горелку. Опасности утечки газа нет — клапан закроется сам.
Архитектура
Raspberry Pi Zero WH отправляет уведомления. Arduino Nano измеряет напряжение (у Pi нет ADC). Arduino читает АЦП (0–1023) с опорным 1.1В (для разрешения 30 мВ ≈ 28 единиц) каждые 30 секунд и передаёт однопроводным протоколом на Pi (GPIO 16). Pi декодирует, если значение низкое 5 раз подряд (2.5 мин) — шлёт алерт. Повторный алерт не чаще 6 часов.
Шаг 1. Подготовка Raspberry Pi
Настройте Pi в сети (см. другой проект автора). Напишите Python-скрипт приёма сигналов и отправки почты/SMS (код ниже).
Шаг 2. Подготовка Arduino
Загрузите скетч: измерение АЦП на A0 каждые 30 сек, опорное 1.1В (analogReference(INTERNAL)), передача числа по пину 9. Дешёвые китайские Nano могут требовать настройки драйверов CH340 на Windows 10.
Шаг 3. Подключение к термопаре
Термопара подключена коаксиальным кабелем к термостату колонка. Оболочка кабеля — «плюс».
- Перережьте кабель в 5 дюймах от термостата.
- Снимите внешнюю изоляцию, чтобы добраться до центральной жилы (не повреждайте её изоляцию!).
- Подключите центральную жилу к входу A0 Arduino и параллельно к термостату. Экранирующую оплетку (минус) — к GND Arduino и к термостату.
- Автор использовал проводки с крокодилами для соединений.



Шаг 4. Соединение Arduino с Raspberry Pi
Arduino выдаёт 5В, Pi принимает 3.3В. Нужен делитель напряжения на двух резисторах (например, 330 и 220 Ом → ~2.8В). Общие GND обязательны.

Формула: Vout = Vin * R2 / (R1 + R2). Один резистор не подходит — при отсутствии тока на нём 5В.
Шаг 5. Передача данных Arduino
Каждые 30 сек: измерение АЦП → передача двузначного числа (десятки и единицы) серией импульсов на пине 9. Импульс: 20 мс высокий, 20 мс низкий. Между цифрами пауза 50 мс. Код функции sendNumberSignal в скетче.
Шаг 6. Приём на Raspberry Pi
Python-скрипт ждёт восходящий фронт на GPIO 16. Декодирует импульсы: < 70 мс между фронтами — та же цифра, > 70 мс — следующая цифра, таймаут 200 мс — конец числа. Метод waitReceiveNumber возвращает целое (0–1023).
Шаг 7. Отправка уведомлений
Если измерение ≤ 1 (настроено эмпирически) — инкремент счётчика lowVoltageCount. При ≥5 — отправка через smtplib (Gmail). Для SMS используется шлюз оператора (например, 5555555555@vtext.com для Verizon). Gmail требует App Password (настройка безопасности). Время последнего алерта запоминается, следующий не ранее 6 часов.
Шаг 8. Финальная сборка

Подключите Arduino к термопаре и к Pi через делитель. Подайте питание — Arduino начнёт слать данные.
Шаг 9. Автозапуск Python-скрипта
- Создайте shell-скрипт запуска (
run_HotWaterNotifier.sh). - Добавьте в root crontab (
sudo crontab -e):@reboot sh /home/pi/hotwater/run_HotWaterNotifier.sh >> /home/pi/hotwater/logs/HotWaterNotifier.log 2>&1 - Для ручного запуска в фоне: запустите скрипт,
Ctrl+Z,bg,disown. Проверка:ps aux | grep -i HotWater.

Код
Arduino скетч
/****************************************************************************** HotWaterHeater Measures the voltage coming from the hot water heater's thermocouple. Uses signaling to send this voltage sensor measurement to the Raspberry Pi. ****************************************************************************/
double ReferenceVoltage = 1.1;
int OUTPUT_PIN = 9;
int INTRA_DIGIT_WAIT=20;
int BETWEEN_DIGIT_WAIT=50;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(OUTPUT_PIN, OUTPUT);
analogReference(INTERNAL);
Serial.begin(9600);
}
void loop() {
int pinInput = analogRead(A0);
double voltage = (pinInput * ReferenceVoltage) / 1023;
Serial.print("PinInputA0="); Serial.print(pinInput);
Serial.print(", voltageA0="); Serial.println(voltage);
sendNumberSignal(pinInput, OUTPUT_PIN, INTRA_DIGIT_WAIT, BETWEEN_DIGIT_WAIT);
delay(30000);
}
void sendNumberSignal(int number, int pin, int intraDigitWait, int betweenDigitWait) {
int tens = number/10;
int ones = number % 10;
sendPulse(pin, intraDigitWait);
for (int i=0; i
Python код (HotWaterNotifier.py)
###############################################################################
# HotWaterNotifier.py
# Monitor the input pin and receive signals from an Arduino.
# Decode the signals into numeric voltage sensor measurements.
# When the received voltage reading is too low, this indicates that the
# pilot light is out. When this occurs for 5 measurements in a row, assume that
# the pilot light really is out. Send emails/texts and record the time.
# Only send an email/text every six hours.
###############################################################################
import smtplib, RPi.GPIO as GPIO, os, time, datetime, string, logging, sys
GPIO_Alert = 16
emailWaitHours = 6
lowSensorReading = 1
maxLowVoltageCount = 5
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_Alert, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
lastEmailSentTime = datetime.datetime(2000,1,1,0,0,0,0)
def setup_custom_logger(name):
formatter = logging.Formatter(fmt='%(asctime)s %(levelname)-8s %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
handler = logging.FileHandler('HotWaterLog.txt', mode='a')
handler.setFormatter(formatter)
screen_handler = logging.StreamHandler(stream=sys.stdout)
screen_handler.setFormatter(formatter)
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)
logger.addHandler(screen_handler)
return logger
logger = setup_custom_logger('HotWaterNotifier')
def send_email_alert():
global lastEmailSentTime
nowTime = datetime.datetime.now()
emailWaitDelta = datetime.timedelta(hours=emailWaitHours)
limitTime = nowTime - emailWaitDelta
if lastEmailSentTime is None or limitTime > lastEmailSentTime:
logger.info('Sending email alert...')
HOST = "smtp.gmail.com"; PORT = 587
SUBJECT = "Hot Water Heater Alert"
TO = ["XXXXXXXXXXXXXX@gmail.com", "5555555555@vtext.com"]
FROM = "XXXXXXXXXXXXXX@gmail.com"
PWD = "XXXXXXXXXXXXXX" # App Password
text = "Low Voltage Measured on the Hot Water Heater"
BODY = "\r\n".join(["from: %s" % FROM, "to: %s" % ", ".join(TO), "Subject: %s" % SUBJECT, "", text])
try:
s = smtplib.SMTP(HOST, PORT)
s.set_debuglevel(1)
s.ehlo(); s.starttls()
s.login(FROM, PWD)
s.sendmail(FROM, TO, BODY)
s.quit()
lastEmailSentTime = nowTime
except Exception as e:
logger.exception('Exception caught file sending email. Trying again in 6 hours')
lastEmailSentTime = nowTime
else:
logger.info('Not sending email. Last email sent at: ' + lastEmailSentTime.strftime("%Y-%m-%d %H:%M:%S"))
def waitReceiveNumber(GPIO_Alert):
lastSignalTime = datetime.datetime(2000,1,1,0,0,0,0)
isTens = True; isFirstIteration = True
tensValue = 0; onesValue = 0
singleDigitMilliseconds = datetime.timedelta(milliseconds=70)
wholeNumberWaitTime = 200
while True:
if isFirstIteration:
receivedEdge = GPIO.wait_for_edge(GPIO_Alert, GPIO.RISING, timeout=-1)
else:
receivedEdge = GPIO.wait_for_edge(GPIO_Alert, GPIO.RISING, timeout=wholeNumberWaitTime)
signalTime = datetime.datetime.now()
signalInterval = signalTime - lastSignalTime
lastSignalTime = signalTime
if (signalInterval < singleDigitMilliseconds) or isFirstIteration:
isFirstIteration = False
if isTens: tensValue += 1
else: onesValue += 1
elif receivedEdge is not None:
if isTens:
isTens = False; onesValue += 1
else:
return ((tensValue -1)*10) + (onesValue -1)
else:
return ((tensValue -1)*10) + (onesValue -1)
def main():
logger.info('Starting HotWaterNotifier')
referenceVoltage = 1.1
lowVoltageCount = 0
try:
while True:
sensorReading = waitReceiveNumber(GPIO_Alert)
voltage = (sensorReading * referenceVoltage) / 1023
logger.info('sensorReading: ' + str(sensorReading) + ', voltage: ' + str(voltage))
if sensorReading <= lowSensorReading:
lowVoltageCount += 1
if lowVoltageCount >= maxLowVoltageCount:
logger.info('Low voltage alert')
send_email_alert()
lowVoltageCount = 0
else:
lowVoltageCount = 0
except KeyboardInterrupt:
logger.info('Keyboard interrupt received')
finally:
GPIO.cleanup()
if __name__ == "__main__":
main()
Shell-скрипт запуска
#!/bin/bash
# run_HotWaterNotifier.sh
cd /home/pi/hotwater
sudo python HotWaterNotifier.py
Автоматическая система полива растений на Arduino — уходи в отпуск спокойно
Набор «Инопланетный слот-машин» на ATmega328P — DIY игровой автомат с NeoPixel и I2C дисплеем
Производственный процесс
- Домашний энергетический центр: мониторинг солнечных панелей, тепловых коллекторов и температуры комнат на Ra…
- Создание потрясающей системы Ambilight для монитора на Arduino
- Мониторинг солнечной панели в реальном времени: дешёвая система сбора данных на Arduino и Excel
- Высокоскоростная фотография капель воды на Arduino Nano — DIY-триггер для вспышки
- Создание надежного монитора энергии и логгера данных на Arduino — пошаговое руководство
- IoT-монитор пульса в реальном времени на Arduino и MAX30100
- Монитор температуры и реальные часы на Arduino Due с TFT-дисплеем 3.2"
- Оптимальный тепловой насос для водонагревателя: энергоэффективные решения горячей воды
- Как выбрать лучший бытовой насос горячей воды — экспертные советы и руководство
- Основы насосов горячей воды: принцип работы и значение