Когда эффективнее подавать запрос на датчик ds18b20?

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Друзья, подскажите чисто теоретически, когда это лучше сделать? В примерах библиотеки oneware.h запрос на измерение температуры производится в самом начале loop().  А потом по прошествии некоторого времени (750-1000 мсек), нужного датчику, чтобы измерить температуру, пересчитать и выдать результат, считываем показания. Вариантов отсчета времени много (прерывание по таймеру, delay, по millis() и т.д.). Получается, что если код в цикле loop () короткий, то запрос на измерение температуры посылается много кратно, а считывание результата на порядки реже. Можно ли посылать запрос после считывания результата, в цикле, например, "if", в котором отсчитываем эти 750-1000 мсек? Представляется, что  такой запрос будет единственным, а не многократным. Что немного, но сэкономит время. Или я что-то упустил?

В качестве примера (взято из Интернета):

    #include <OneWire.h>
     
    // Чтение данных с температурных датчиков типа 1-Wire: DS18S20, DS18B20, DS1822
    //
    // http://www.pjrc.com/teensy/td_libs_OneWire.html
    //
    // Библиотека DallasTemperature может сделать это все за вас!
    // http://milesburton.com/Dallas_Temperature_Control_Library
     
    OneWire  ds(10);  // на 10-ом контакте (если необходимо, подключите резистор на 4,7 кОм)
     
    void setup(void) {
      Serial.begin(9600);
    }
     
    void loop(void) {
      byte i;
      byte present = 0;
      byte type_s;
      byte data[12];
      byte addr[8];
      float celsius, fahrenheit;
     
      if ( !ds.search(addr)) {
        Serial.println("No more addresses.");  //  "Адресов больше нет."
        Serial.println();
        ds.reset_search();
        delay(250);
        return;
      }
     
      Serial.print("ROM =");
      for( i = 0; i < 8; i++) {
        Serial.write(' ');
        Serial.print(addr[i], HEX);
      }
     
      if (OneWire::crc8(addr, 7) != addr[7]) {
          Serial.println("CRC is not valid!");  //  "CRC не корректен!"
          return;
      }
      Serial.println();
     
      // первый ROM-байт, определяет, с каким чипом мы имеем дело:
      switch (addr[0]) {
        case 0x10:
          Serial.println("  Chip = DS18S20");  //  "  Чип – DS18S20" или старый DS1820
          type_s = 1;
          break;
        case 0x28:
          Serial.println("  Chip = DS18B20");  //  "  Чип – DS18B20"
          type_s = 0;
          break;
        case 0x22:
          Serial.println("  Chip = DS1822");  //  "  Чип – DS1822"
          type_s = 0;
          break;
        default:
          Serial.println("Device is not a DS18x20 family device.");  //  "Устройство не принадлежит семейству DS18x20."
          return;
      }
     
      ds.reset();
      ds.select(addr);
      ds.write(0x44, 1);        // запускаем конверсию и включаем паразитное питание
     
      delay(1000);     // 750 миллисекунд может хватить, а может и нет;
                       // здесь можно использовать ds.depower(),
                       // но об этом позаботится сброс
     
      present = ds.reset();
      ds.select(addr);    
      ds.write(0xBE);         // считываем scratchpad-память
     
      Serial.print("  Data = ");  //  "  Данные = "
      Serial.print(present, HEX);
      Serial.print(" ");
      for ( i = 0; i < 9; i++) {           // нам нужно 9 байтов
        data[i] = ds.read();
        Serial.print(data[i], HEX);
        Serial.print(" ");
      }
      Serial.print(" CRC=");
      Serial.print(OneWire::crc8(data, 8), HEX);
      Serial.println();
     
      // конвертируем данные в температуру; поскольку результат - это
      // 16-битное целое знаковое число, оно должно быть записано в типе
      // данных "int16_t", который всегда будет 16-битным – даже если
      // данные скомпилированы на 32-битном процессоре.
      int16_t raw = (data[1] << 8) | data[0];
      if (type_s) {
        raw = raw << 3; // разрешение по умолчанию – 9 бит
        if (data[7] == 0x10) {
          // регистр "count remain" дает полное 12-битное разрешение
          raw = (raw & 0xFFF0) + 12 - data[6];
        }
      } else {
        byte cfg = (data[4] & 0x60);
        // при низком разрешении младшие биты не определяются, поэтому обнуляем их:
        if (cfg == 0x00) raw = raw & ~7;  // 9-битное разрешение (93,75 миллисекунд)
        else if (cfg == 0x20) raw = raw & ~3;  // 10-битное разрешение (187,5 миллисекунд)
        else if (cfg == 0x40) raw = raw & ~1;  // 11-битное разрешение (375 миллисекунд)
     
        //// по умолчанию стоит 12-битное разрешение; время конверсии – 750 миллисекунд
      }
      celsius = (float)raw / 16.0;
      fahrenheit = celsius * 1.8 + 32.0;
      Serial.print("  Temperature = ");  //  "Температура = "
      Serial.print(celsius);
      Serial.print(" Celsius, ");  //  " по Цельсию, "
      Serial.print(fahrenheit);
      Serial.println(" Fahrenheit");  //  " по Фаренгейту"
    }

 

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

