Термодатчики DS18x20, продолжение…

в 17:49, , рубрики: 1-wire, arduino, ds1820, DS18B20, esp

"1 л.с. - это сила, которую развивает 1 лошадь диаметром 1 м и массой 1 кг, в вакууме".
Не первый раз встречается, что люди, видя какую-то техническую задачу, пытаются решить ее сначала с использованием каких-нибудь эмуляторов, причем за основу берутся допущения и упрощения. В итоге приходят к закономерному выводу - "так оно работать не будет! Программа посчитала..."

Если бы мы жили в мире сферических лошадей диаметром 1м - наверное да, реальность всегда и точно соответствовала бы теоретическим предположениям.
Но, к счастью или к сожалению, оно немножко не так.
И вот пример, снова наши термодатчики (+ немного занудства):

В предыдущей статье написал про работающую у меня ( "у меня всё работает!" (с) (тм) ) схемку подключения:

Термодатчики DS18x20, продолжение… - 1
пояснять, как работает транзистор, надо?

Транзистор, если не вникать в детали, работает очень просто: когда вон на том выводе, посередине слева, затворе, "+" относительно нижнего вывода - он ток проводит, когда "0" или "-" - не проводит. Такая у него базовая фича.

Когда на ножке GPIO логический 0 (0В) - получается, что напряжение на затворе = 3.3В, транзистор открыт, и линия 1-wire закорочена на 0, через транзистор и ножку процессора.

Когда на ножке логическая 1 (3.3В или около того) - получается что напряжение на затворе около 0В, он закрыт, считаем что его вообще нет, и на шине тогда 5В со стабилитрона через резистор R2.

Когда ножка в режиме чтения - напряжение на ней само поднимется до 3.3, логической 1, потому что если оно меньше - транзистор откроется и добавит, а если дошло до 3.3 - закроется. На этой точке оно и будет балансировать.
Если при этом датчик притянет линию к 0 - напряжению будет неоткуда браться, оно упадет до 0.

Поэтому на линии импульсы будут 5В, а на ножке GPIO - не выше 3.3. Всё просто.

и еще одну, тоже реально работающую штуку:

Термодатчики DS18x20, продолжение… - 2

Эти схемы - как основа для реального применения, точка опоры, от которой можно отталкиваться. А вот суровая реальность:

Куплена партия датчиков, DS18B20, Китай. Подключение точно по первой схеме, в частности обращу внимание на то что ножки Vcc и Gnd самих датчиков закорочены.
Всё работает идеально.
Если не закоротить - начинаются помехи в линии, отваливаются отдельные датчики.

Куплена партия датчиков, DS18B20, Китай. Подключение точно по первой схеме - глухо, ничего не работает. Берем осциллограф в руки - просадка на линии. Просаживается она как раз при подключении ножки Vcc к Gnd, причем сразу.
Отключаем ножку Vcc - всё работает.

Еще одна линия на датчиках "валялись в коробке", т.е. были куплены непонятно когда, где, с какой партией, вперемешку.
Одни из них начинают работать, когда ножку закоротишь, другие - когда не закоротишь, третьи требуют конденсатора как на второй схеме, иначе не обнаруживаются если провод длиннее метра.
Причем с конденсаторами все работают идеально, а без них - одни да, другие нет.

То есть, при одном и том же названии микросхемы, поведение разных датчиков существенно отличается одно от другого, наверняка и внутренние схемы разные, а значит и токи потребления.
Ну и как это в принципе можно однозначно рассчитать, чтобы с уверенностью потом говорить "будет так!" ?
Да никак. Можно только оценочно прикидывать, и проверять на практике.

Но и для оценочных прикидок надо для начала разобраться, как оно работает в конкретном случае.
Например, допустим, что 1 датчик в момент измерения температуры потребляет 10мА тока в течении 100мс (все цифры оценочные, с потолка, т.к. реальных мы не знаем, датчики разные).
Вопрос, какой ток будут потреблять 4 датчика?

Думаете, арифметика вам поможет: 10мА * 4 = 40мА?
А кто сказал что они будут потреблять ток одновременно? Или сразу один за другим без пауз?
Это такое допущение, которое на самом деле еще больше с потолка, чем ток потребления неизвестной схемы. В реальности оно будет работать так, как задано процессом опроса датчиков.

И этим мы можем управлять. Например, часто в инструкциях к Адруино приводятся примеры с использованием библиотеки DallasTemperature:

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

void setup(void)
{
  ....
  sensors.begin();
}

void loop(void)
{
  ....
  sensors.requestTemperatures(); // Send the command to get temperatures
  float tempC = sensors.getTempCByIndex(0);
  ....
}

Действительно, в этом случае в шину 1-wire отправляется команда запуска измерений (0x44) сразу для всех датчиков, после чего запрашивается результат с датчика, который обнаружился и попал в список на этапе инициализации, функция sensors.begin()

