Опрос датчиков в Node-RED: поиск устройств и чтение значений
Введение: 1-Wire в экосистеме Node-RED на контроллерах HI
Протокол 1-Wire представляет собой уникальное решение для подключения множества устройств, в первую очередь датчиков, по общей трех- или даже двухпроводной шине. Как мы рассматривали в предыдущих уроках, каждое устройство на шине имеет уникальный 64-битный адрес, что позволяет контроллеру обращаться к ним индивидуально. Особенность интеграции 1-Wire в контроллеры HI на базе Linux заключается в том, что операционная система предоставляет доступ к этим устройствам через специальную файловую систему.
Это означает, что для программного обеспечения, такого как Node-RED, каждый датчик на шине выглядит как обычная папка, а его данные — как текстовый файл внутри этой папки. Такой подход значительно упрощает взаимодействие: вместо сложных протокольных обменов на низком уровне мы можем использовать стандартные системные команды для чтения информации.
> 🔗 Связанный материал: Убедитесь, что вы изучили материал урока COURSE-04-M06-L04 "Конфигурация 1-Wire интерфейса в системе". Данный урок предполагает, что шина уже настроена на уровне операционной системы и готова к использованию.
В этой экосистеме Node-RED выступает в роли прикладного уровня (L7). Его задача — оркестрировать процесс:
Для решения этих задач в данном уроке мы будем использовать базовый, но очень мощный набор узлов Node-RED:
- Inject: Узел для инициирования потока. Мы будем использовать его для запуска периодического опроса датчиков.
- Exec: Ключевой узел для этого урока. Он позволяет выполнить любую команду в командной строке Linux и получить ее результат (вывод `stdout`) в `msg.payload`.
- Function: "Мозг" нашего потока. Этот узел содержит JavaScript-код, который будет выполнять парсинг данных, валидацию и форматирование сообщений в соответствии с внутренним контрактом.
- Debug: Наш основной инструмент для отладки, позволяющий видеть содержимое объекта `msg` на любом этапе выполнения потока.
Цель этого урока — научить вас строить надежный и воспроизводимый поток для считывания данных с популярных датчиков температуры DS18B20, используя стандартные возможности контроллера HI и Node-RED.
---
Секция 1: Поиск устройств на шине (Device Discovery)
Прежде чем считывать данные с конкретного датчика, необходимо узнать его уникальный идентификатор (ID). Операционная система Linux автоматически выполняет обнаружение устройств (discovery) при их подключении к шине 1-Wire и создает для каждого из них отдельную директорию.
Все обнаруженные устройства доступны в системной директории `/sys/bus/w1/devices/`. Содержимое этой директории представляет собой список всех "ведомых" (slave) устройств, подключенных к шине.
Чтобы получить этот список в Node-RED, мы можем использовать узел Exec для выполнения стандартной команды Linux `ls`.
Практикум: Обнаружение 1-Wire устройств
ls /sys/bus/w1/devices
Убедитесь, что опция "Добавить к сообщению" установлена в `payload`.
В окне отладки вы увидите `msg.payload`, содержащий строку, подобную этой:
28-01193a79f17d
28-01193a8479e9
w1_bus_master1
Это и есть список устройств на вашей шине.
> 💡 Подсказка: Чтобы сопоставить физический датчик с его ID в системе, можно поочередно нагревать датчики (например, пальцем) и отслеживать, значение с какого ID меняется в реальном времени. Этот метод мы применим в следующих секциях.
Анализ результата
- `28-xxxxxxxxxxxx`: Это уникальные адреса ваших датчиков. Префикс `28` — это код семейства (Family Code) для датчиков температуры DS18B20. `xxxxxxxxxxxx` — это уникальный 48-битный серийный номер устройства. Именно этот полный ID (`28-01193a79f17d`) нам понадобится для чтения данных с конкретного датчика.
- `w1_bus_master1`: Это системная папка, представляющая сам контроллер шины (master). Она нам не нужна для чтения данных с датчиков.
На данном этапе крайне важно задокументировать, какой физический датчик какому ID соответствует. Например, в проектной документации или прямо в комментариях в Node-RED:
| ID устройства | Физическое расположение |
| ------------------ | ------------------------- |
| `28-01193a79f17d` | Температура в гостиной |
| `28-01193a8479e9` | Температура на улице |
Этот простой шаг сэкономит массу времени при дальнейшей настройке и обслуживании системы. Ошибочная идентификация датчиков — одна из самых частых проблем на этапе пусконаладки.
---
Секция 2: Структура файла данных датчика и чтение 'сырых' значений
После того как мы обнаружили ID нужного нам датчика, мы можем прочитать с него данные. Как упоминалось ранее, система представляет данные от датчика в виде специального файла с именем w1_slave. Этот файл находится внутри директории, соответствующей ID датчика.
Полный путь к файлу с данными для датчика с ID `28-01193a79f17d` будет выглядеть так:
`/sys/bus/w1/devices/28-01193a79f17d/w1_slave`
Чтобы прочитать содержимое этого файла, используется стандартная команда Linux `cat`.
Формат файла `w1_slave`
Содержимое этого файла — это обычный текст, состоящий из двух строк. Давайте рассмотрим пример вывода команды `cat` для файла `w1_slave`:
cat /sys/bus/w1/devices/28-01193a79f17d/w1_slave
Результат в `msg.payload` будет выглядеть так (в виде одной строки с символом переноса `\n`):
79 01 4b 46 7f ff 0c 10 31 : crc=31 YES
79 01 4b 46 7f ff 0c 10 31 t=23562
Разберем эти строки:
* `79 01 ... 10 31`: Это сырые данные, считанные из памяти датчика. Для нас они не представляют прямого интереса.
* `crc=31`: Это контрольная сумма (CRC), вычисленная на основе сырых данных.
* `YES`: Это — самый важный флаг. Он означает, что контрольная сумма, вычисленная контроллером, совпала с контрольной суммой, переданной датчиком. Чтение успешно.
* Данные здесь дублируются.
* `t=23562`: Это и есть "сырое" значение температуры. Это целочисленное значение, которое необходимо разделить на 1000, чтобы получить температуру в градусах Цельсия. В данном случае `23562 / 1000 = 23.562 °C`.
> ⚠️ Внимание: Всегда проверяйте результат CRC. Если первая строка содержит `NO`, данные во второй строке некорректны и не должны использоваться. Это может указывать на проблемы с линией связи (помехи, плохой контакт, превышение длины) или питанием датчика, как было рассмотрено в уроке `COURSE-04-M06-L03`. Игнорирование проверки CRC — прямой путь к ненадёжной работе системы.
Если чтение не удалось (например, из-за помех), вывод может выглядеть так:
79 01 4b 46 7f ff 0c 10 31 : crc=31 NO
79 01 4b 46 7f ff 0c 10 31 t=23562
Несмотря на наличие значения `t=...`, его использование недопустимо, так как целостность данных не подтверждена.
---
Секция 3: Практикум: Создание потока для чтения и парсинга данных DS18B20
Теперь, зная ID датчика и структуру его файла данных, мы можем собрать полноценный поток в Node-RED, который будет автоматически считывать, проверять и преобразовывать температуру в удобный для использования формат.
Пошаговое создание потока
cat /sys/bus/w1/devices/28-xxxxxxxxxxxx/w1_slave
Для второго и третьего выхода (`stderr` и `code`) узла `Exec` добавьте узлы `Debug`, чтобы видеть возможные ошибки выполнения команды (например, если ID датчика введён неверно).
// Получаем сырые данные из payload в виде строки
const rawData = msg.payload;
// 1. Проверяем, что данные не пустые
if (!rawData) {
node.error("Пустой payload от узла Exec", msg);
node.status({fill:"red", shape:"dot", text:"Пустой payload"});
return null; // Останавливаем поток
}
// 2. Проверяем целостность данных (CRC)
if (rawData.indexOf('YES') === -1) {
node.warn("Ошибка CRC или некорректные данные", msg);
node.status({fill:"red", shape:"dot", text:"Ошибка CRC"});
return null; // Останавливаем поток, если CRC не пройдена
}
// 3. Ищем позицию 't='
const tempPosition = rawData.indexOf('t=');
if (tempPosition === -1) {
node.error("Не удалось найти 't=' в строке данных", msg);
node.status({fill:"red", shape:"dot", text:"Формат нарушен"});
return null; // Останавливаем поток
}
// 4. Извлекаем и преобразуем значение
try {
// Берем подстроку после 't='
const tempString = rawData.substring(tempPosition + 2);
// Преобразуем в число и делим на 1000
const temperature = parseInt(tempString, 10) / 1000.0;
// Валидация значения (DS18B20 работает от -55 до +125)
if (temperature < -55 || temperature > 125) {
node.warn("Температура вне допустимого диапазона: " + temperature, msg);
node.status({fill:"yellow", shape:"ring", text:"Недостоверное значение"});
return null;
}
// 5. Формируем новое сообщение по стандарту "Контракт сообщения"
msg.payload = {
"value": temperature,
"unit": "°C",
"source": "28-xxxxxxxxxxxx", // Замените на ID вашего датчика
"ts": Date.now()
};
// Устанавливаем визуальный статус для быстрой диагностики
node.status({fill:"green", shape:"dot", text: temperature.toFixed(2) + " °C"});
return msg;
} catch (e) {
node.error("Ошибка при парсинге температуры", { original_msg: msg, error: e });
node.status({fill:"red", shape:"dot", text:"Ошибка парсинга"});
return null;
}
> 💡 Подсказка: Для проектов с множеством датчиков вы можете сделать этот поток более динамичным. Передавайте ID датчика в `msg.topic` и используйте его для формирования команды в узле Exec с помощью синтаксиса `{{{topic}}}`. Это позволит создать один переиспользуемый субпоток (subflow) для всех датчиков.
Пример успешного `msg.payload` на выходе:
{
"value": 23.562,
"unit": "°C",
"source": "28-01193a79f17d",
"ts": 1678886400000
}
Такой формат легко обрабатывать, логировать в базу данных MySQL или отправлять по MQTT.
---
Секция 4: Резюме и лучшие практики
В этом уроке мы прошли полный цикл работы с датчиком температуры DS18B20 на контроллере HI: от обнаружения его на шине до создания автоматического потока опроса и обработки данных в Node-RED.
Ключевые этапы, которые мы освоили:Рекомендации и лучшие практики
- Интервал опроса: Датчику DS18B20 требуется до 750 мс для преобразования температуры (при 12-битном разрешении). Слишком частый опрос (чаще, чем раз в 2-3 секунды) нецелесообразен, так как он не даст более актуальных данных, но создаст избыточную нагрузку на CPU контроллера из-за постоянного вызова команды `cat`. Выбирайте интервал в зависимости от инертности измеряемого процесса. Для температуры в помещении достаточно интервала в 30-60 секунд.
- Обработка ошибок: Наш код в узле `Function` уже реализует базовую обработку ошибок (проверка CRC, формата строки). Для создания по-настоящему отказоустойчивой системы используйте второй и третий выходы узла `Exec` для отлова ошибок выполнения команды. Подключите к ним узел `Catch` (или просто `Debug` на этапе разработки), чтобы логировать ситуации, когда датчик "отвалился" от шины и его файл стал недоступен.
- Масштабирование и организация: Если в вашем проекте более 2-3 датчиков, не создавайте копии потока для каждого. Вместо этого, инкапсулируйте логику чтения и парсинга в субпоток (subflow). Такой субпоток может принимать ID датчика на входе и возвращать готовый JSON-объект на выходе. Это кардинально упрощает поддержку и модификацию системы.
> ℹ️ Информация: Использование узла `Exec` — это базовый, но достаточно ресурсоемкий метод. Он отлично подходит для небольшого количества датчиков и для изучения принципов работы. В курсах продвинутого уровня (`automation` и `integration`) мы рассмотрим альтернативные, более производительные способы работы с 1-Wire, например, через специализированные узлы Node-RED (если они доступны) или через промежуточные скрипты, публикующие данные напрямую в MQTT.
Что дальше
Освоив чтение данных, следующим логическим шагом является их использование. В последующих уроках мы научимся сохранять эти показания в базу данных MySQL для построения исторических графиков, а также создавать на их основе сценарии автоматизации — например, управление системой отопления или вентиляции в зависимости от текущей температуры.