То есть, строчки № 63-65 перенестри в позицию № 114?

Заранее спасибо!

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Sonologist пишет:

То есть, строчки № 63-65 перенести в позицию № 114? Понятно, что при первом чтении результата мы получим ноль, но потом, вроде, все должно работать штатно.

Заранее спасибо!

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Если не выставлен флаг busy, послал запрос, запомнил время, выставил флаг busy. Через N ms, если флаг busy выставлен, считал результат, сбросил флаг.

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

sadman41 пишет:

Если не выставлен флаг busy, послал запрос, запомнил время, выставил флаг busy. Через N ms, если флаг busy выставлен, считал результат, сбросил флаг.

Ага, это я тоже рассматривал. Но тут появляется новая переменная (флаг). В моем предложении - лишняя. Или я что-то по неразумности и неквалифицированности сказанул в топике не то?

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Я тему вообще не понял. "Когда лучше пить чай?" - вот примерно так она для меня выглядит. А с "лишней переменной" вообще добили - у Вас норматив? Во сколько переменых нужно уложиться? В курсе ли Вы, что МК эти переменные не только не считает, но и не видит, в принципе?

vvadim
Offline
Зарегистрирован: 23.05.2012

посмотрите пример блинк без делея и сделайте аналогично чтение показаний датчика.

bwn
Offline
Зарегистрирован: 25.08.2014

