Мой погреб, инкубатор и уличный термометр жили на трёх разных пинах Arduino — по одному DS18B20 на пин, и всё было хорошо, пока я не захотел четвёртую точку и не обнаружил, что пины кончились, а расширять плату ради пары градусов как-то неловко. Между тем весь этот зоопарк можно посадить на один провод: 1-Wire для того и придуман. Я пересобрал всё на пин 2 — и наступил во вторые грабли: привязал к индексам «датчик 0 — улица, датчик 1 — погреб», а через неделю перекинул контакты на макетке, индексы перемешались, и термореле неделю грело улицу вместо погреба. Показания были настоящие, просто ярлыки повисли в воздухе.
В этом уроке повесим несколько DS18B20 на одну линию, узнаем адрес каждого и построим опрос так, чтобы после любой перестановки проводов «улица» оставалась улицей. Заодно поговорим о физике длинных шин — там, где один датчик прощает почти всё, три уже требуют планировки. А если вы ещё не держали в руках одиночного DS18B20 — начните с предыдущего урока про подключение и подтяжку: без неё шина молчит даже с одним датчиком.
Почему на один провод вешают десятки датчиков
Каждый DS18B20 несёт уникальный 64-битный адрес, зашитый на заводе: первый байт — семейный код 0x28 (визитная карточка семейства термометров Dallas), следующие шесть — серийный номер, последний — контрольная сумма CRC. Двух одинаковых адресов не бывает, поэтому шина и работает: контроллер называет адрес — откликается ровно один датчик. Сколько их там, десять или двадцать, линии всё равно: у шины I2C та же идея, только там адреса вы выставляете ножками, а здесь они уже вшиты в кремний.
Адреса нужно сначала узнать. Помогает короткий скетч-адресоскан: библиотека OneWire умеет перебирать устройства на шине командой search() и выдавать их адреса по очереди. Запускаете с одним подключённым датчиком — записываете восемь байт, подключаете следующий — и так, пока не соберёте весь список. Я вешаю на каждый зонд бумажную бирку с его адресом, и через полгода это выглядит как избыточная педантичность ровно до первой перестановки макетки.
Сколько датчиков — это много? Теоретический потолок шины — десятки устройств: адресов хватает, а ток каждый датчик берёт микроамперный. Практический предел ставит ёмкость кабеля и качество скруток: для бытовых задач — аквариум, погреб, улица, теплица — четыре-шесть точек на одном пине чувствуют себя свободно, конверсия у всех идёт параллельно, и задержка шины от их числа не растёт. Цена у простоты одна: каждому зонду нужны свои провода питания, и на длинной трассе три жилы вместо двух — лишний моток кабеля, честная плата за стабильность.
#include <OneWire.h> OneWire bus(2); // шина 1-Wire на пине 2 void setup() { Serial.begin(9600); Serial.println("Adresa ustroistv na shine:"); byte addr[8]; while (bus.search(addr)) { // следующее устройство шины for (byte i = 0; i < 8; i++) { if (addr[i] < 0x10) Serial.print("0"); // ведущий ноль байта Serial.print(addr[i], HEX); Serial.print(" "); } Serial.println(); } Serial.println("Poisk zavershon.");} void loop() { // адреса нужны один раз, тут делать нечего}Индексы или адреса: что надёжнее
Библиотека DallasTemperature предлагает два пути. Первый — по индексам: getTempCByIndex(0), getTempCByIndex(1) и далее; быстро начать, ничего не записывать. Второй — по адресам: getTempCByAddress(addr) с восемью байтами, выловленными адресосканом. Разница не в коде, а в обещаниях. Порядок индексов — это порядок обнаружения устройств, и библиотека его не гарантирует: он может измениться после пересборки схемы, замены датчика, а иногда и просто между включениями. Усугубляет всё то, что два зонда из одной партии не отличаются ни внешне, ни показаниями в одной комнате — перепутаешь и не заметишь. История из начала урока — прямое следствие: индекс честно вернул температуру, просто это была температура не того места.
Адрес не зависит ни от порядка, ни от перестановок: байт 0x28, серийник, CRC — паспорт конкретного куска кремния. Мой вывод после той недели с грющей погребом: индексы годятся для эксперимента на столе, для всего, что работает дольше вечера, — только адреса. Компромисс для ленивых: один раз в setup() скормить библиотеке адреса методом getAddress() и дальше жить спокойно.
Практический пример, где адреса окупаются мгновенно: термостат с гистерезисом из урока про термореле. Его пороговую логику можно повесить на конкретный адрес погреба — и никакая перестановка проводов её не сдвинет. А показания всех точек шины удобно сводить в таблицу в мониторе порта — как строить такие таблицы, мы разбирали на данных Serial.
Конверсия при этом устроена выгодно: requestTemperatures() шлёт команду «меряй!» всем датчикам разом — broadcast по шине, без перечисления. Потом вы неспешно читаете каждого по его адресу. Именно поэтому таймер на всю шину остаётся тем же: 750 миллисекунд на 12 битах независимо от того, один на линии датчик или шесть. Читаете вы их по очереди, в кооперативном цикле без delay — и шина не станет узким местом.
Физика длинной линии — вторая часть взрослой жизни 1-Wire. Подтяжка ставится одна на всю шину, а не на каждый датчик: резистор 4,7 кОм между DATA и 5 В в одной точке у контроллера. До пары метров обычные макетные провода живут беззаботно. Дальше начинаются наводки и отражения: провода скручивают в витую пару (данные с землёй), подтяжку пробуют ужимать до 2,2 кОм, а то и ставят резистор порядка 100 Ом последовательно в линию данных у каждого датчика — он гасит звон и делает фронты честнее. Мы разбирали, откуда берутся эти помехи, в уроке про наводки на датчиках. Мнение с границей: до пяти-шести датчиков и десятка метров 1-Wire — приятная прогулка; если хочется больше — это уже проект с планом трассы, а не «прикручу по ходу».
Симулятор в этой паре честен до упрямства: он держит один датчик, и оба адреса в скетче ниже читают одну и ту же микросхему — значения в мониторе совпадут с точностью до шума. Множественность видно только на живой плате, зато структуру кода — конверсия всем разом, чтение по адресам — можно отработать прямо в браузере, и она в точности переносится на реальное железо.
Есть у сима с одним датчиком и своя польза: он отлично отлаживает саму логику опроса. Если скетч печатает n=, потом оба адреса и не падает — половина работы сделана. На живой плате вам останется только подставить свои адреса и убедиться, что значения по ним разъехались: инкубатор теплее улицы на двадцать градусов, и перепутать их станет невозможно.
| Симптом в мониторе | Причина | Что делать |
|---|---|---|
| −127 у всех датчиков | нет подтяжки или обрыв линии данных | проверить 4,7 кОм и целостность провода |
| −127 у одного из датчиков | плохой контакт именно в его ветке | перезатянуть скрутку, прозвонить ветку |
| значения двух адресов совпадают | физически один датчик или дважды один зонд | сверить адреса адресосканом с бирками |
| на длинной линии показания рвутся | наводки и ёмкость кабеля | витая пара, подтяжка 2,2 кОм, укорить трассу |
- 1. Почему привязка «индекс 0 — улица» со временем ломается?
- 2. На шине три DS18B20. Сколько подтягивающих резисторов нужно?
- 3. Что делает requestTemperatures() на шине с несколькими датчиками?
- 4. Что значит первый байт адреса DS18B20, равный 0x28?
- 5. Шина на пятнадцать метров начала терять показания. С чего начать лечение?
- Адрес DS18B20 — 64 бита: 0x28, серийник, CRC; он вшит на заводе и не меняется от перестановок.
- Адресоскан на bus.search() печатает восемь байт каждого устройства — собираете список с бирками один раз.
- Индексы — для стола, адреса — для всего, что работает дольше вечера; n= из getDeviceCount() сверяет ожидания с шиной.
- Конверсия запускается всем датчикам разом: 750 мс на шину независимо от их числа.
- Одна подтяжка 4,7 кОм на шину; длинные линии — витая пара, 2,2 кОм и план трассы.
Частые вопросы по теме урока
- Почему привязка «индекс 0 — улица» со временем ломается?
- Индекс — это номер в очереди обнаружения. Пересобрали схему, заменили датчик — очередь перестроилась, и индекс 0 внезапно оказался погребом. Адрес же вшит в чип и от перестановок не зависит. Порядок обнаружения устройств библиотекой не гарантирован и меняется после перестановки схемы
- На шине три DS18B20. Сколько подтягивающих резисторов нужно?
- Подтяжка прижимает одну общую линию данных, поэтому резистор один — у контроллера. Дополнительные подтяжки на датчиках только искажают фронты. Одна подтяжка 4,7 кОм на всю шину
- Что делает requestTemperatures() на шине с несколькими датчиками?
- Это broadcast: команда «меряй!» уходит всей шине. Поэтому 750 мс на 12 битах остаются общими для любого числа датчиков, а чтение потом идёт по очереди. Шлёт команду конверсии всем датчикам разом
- Что значит первый байт адреса DS18B20, равный 0x28?
- Структура адреса: 0x28 — тип устройства, шесть байт серийного номера, байт CRC. По первому байту адресоскан отличает термометр от другого 1-Wire устройства. Семейный код — визитная карточка термометров Dallas
- Шина на пятнадцать метров начала терять показания. С чего начать лечение?
- На длинных линиях виноваты ёмкость кабеля и наводки: скрутка «данные+земля», уменьшение подтяжки до 2,2 кОм и разумная топология лечат почти всегда. Витая пара для данных с землёй, подтяжку 2,2 кОм, укоротить трассу