DS18B20 && Arduino

Тема в разделе "Arduino & Shields", создана пользователем Salk, 1 мар 2015.

  1. Salk

    Salk Гик

    Всем привет. Все, наверняка, знают цифровые термодатчики DS18B20. Вот и я решил с ними познакомится поближе. Купил - подключил - работают. НО...
    Никак в толк не возьму, либо я что-то упускаю из вида в работе с ними, либо действительно проблема в датчиках.
    Суть - решил использовать эти датчики в своем проекте, на ряду с такими компонентами, как LCD (I2C), ИК-приемник и многое, многое другое, но остановимся на этом.
    Датчики - 2 шт., используемая библиотека <DallasTemperature.h>. Так вот, при опросе датчиков и вывода значений на экран, Arduino "тормозит" и очень заметно, соответственно все остальные процессы (в скетче) тоже работают с задержкой: команды с ИК-пульта доходят через раз и все, что управляется с пульта, разумеется, работает раз через раз и т.д.
    Я понимаю, что датчики цифровые и нужно время, чтобы их опросить, но все же - это особенность библиотеки или все ресурсы Arduino Uno тратятся на снятия показаний с датчиков?
    Скетч:
    PHP:
    #include <IRremote.h>            // IR
    #include <Wire.h>                // I2C
    #include <LiquidCrystal_I2C.h>  // LCD
    #include <OneWire.h>            // DS18b20
    #include <DallasTemperature.h>

    #define RELAY_PIN      9
    #define RELAY_PIN2    10
    #define RELAY_PIN3    11
    #define IR_PIN        5
    // DS18b20
    #define ONE_WIRE_BUS  4

    LiquidCrystal_I2C lcd(0x27, 20, 4);
    #define LCD_UPDATE_TIME  250
    unsigned long lcdLastUpdateTime = 0;

    IRrecv irrecv(IR_PIN);
    decode_results results;

    // Реле
    bool LampState = false;
    bool LampState2 = false;
    bool LampState3 = false;

    //DS18b20
    OneWire oneWire(ONE_WIRE_BUS);
    DallasTemperature sensors(&oneWire);
    DeviceAddress sensor1 = {0x28, 0xFF, 0xF2, 0x9C, 0x67, 0x14, 0x04, 0xF7}; // адреса датчиков
    DeviceAddress sensor2 = {0x28, 0xFF, 0xA1, 0x9B, 0x67, 0x14, 0x04, 0xAA};

    // Температура
    float temp1; // текущая температура первого датчика
    float temp2; // второй датчик

    void setup()
    {
      Wire.begin();
      lcd.init();
      lcd.backlight(); // вкл. подсветку LCD (noBack-выкл)
      lcd.clear();
      sensors.begin();
      sensors.setResolution(sensor1, 10);
      sensors.setResolution(sensor2, 10);
      pinMode(IR_PIN, INPUT);
      pinMode(RELAY_PIN, OUTPUT);
      pinMode(RELAY_PIN2, OUTPUT);
      pinMode(RELAY_PIN3, OUTPUT);
      digitalWrite(RELAY_PIN, LOW);
      digitalWrite(RELAY_PIN2, LOW);
      digitalWrite(RELAY_PIN3, LOW);
      irrecv.enableIRIn(); // Включаем ресивер
    }

    void loop()
    {
      // Реле
      if (irrecv.decode(&results))
      {
        if (results.value == 524543)
          LampState = !LampState;
        if (LampState)
        {
          digitalWrite(RELAY_PIN, HIGH);
        }
        else
        {
          digitalWrite(RELAY_PIN, LOW);
        }

        if (results.value == 573503)
          LampState2 = !LampState2;
        if (LampState2)
        {
          digitalWrite(RELAY_PIN2, HIGH);
        }
        else
        {
          digitalWrite(RELAY_PIN2, LOW);
        }

        if (results.value == 532703)
          LampState3 = !LampState3;
        if (LampState3)
        {
          digitalWrite(RELAY_PIN3, HIGH);
        }
        else
        {
          digitalWrite(RELAY_PIN3, LOW);
        }
        irrecv.resume(); // Получаем следующее значение
      }

      if (millis() - lcdLastUpdateTime > LCD_UPDATE_TIME) // обращаемся к датчикам раз в 250 мс
      {
        lcdLastUpdateTime = millis();
        // отображение на LCD
        printTemperature(sensor1); // опрашиваем датчики
        printTemperature(sensor2);
      }
    }

    void printTemperature(DeviceAddress deviceAddress) // DS18B20
    {
      sensors.requestTemperatures();
      // Температура
      temp1 = sensors.getTempC(sensor1); // получаем температуру
      lcd.setCursor(11, 0);
      lcd.print("T1: ");
      lcd.print(temp1, 1); // отображение температуры
      lcd.print("C ");
      lcd.print(" ");

      temp2 = sensors.getTempC(sensor2);
      lcd.setCursor(11, 1);
      lcd.print("T2: ");
      lcd.print(temp2, 1); // отображение температуры
      lcd.print("C");
      lcd.print(" ");
    }
    Если закомментировать строчки:
    Код (Text):
    printTemperature(sensor1); // опрашиваем датчики
    printTemperature(sensor2);
    то скетч выполняется как надо, без всяких задержек. Если только одну, то скетч выполняется с менее сильными задержками, но они все равно очень сильно портят общение с Arduino :(
    Можно как-то оптимизировать затрачиваемые ресурсы Arduino, чтобы ей хватало времени на работу с другими устройствами? Спасибо.
     
    AL___2008 нравится это.
  2. vvr

    vvr Инженерище

    Tomasina и Megakoteyka нравится это.
  3. Tomasina

    Tomasina Сушитель лампочек Модератор

    это особенность этих датчиков - им надо 0,7 сек на обработку данных.
    Используемая вами библиотека это время просто тупо ждет.

    Выход - обращаться к датчику два раза: первый раз - дать ему команду на измерение и обработку данных, но не ждать ответа; второй раз - через 1 сек (можно использовать скетч Blink without delay для отсчета времени без задержки) и забрать готовые данные.
     
  4. Salk

    Salk Гик

    Простите, Я не понял, как сделать:
    Вы имеете ввиду вообще отказаться от использования библиотеки DallasTemperature ?

    Я поковырялся в самой библиотеке DallasTemperature.cpp. Попытался заменить delay на millis. Заменить, то заменил, но ничего не изменилось.
    PHP:
    ///
    #define CASE_9_UPDATE_TIME  94
    unsigned long CASE_9LastUpdateTime = 0;

    #define CASE_10_UPDATE_TIME  188
    unsigned long CASE_10LastUpdateTime = 0;

    #define CASE_11_UPDATE_TIME  375
    unsigned long CASE_11LastUpdateTime = 0;

    #define CASE_12_UPDATE_TIME  750
    unsigned long CASE_12LastUpdateTime = 0;

    // Wait a fix number of cycles till conversion is complete (based on IC datasheet)
    switch (*bitResolution)
    {
    case 9:
      if (millis() - CASE_9LastUpdateTime > CASE_9_UPDATE_TIME)
      {
        CASE_9LastUpdateTime = millis();
        break;
      }

    case 10:
      if (millis() - CASE_10LastUpdateTime > CASE_10_UPDATE_TIME)
      {
        CASE_10LastUpdateTime = millis();
        break;
      }

    case 11:
      if (millis() - CASE_11LastUpdateTime > CASE_11_UPDATE_TIME)
      {
        CASE_11LastUpdateTime = millis();
        break;
      }

    case 12:
      if (millis() - CASE_12LastUpdateTime > CASE_12_UPDATE_TIME)
      {
        CASE_12LastUpdateTime = millis();
        break;
      }
    }
    Подскажите, пожалуйста, поподробнее, как избавится от задержек. Спасибо.
     
  5. Выбросьте библиотеку DallasTemperature,
    используйте библиотеку OneWire,
    операции раскидайте по циклу как хотите, задав свои задержки.

    Никогда не смешивайте операции ввода и вывода внутри библиотек.
     
  6. Salk

    Salk Гик

    Спасибо за совет. Тоже склонялся уже выбросить эту библиотеку :) Но разве с помощью OneWire можно подключить два датчика ?
     
  7. Salk

    Salk Гик

    Скетч с одной библиотекой OneWire получился такой. Да работает без задержек. Но как сюда добавить второй датчик ?
    PHP:
    #include <IRremote.h>            // IR
    #include <Wire.h>                // I2C
    #include <LiquidCrystal_I2C.h>  // LCD
    #include <OneWire.h>

    #define IR_PIN        5
    //DS18b20
    OneWire ds(4);

    bool LampState4 = false;

    int Temp;

    LiquidCrystal_I2C lcd(0x27, 20, 4);
    #define LCD_UPDATE_TIME  1000
    unsigned long lcdLastUpdateTime = 0;

    IRrecv irrecv(IR_PIN);
    decode_results results;

    void setup()
    {
      Wire.begin();
      lcd.init();
      lcd.backlight(); // вкл. подсветку LCD (noBack-выкл)
      lcd.clear();
      pinMode(IR_PIN, INPUT);
      irrecv.enableIRIn(); // Включаем ресивер
    }

    void loop()
    {
      if (irrecv.decode(&results))
      {
        if (results.value == 565343)
          LampState4 = !LampState4;
        if (LampState4)
        {
          lcd.noBacklight(); // выкл. подсветку
        }
        else
        {
          lcd.backlight(); // вкл. подсветку
        }
        irrecv.resume(); // Получаем следующее значение
      }
      setTemperature(); // вызываем функцию работы с DS18b20
    }

    void setTemperature() // DS18B20
    {
      byte data[2];
      ds.reset();
      ds.write(0xCC);
      ds.write(0x44);
      // обращаемся к датчикам раз в 1000 мс, т.к. 750 может быть недостаточно
      if (millis() - lcdLastUpdateTime > LCD_UPDATE_TIME)
      {
        lcdLastUpdateTime = millis();
        ds.reset();
        ds.write(0xCC);
        ds.write(0xBE);
        data[0] = ds.read();
        data[1] = ds.read();
        Temp = (data[1] << 8) + data[0];
        Temp = Temp >> 4;
      }

      // Температура
      lcd.setCursor(11, 0);
      lcd.print("T1: ");
      lcd.print(Temp); // отображение температуры
      lcd.print("C ");
    }
     
  8. Salk

    Salk Гик

    Аллилуйя. Добился чего хотел. Выкладываю скетч, может кому-то пригодится, кто захочет интегрировать в свой проект датчики температуры DS18B20 более 2 шт, не опасаясь задержек в работе других компонентов проекта (хотя может тема дубль, тогда извините).
    В данном варианте не требуется подключение "тормознутой" библиотеки DallasTemperature, только OneWire.
    PHP:
    #include <OneWire.h>
    #include <IRremote.h>            // IR
    #include <Wire.h>                // I2C
    #include <LiquidCrystal_I2C.h>  // LCD

    #define IR_PIN        5

    OneWire ds (4);
    byte data[12];
    byte addr1[8] = {0x28, 0xFF, 0xF2, 0x9C, 0x67, 0x14, 0x04, 0xF7};
    byte addr2[8] = {0x28, 0xFF, 0xA1, 0x9B, 0x67, 0x14, 0x04, 0xAA};
    unsigned int raw;
    float temp1, temp2;

    bool LampState4 = false;

    LiquidCrystal_I2C lcd(0x27, 20, 4);
    #define LCD_UPDATE_TIME  1000
    unsigned long lcdLastUpdateTime = 0;

    IRrecv irrecv(IR_PIN);
    decode_results results;

    void setup()
    {
      Wire.begin();
      lcd.init();
      lcd.backlight(); // вкл. подсветку LCD (noBack-выкл)
      lcd.clear();
      irrecv.enableIRIn(); // Включаем ресивер
    }

    void loop()
    {
      temp1 = DS18B20(addr1);
      temp2 = DS18B20(addr2);

      if (irrecv.decode(&results))
      {
        if (results.value == 565343)
          LampState4 = !LampState4;
        if (LampState4)
        {
          lcd.noBacklight(); // выкл. подсветку
        }
        else
        {
          lcd.backlight(); // вкл. подсветку
        }
        irrecv.resume(); // Получаем следующее значение
      }
      lcd.setCursor(9, 0);
      lcd.print("T1: ");
      lcd.print(temp1); // отображение температуры
      lcd.print("C ");

      lcd.setCursor(9, 1);
      lcd.print("T1: ");
      lcd.print(temp2); // отображение температуры
      lcd.print("C ");
    }

    //==================================================================================
    //                            Считывание температуры
    //==================================================================================
    float DS18B20(byte *adres)
    {
      if (millis() - lcdLastUpdateTime > LCD_UPDATE_TIME) // обращаемся к датчикам раз в 1000 мс
      {
        lcdLastUpdateTime = millis();
        ds.reset();
        ds.select(adres);
        ds.write(0x44, 1); // start conversion, with parasite power on at the end
      }
      ds.reset();
      ds.select(adres);
      ds.write(0xBE); // Read Scratchpad

      for (byte i = 0; i < 9; i++) // можно увеличить точность измерения до 0.0625 *С (от 9 до 12 бит)
      { // we need 9 bytes
        data[i] = ds.read ();
      }
      raw =  (data[1] << 8) | data[0];//=======Пересчитываем в температуру
      float celsius =  (float)raw / 16.0;
      return celsius;
    }
    В качестве проверки работоспособности скетча, в данном случае, моргаем подсветкой LCD экрана посредствам нажатия кнопки ИК-пульта (все быстро, как и без датчиков :) )
    P.S. источник
     
    9xA59kK и pastry777 нравится это.
  9. vvr

    vvr Инженерище

    поздравляю!
     
  10. Не совсем корректно работает отображение 2-го датчика, параметр не меняется на экране
     
  11. Salk

    Salk Гик

    Да есть такое. Нужно играться с задержкой:
    PHP:
    #define LCD_UPDATE_TIME  1000
    Я уменьшил до 750 мс, так у меня работают два датчика. Бывает при включении Arduino, второй датчик "запустится" только через секунду, две. Пока не знаю, как доработать код.
     
  12. При изменении (#define LCD_UPDATE_TIME 1000) ничего не изменилось, как показывал 85.00 градусов так и показывает. Первый датчик исправно отображает температуру. При запуске другой программы с мониторингом через COM порт отображает оба датчика исправно.
     
  13. Salk

    Salk Гик

    Полностью удалить задержку и все будет работать как часы :D
    PHP:
    #include <OneWire.h>
    #include <IRremote.h>            // IR
    #include <Wire.h>                // I2C
    #include <LiquidCrystal_I2C.h>  // LCD

    #define IR_PIN        5

    OneWire ds (4);
    byte data[12];
    byte addr1[8] = {0x28, 0xFF, 0xF2, 0x9C, 0x67, 0x14, 0x04, 0xF7}; // ID индивидуально для каждого датчика
    byte addr2[8] = {0x28, 0xFF, 0xA1, 0x9B, 0x67, 0x14, 0x04, 0xAA};
    unsigned int raw;
    float temp1, temp2;

    bool LampState4 = false;

    LiquidCrystal_I2C lcd(0x27, 20, 4);

    IRrecv irrecv(IR_PIN);
    decode_results results;

    void setup()
    {
      Wire.begin();
      lcd.init();
      lcd.backlight(); // вкл. подсветку LCD (noBack-выкл)
      lcd.clear();
      irrecv.enableIRIn(); // Включаем ресивер
    }

    void loop()
    {
      temp1 = DS18B20(addr1);
      temp2 = DS18B20(addr2);

      if (irrecv.decode(&results))
      {
        if (results.value == 565343)
          LampState4 = !LampState4;
        if (LampState4)
        {
          lcd.noBacklight(); // выкл. подсветку
        }
        else
        {
          lcd.backlight(); // вкл. подсветку
        }
        irrecv.resume(); // Получаем следующее значение
      }
      lcd.setCursor(9, 0);
      lcd.print("T1: ");
      lcd.print(temp1); // отображение температуры
      lcd.print("C ");

      lcd.setCursor(9, 1);
      lcd.print("T1: ");
      lcd.print(temp2); // отображение температуры
      lcd.print("C ");
    }

    //==================================================================================
    //                            Считывание температуры
    //==================================================================================
    float DS18B20(byte *adres)
    {
      ds.reset();
      ds.select(adres);
      ds.write(0x44, 1); // start conversion, with parasite power on at the end
      ds.reset();
      ds.select(adres);
      ds.write(0xBE); // Read Scratchpad

      for (byte i = 0; i < 9; i++) // можно увеличить точность измерения до 0.0625 *С (от 9 до 12 бит)
      { // we need 9 bytes
        data[i] = ds.read ();
      }
      raw =  (data[1] << 8) | data[0];//=======Пересчитываем в температуру
      float celsius =  (float)raw / 16.0;
      return celsius;
    }
    Автор скетча, видимо, преднамеренно ввел задержку в 1 секунду, что сбило меня с толку. Я думал, что она нужна для подготовки данных с термодатчиков; никакой задержки здесь не нужно.
     
    9xA59kK, AL___2008, zserg и ещё 1-му нравится это.
  14. :D Счастливый как удав, заработало!!! На выходным буду пробовать управлять реле. Спасибо ;)
     
  15. float DS18B20(byte *adres) - не могу понять, что это значит? ранее adres в переменных не фигурировал
    ds.select(adres) - и как это работает? Ведь до этого вы прописывали адрес датчика в массив addr1? Где связь между addr1 и adres? Объясните пожалуйста новичку)
     
  16. vvr

    vvr Инженерище

    каждый датчик имеет свой уникальный адрес.
    вот его и определяют и по нему работают с датчиком.
    поэтому на один пин можно повесить гроздь этих датчиков
     
  17. Это мне понятно
    Код (PHP):
     #include <OneWire.h>

    OneWire ds (10);
    byte data[12];
    byte addr1[8] = {0x28, 0xC6, 0xAE, 0x0A, 0x05, 0x00, 0x00, 0x8C};
    byte addr2[8] = {0x28, 0x7D, 0xA6, 0x0A, 0x05, 0x00, 0x00, 0x4D};
    unsigned int raw;
    float temp1, temp2;

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

    void loop(){
      temp1 = DS18B20(addr1);
      temp2 = DS18B20(addr2);

      Serial.print("Temp1=");
      Serial.print(temp1);
      Serial.print("Temp2=");
      Serial.println(temp2);

    }

    //==================================================================================
    //  Считывание температуры
    //==================================================================================
    float DS18B20(byte *adres){
      ds.reset();
      ds.select(adres);
      ds.write(0x44,1); // start conversion, with parasite power on at the end
      delay(1000);
      ds.reset();
      ds.select(adres);
      ds.write(0xBE); // Read Scratchpad
      for (byte i = 0; i < 9; i++) { // we need 9 bytes
      data[I] = ds.read ();
      }
      raw =  (data[1] << 8) | data[0];//=======Пересчитываем в температуру
      float celsius =  (float)raw / 16.0;
      return celsius;
    }
    В переменных указаны массивы addr1 и addr2 - в этих массивах "жестко" прописаны адреса датчиков. А вот дальше - когда идет участок считывания температуры:
    строка
    Код (Text):
    float DS18B20(byte *adres)
    - что такое adres (да и вообще что значит это выражение)? Точнее я понимаю что это похоже адрес датчика, но откуда откуда взялась эта переменаня adres? Ранее в коде она НИГДЕ (в том числе в описании переменных) не фигурировала. Как же это работает?
     
  18. vvr

    vvr Инженерище

    почитайте библиотеку OneWire
     
  19. Почитал (саму библиотеку)... ничего не понял. В описании на сайте автора вообще ни слова про это.
    float DS18B20(byte *adres) - Товарищи, объясните что значит это выражение?
    Желательно на пальцах, float потому что имеем дело с дробным числом (температурой)?
    DS18B20 - название массива?
     
  20. Megakoteyka

    Megakoteyka Оракул Модератор

    Это функция, которая принимает один параметр (указатель на byte) и возвращает вещественное число. Читайте про функции в С/С++ и все поймете.