Ссылку на тему Dimax походу не читали.((((

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

vvadim пишет:

посмотрите пример блинк без делея и сделайте аналогично чтение показаний датчика.

Насколько я вас понимаю, предлагается или такая конструкция:

long interval;
  
void setup()
{
}
 
void loop()
{
  // запрос на чтение температуры датчиком

  unsigned long currentMillis = millis();
  
  if(millis() - interval > 1000)
    {
    //  считываем показания датчика и показываем их так или иначе
    interval=millis();
    }
}

 

или такая:

long interval;
  
void setup()
{
}
 
void loop()
{
  unsigned long currentMillis = millis();
  
  if(millis() - interval > 1000)
    {
    //  считываем показания датчика и показываем их так или иначе
    interval=millis();
    // запрос на чтение температуры датчиком 
    }
}

Но в данном случае сам способ отсчета времени я не имел ввиду (их может быть несколько). Речь идет о запросе на измерение температуры в начале лупа (как в примерах бибилиотек) или сразу после считывания результата.

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

bwn пишет:

Ссылку на тему Dimax походу не читали.((((

Буду признателен, если ткнете меня в нее носом :)

bwn
Offline
Зарегистрирован: 25.08.2014

Sonologist пишет:

bwn пишет:

Ссылку на тему Dimax походу не читали.((((

Буду признателен, если ткнете меня в нее носом :)

Тыц, а еще #10 и #11, в Вашей же теме.(((

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

bwn пишет:

Тыц, а еще #10 и #11, в Вашей же теме.(((

Спасибо, там-то я побывал (думал, Димакс еще где-то полезное про это написал). Но беда в том, что пока с портами и адресами у меня не сильно здорово :( То есть. основная мысль оттуда понятна, но как ее самому себе разжевать - еще не знаю. Конечно, буду образовавываться, но пока задал  более приземленный (для меня) вопрос.

bwn
Offline
Зарегистрирован: 25.08.2014

Там ниже, еще на первой странице, есть подпрограмма на миллисе, по образу и подобию, кому таймер собаки занимать не хотца.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Sonologist пишет:

Но в данном случае сам способ отсчета времени я не имел ввиду (их может быть несколько). Речь идет о запросе на измерение температуры в начале лупа (как в примерах бибилиотек) или сразу после считывания результата.

В упрощённых блокирующих примерах библиотек, как правило, стоит delay(), который придерживает следующий запрос к датчику на N ms. 

Фактически всегда получается такая конструкция:

while (true) {
   // запрос
   delay(1000); // время на конверсию
   // получение результата
}

Это практически то же самое, что и неблокирующая реализация:

while (true) {
  unsigned long currentMillis = millis();
  
  if(millis() - startWaitTime > 1000UL) {
     startWaitTime = millis();
     // чтение датчика
     // запрос на чтение температуры датчиком 
  }
}

Но, обратите внимание на то, что в ней чтение датчика приходится ставить впереди запроса. Корректно ли это?

А если перепишем вот так?

readingDone = true;

while (true) {
  unsigned long currentMillis = millis();

  if (readingDone) {
     startWaitTime = millis();  
     // запрос на чтение температуры датчиком 
     readingDone = false;
  } else {
    if (millis() - startWaitTime > 1000UL) {
       // чтение датчика
       readingDone = true;
    }
  }
}

 

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

bwn пишет:

Там ниже, еще на первой странице, есть подпрограмма на миллисе, по образу и подобию, кому таймер собаки занимать не хотца.

Спасибо, прочел уже.

bwn
Offline
Зарегистрирован: 25.08.2014

Sonologist пишет:

Спасибо, прочел уже.

Помогло, али опять мимо?

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Помогло. Во всяком случае, в смысле воспитания. Но, тем не менее, там упорно используется флаг. Мне он по-прежнему кажется лишним, если запрашивать сенсор сразу после его прочтения посредством "старушки"- миллис :).

MaksVV
Онлайн
Зарегистрирован: 06.08.2015
#include <OneWire.h>
OneWire  ds(2);  // выбор пина ардуино, на котором висит шина 1-wire 

// ниже перечисление температур 

 enum Temper_enum
{
    KuhnyaC,            //0
    VannaNizC,          //1
    FreezeC,            //2
    UlicaC,             //3 
    
    size_array_Temp     //4 size
                   
} Temper_ENUM;   


// ниже соответствие адресов датчиков определённым температурам
byte ADDR_DS18B20 [size_array_Temp][8] =
{
{0x28, 0xFF, 0xB1, 0x43, 0x52, 0x15, 0x01, 0xDB}, //KuhnyaC   0
{0x28, 0xFF, 0xBA, 0x6C, 0x52, 0x15, 0x01, 0x41}, //VannaNizC 1
{0x28, 0xEE, 0x9B, 0x4D, 0x25, 0x16, 0x02, 0xC5}, //FreezeC   2
{0x28, 0x9D, 0xE5, 0x70, 0x01, 0x00, 0x00, 0xEE}  //UlicaC    3
};


// ниже сами переменные температур, изначально ставим +20*С
int8_t  Temper[size_array_Temp] = 
{
  20,     //KuhnyaC   0
  20,     //VannaNizC 1
  20,     //FreezeC   2
  20      //UlicaC    3
};

// например, чтобы использовать переменную температуры на улице, пишем так: Serial.print (Temper[UlicaC]); 


uint32_t prev1Wire_polling = 0;
byte interval_1wire = 4;  // интервал опроса шины 1-wire, *2 (сек.) в данном случае 8 сек
boolean n=0;

void setup() {
Serial.begin(9600);
}

void loop() {

dallas();

             }


void dallas() {

  if (millis() - prev1Wire_polling > (uint32_t)interval_1wire*1000){ // Здесь происходит опрос датчиков шины 1-Wire
 
n=!n;                // флаг работы: запрос температуры или её чтение
if (n) {ds.reset();  // сброс шины
        ds.write(0xCC);//обращение ко всем датчикам
        ds.write(0x44);// начать преобразование (без паразитного питания)  
       }
else   {
  for (byte i = 0; i < size_array_Temp; i++){// цикл фор перебирает все датчики t 
        int Temper_= 20;  
        byte buff[9];
        
        ds.reset();
        ds.select(ADDR_DS18B20[i]);    //выбор адреса DS18B20
        ds.write(0xBE); // Read Scratchpad (чтение регистров)  
        for (byte j = 0; j<9; j++) buff[j]= ds.read();    //читаем все 9 байт от датчика 
        ds.reset();
        if (OneWire::crc8(buff, 8) == buff[8]){ // если CRC верна
        Temper_ =  buff[0] | (buff[1]<<8); // читаем температуру из первых двух байт (остальные были нужны для проверки CRC)
        Temper_ = Temper_ / 16;  
                if  (Temper_ < 150 && Temper_ > -50 && Temper_ !=85 && Temper_!=-127)  // ещё раз перестраховываемся от дерьмовых значений
                     {
        Temper[i] =  (int8_t) Temper_; // всё, тут уже пишем температуру в переменную
                     }
                                              }
        
        
                                            } 
       }

Serial.print ("Temper AntiFreeze = "); Serial.println (Temper[FreezeC]);   // так для примера

 prev1Wire_polling = millis();
}
}

Sonologist пишет:

П Но, тем не менее, там упорно используется флаг. Мне он по-прежнему кажется лишним, если запрашивать сенсор сразу после его прочтения посредством "старушки"- миллис :).

вам что так часто температуру надо читать? есть флаг и ладно. Работает хорошо, зачем воду мутить?

bwn
Offline
Зарегистрирован: 25.08.2014

Sonologist пишет:

Помогло. Во всяком случае, в смысле воспитания. Но, тем не менее, там упорно используется флаг. Мне он по-прежнему кажется лишним, если запрашивать сенсор сразу после его прочтения посредством "старушки"- миллис :).

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

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Друзья, спасибо. Тайм-аут: бегу в операционную. Вечером все проштудирую.!

Pyotr
Offline
Зарегистрирован: 12.03.2014

Вот такой пример чтения далласа через определенное количество сек. Температура в сотых градуса int.

#include <OneWire.h>
#define DS_PIN   4
OneWire ds(DS_PIN);
int temp;
word currMillis, prevMillis, intervalMs = 10;
byte second, countCycle;
void setup() {
  Serial.begin(9600);
  startDS();
}

void loop() {
  currMillis = millis();
  if(currMillis - prevMillis >= intervalMs){//10ms
    prevMillis += intervalMs;

    switch (countCycle){
//      case 0 ... (TOT_STR_OLED-1):
//        printLCD(countCycle); 
//      break; 
      case 5:
        if((second % 3) == 0)//закомент.если нужно каждую сек
          temp = getDS();//кажд.3 сек. В сот *С 
        Serial.println(temp);      
        break;
      case 6: 
        if((second % 3) == 2)//закомент.если нужно каждую сек
          startDS(); 
        break;//за 1 сек до чтения
//      case 10:  
//        if((second % 3) == 0)  read_Si7021();//20 раз в мин
//        break;      
    }    
    if(++countCycle >= 100){//прошла  1 сек
      countCycle = 0;
      if(++second >= 60) second = 0;; //или с RTC
    }
  }  
}
//=============================
void startDS(){//Т котла
  ds.reset();  //команда на преобразов. = 2200 mksec
  ds.write(0xCC);// пофиг на адреса (SKIP ROM)
  ds.write(0x44);// начать преобразование 
}
//=======================
int getDS(){//возвр. полож. и отрицательную Т в сотых *С
  int t = 0;//При опросе раз в 2 сек Т датчика поднимается на 0.4С
  ds.reset();      // чтение3250 mks
  ds.write(0xCC);
  ds.write(0xBE);
  t = ds.read() | (ds.read()<<8); 
  //if(t == 0x550) error;//+85C = B10101010000 (первое измерение после подачи питан)
  //if(t == 0xFFFF) //(-1 == 0xFFFF)  нет датчика при вкл. подтяжке
  if(t == 0xFFFF){// -1 если есть подтяжка
    t = -10000;//ошибка -100*C
  }else /*if(t < 0x550)*/{ //для Т воздуха
    int tFract = ((t & 0x0F) *100 +8) / 16;//  fractional - дробная
    t = t / 16 *100 + tFract;
  }
  return t;//Т в сотых *С
}

 

ratman
Offline
Зарегистрирован: 11.10.2015

sadman41 пишет:

А если перепишем вот так?

readingDone = true;

while (true) {
  unsigned long currentMillis = millis();

  if (readingDone) {
     startWaitTime = millis();  
     // запрос на чтение температуры датчиком 
     readingDone = false;
  } else {
    if (millis() - startWaitTime > 1000UL) {
       // чтение датчика
       readingDone = true;
    }
  }
}

Тогда при опросе по таймеру раз в пять минут получаем данные пятиминутной свежести.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

ratman пишет:

Тогда при опросе по таймеру раз в пять минут получаем данные пятиминутной свежести.

...и? Перепишите так, как полагается в продакшне, если считаете, что объясняющий принцип работы флага пост должен содержать трёхстраничный код, который делает вообще всё правильно.

inspiritus
Offline
Зарегистрирован: 17.12.2012

Я делаю всегда так используя или TimerOne, или миллис:

перввый запуск счета в сетупе и там же запуск задержки на  требуемое время обновления температуры, например «раз в минуту».

когда время наступило считываю готовую температуру , запускаю измерение и новую задержку.

процессы достаточно медленно-меняющиеся и развминуту видеть , что было 59.250 сек назад мне вполне достаточно.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

sadman41 пишет:

ratman пишет:

Тогда при опросе по таймеру раз в пять минут получаем данные пятиминутной свежести.

...и? Перепишите так, как полагается в продакшне, если считаете, что объясняющий принцип работы флага пост должен содержать трёхстраничный код, который делает вообще всё правильно.

не должен!
Но ТС человек настойчивый упорно не желает делать по простому, с флагом

Green
Offline
Зарегистрирован: 01.10.2015

Часто делаю в лупе функцией из 8! шагов, что бы не тормозить сам луп.