Какие тут минусы?
- измерение производится сразу всеми датчиками (уточню: - получившими команду, а это не обязательно все на линии!)
- в момент измерения датчики потребляют больше, чем во всё оставшееся время. Именно тот случай когда несколько датчиков способны разрядить всю линию.
- этот процесс занимает некоторое время, ну пусть всё-таки 100мс. А потом идет запрос к конкретному датчику, чтобы он передал температуру - это еще время. То есть, на этом участке исполнения программы МК задерживается, ждет пока там отработают датчики.
- мы не можем сказать какой именно из "287d9244060000c7", "285bf044d4e13cab", "28f4b844d4e13c04" будет в списке первый, какой второй и т.д., нужно работать с конкретными адресами, а для этого надо их сначала узнать.
- главный минус: всё это работает с теми датчиками, которые определились сразу, в первый раз. То есть если какой-то не определился, или добавили-удалили потом - мы этого уже не узнаем, до перезагрузки.

Этого всего можно избежать, если снимать показания немного по-другому.

Во-первых, можно измерять температуру не всеми датчиками одновременно, а по очереди. Для этого надо выбирать конкретный датчик, и отправлять команду ему.
Во-вторых, можно не ждать пока он наизмеряет - отправили команду, вернулись с вопросами через некоторое время.
Для этого нужно иметь список устройств, а вот обнаруживать устройства можно в процессе заново, т.е. появляется возможность добавлять-удалять датчики на лету, не перезагружая контроллер и не меняя прошивку.
И наконец, можно сделать так, чтобы обработка результатов измерения происходила тогда, когда они получены, и если получены.

За основу взята библиотека OneWire, которая содержит низкоуровневые команды управления 1-wire, поверх нее был написан класс:

....
  
#define DS_INIT     0
#define DS_SEARCH   1
#define DS_CONVERT  2
#define DS_RESULT   3
#define DS_REPORT   4

....
  
class owds : public OneWire {

    int ds_count;                               // device count
    unsigned long ds_timer;                     // loop timer
    unsigned long ds_period;                    // loop period
    int ds_mode;                                // current mode
    int ds_sel;                                 // selected device

    owds_Addr ds_list[DS_COUNT];
    owds_Name names[DS_COUNT];

    int fill_mac(char * mac, int i);

  public:

    owds(int pin);                                          // init

    void loop();                                            // main procedure at system loop

    const char * find_name(const char * mac);               // search name for MAC
    void set_name(const char * mac, const char * name);     // set name for MAC
    inline void clear_names() {                             // clear names
      memset(names,0,sizeof(names));
    }
    inline String get_names(){                              // get all names
      String ret = "{";
      for(int i=0; i< DS_COUNT; i++){
        if(strcmp(names[i].name,"") != 0){
          if(i > 0) ret += ",";
          ret += """;
          ret += names[i].mac;
          ret += "":"";
          ret += names[i].name;
          ret += """;
        }
      }
      ret += "}";
      return ret;
    }
    void save_config(File & file);                          // save names to config file (JSON)
    int load_config(File & file);                           // read names from config file (JSON)

    virtual void report(const char * str) {};               // when reporting for all devices (JSON)
    virtual void processing(const char * id, float t) {};   // when processing one device (MAC, TEMP)
    virtual void debug(const char * str) {};                // hook for debug

};
.....

Принцип заключается в том, что выделены 5 режимов работы:
1 - инициализация переменных, "начинаем с начала"
2 - поиск устройств на шине
3 - для каждого устройства запрашивается измерение
4 - для каждого устройства выводятся данные
5 - отправляется итоговый отчет
И всё повторяется через некоторое время.

Переход между режимами полностью автоматический, происходит последовательно при каждой итерации.
Для вызова пользовательских функций использованы виртуальные методы класса, т.е. их можно использовать или не использовать.
В программе достаточно просто подключить библиотеку и создать свой класс, примерно так:

#define PIN_OW   5

// OneWire settings =======================================
#include <OWDS.h>

// Create own class based on owds, define user functions and play
class myowds : public owds {
  public:
  myowds(int pin) : owds(pin){};

  // report all found devices at once, JSON string
  void report(const char * str){
    Serial.println("report:");
    Serial.println(str);
  }

  // report for each found device 
  void processing(const char * mac, float t){
    Serial.println("processing:");
    Serial.print(mac);
    Serial.print(" = ");
    Serial.println(t);
  }

  // just debug message
  void debug(const char * str){
    Serial.print("debug: ");
    Serial.println(str);
  }

};

myowds  oneWire(PIN_OW);

void setup() {

  Serial.begin(19200);

}


void loop() {

  oneWire.loop();

  delay(100);

}

Всё, теперь при удачном обнаружении датчиков будут вызываться соответствующие функции. Можно просто сбрасывать данные в MQTT, можно обрабатывать на месте, если заранее известен MAC датчика (manufacturer address code, он же ROM, он же ID) и его назначение.

Именно с ее помощью и реализован у меня контроль зон. Там действительно получается всё просто: что определилось - то будет работать, а если не работает - надо лезть с осциллографом и смотреть почему

Термодатчики DS18x20, продолжение… - 3

Библиотеку полностью, если кому надо - закинул на GitHub.

Автор: JBFW

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js