15.07.2009

Аналоговые датчики температуры и Arduino

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

Обычно, мы получаем сенсор в корпусе TO-92 (не перепутайте с транзистором):



На фото - популярный датчик TMP36 от Analog Devices. Как видите, выводов три, и если смотреть в положении "надписью к нам, ножками вниз", то получается (слева направо): питание (Vs), выход (Vout), земля (GND). Главная особенность таких датчиков - значение напряжения на выходе однозначно определяет температуру, независимо от напряжения питания (!). Последнее может варьироваться от 2,7 до 5,5 Вольт.

Это очень удобно, и позволяет нам выполнить проверку датчика вне схемы, если под рукой есть элемент питания 3В, хотя бы даже и батарейка CR2032. В активном состоянии датчик потребляет не более 50 мкА, для проверки в течение двух-трех минут вполне хватает. Собираем схему:



Теперь, подключая мультиметр к выходу датчика (разумеется, в режиме измерения напряжения), мы будем наблюдать напряжение около 0,78 В, что соответствует 28 °С.



Чтобы убедиться в работоспособности датчика, его надо слегка нагреть, отлично подойдут:
  • пальцы рук (температура вашего тела - 36,6 °С)
  • дующий горячим возухом фен (можно довести и до 50 °C)
  • кошка (температура 38-39 °C)

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



Теперь подключим датчик к Arduino. Пусть выход будет подключаться к analog0, а питание - к стабилизированному питанию +5В Arduinio:



Для чтения придется использовать АЦП, а ему, как известно, требуется опорное напряжение, надо выбрать один из трех вариантов: питание, внешнее на AREF или внутреннее.

Напряжение на выходе датчика TMP36 меняется от 100 мВ до 2В, так что использовать внутренний источник опорного напряжения 1,1В не получится. Хотя, такое значение у источника МК ATmega168/328, а вот в ATmega8 это будет уже 2,56 В. Гораздо проще ориентироваться на стабилизированные +5 В, которые поступают от USB.

Чтобы правильно перевести показания датчика в температуру, надо сначала понять, какое напряжение мы прочитали. АЦП возвращает число от 0 до 1023, при этом 0 = 0В, 1023 = 5В для нашего случая. Поэтому:

voltage = 5 В / 1024 * sensor

Заглянем в документацию на датчик: там оговаривается, что изменение на один градус цельсия соответствует изменению на 10 мВ, при этом 500 мВ будет соответствовать температуре 0°C. Получаем формулу:

tempC = (voltage - 0.5) * 100


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

void loop() {
float voltage = 5.0 / 1024.0 * analogRead(0);
Serial.print(voltage);
Serial.println(" B");
float tempC = ( voltage - 0.5 ) * 100;
Serial.print(tempC);
Serial.println(" C");
delay(1000);
}


Одно маленькое замечание к тексту скетча: значение опорного напряжения надо писать именно "5.0", чтобы компилятор случаем не решил разделить 5 на 1024 целочисленным делением и не получил пожизненный ноль в итоге.



Вернемся к вопросу о точности. Наш АЦП имеет шаг измерения 4,9 мВ, в то время как сам датчик имеет погрешность ±1°C, или ±10 мВ. Таким образом, даже если мы понизим опорное напряжение, например, до 3,3 В и получив таким образом шаг преобразования 3,3 / 1024 = 3,2 мВ, это повышение точности не спасет нас от ошибки самого сенсора. С другой стороны, напряжение питания +5 В тоже может запросто "гулять" ±5%, но в итоге это порождает ту же самую ошибку ±10 мВ. Таким образом, как ни крути, для данной схемы погрешность измерения будет не менее ±2°C. Из этого следует, кстати, забавный метрологический вывод: дробную часть можно отбрасывать ;)

(в статье использованы материалы из статьи про измерение температуры by ladyada)

32 комментария:

  1. вот здорово,красота!! :)
    возможно будет интересно-датчик давления за пять рублей:одноразовый шприц,таблетка активированного угля,пружинка и два провода.

    ОтветитьУдалить
  2. да, забавная, наверное, конструкция.

    нельзя ли немного поподробнее? ;)

    ОтветитьУдалить
  3. с двух сторон сторон таблетки проводники(у меня кружки из фольгированного тестолита с припаянными проводами,между ними таблетка),выводятся в носик шприца,сверху пружинка,чтоб поршень возвращать,еще выше поршень шприца,давилка обрезана,давление сверху поршня,всё.таблетка изменяет сопротивление,при 0.8 кг/квсм китайский мультиметр показывает 74,0 ом при 1кг/квсм 72.7 ом в этих пределах,при моем поршне и таблетке почти прямая,я его планирую как отключатель,точность приемлема.беда только-при включении насоса цифры вылезают дикие,а потом вообще зависает,помехи наверно.пальцем давишь-всё в порядке,и прибор китайский исправно показывает.

    ОтветитьУдалить
  4. А, теперь понятно, спасибо ;)

    А кто зависает-то? Какое-то устройство на МК?

    ОтветитьУдалить
  5. выдает какую нибудь цифру левую и перестает реагировать на все изменения,я тогда нажимал кнопку "загрузить скетч" со стрелочкой такая.так вроде,давно не трогал я его

    ОтветитьУдалить
  6. После нажатия на кнопку загрузки обычно происходит сброс, если речь про Arduino, и если Arduino с автосбросом, конечно.

    Чтобы понять, почему зависает, надо смотреть скетч, а затем схему...

    ОтветитьУдалить
  7. а,разберусь.вдохновение вот найдет только :)

    ОтветитьУдалить
  8. Скажите пожалуйста,а почему у меня не отражаются десятые доли,скопировал-вставил ваш пример,проверить-не работает :(
    если написать так:
    void setup() {
    Serial.begin(38400);
    }

    void loop() {
    float voltage = 5.0 / 1024.0;
    Serial.print(voltage);
    delay(1000);
    }
    ,то скетч не грузится,пишет ошибку(на скриншоте видно),а если так:
    void setup() {
    Serial.begin(38400);
    }

    void loop() {
    float voltage = 5.0 / 1024.0;
    Serial.print(voltage ,DEC);
    delay(1000);
    }
    то получается ноль


    http://s52.radikal.ru/i136/0908/04/17bbddcf2b64.jpg

    ОтветитьУдалить
  9. Это где же вы такой древний раритет откопали?!

    Возьмите последнюю ArduinoIDE, тот же 0017 - всё должно заработать.

    В последнем случае пишется 0 из-за неявного приведения типов, видимо результат меньше нуля, после приведения к целому дробная часть отбрасывается. Остается ровно ноль ;)

    ОтветитьУдалить
  10. почему то всё равно не выходит :(
    http://i038.radikal.ru/0908/ef/ba951517efe0.png

    ОтветитьУдалить
  11. а с реальным датчиком всё работает!спасибо,никогда бы не подумал,что дело может быть в софте

    ОтветитьУдалить
  12. Вы хотите распечатать 0,0048828125? Видимо, Serial.print() для типа float предлагает только две цифры после запятой, отсюда и 0,00.

    ОтветитьУдалить
  13. Здравствуте!
    Интересная статья!Я сам новичёк и ещё толком не разобрался с программированием Arduin,пока что получается играться со светодиодами и мерить температуру.У меня такой вот вопрос.Можно ли Arduino Duemilanove настроить так ,что при показании температурного датчика(LM 35)скажем +35 С электромагнит получит свои 9v , ну а если электричество и вовсе отключенно то питание берётся с батарейки "кроны" 9v ??

    ОтветитьУдалить
  14. Ничего не понял :( Надо электромагнитом управлять, что ли?

    ОтветитьУдалить
  15. Устройство такое.Есть аналоговый температурный датчик LM-35,трансформатор на 9v,Arduino Duemilanove,электромагнитный замок потребляющий 8-12v и батарейка на 9v.Задача в том чтоб электромагнитный замок открыл дверцу если температура в комнате превысила +35 С ну и автономное питание-это на случай сбоев в электрической сети чтоб автоматом на батарейку перешёл.Понятно что электромагниту нужно всего 2 секунды задержки чтоб наверняка окрыть дверь.
    Возможно ли всё это сделать при помощи Arduino Duemilanove без реле и прочей конителей?
    Спасибо!

    ОтветитьУдалить
  16. Возможно, если исполнительный механизм замка будет совместим с сигналами TTL. А без этого, увы, "конителей" не избежать...

    ОтветитьУдалить
  17. Совместим.Что мне в скетче написать чтоб при температуре +35С Arduino послал сигнал на эл.замок ? Спасибо.

    ОтветитьУдалить
  18. if (temperature >= 35) digitalWrite(controlPin, HIGH);

    Естественно, температуру надо сначала вычислить (по шкале Цельсия) и поместить в переменную temperature.К пину controlPin, сконфигурированному как выход, подключен исполнительный вход реле.

    Где-то так.

    ОтветитьУдалить
  19. спасибочки!Будем пробовать.

    ОтветитьУдалить
  20. Каким образом можно сделать весь процесс в скетче одноразовым?То есть для повторного запуска нужно нажать на кнопку reset.Спасибо.

    ОтветитьУдалить
  21. Поместите весь код внутри setup. А loop оставьте пустым.

    ОтветитьУдалить
  22. а как же вычисление температуры?

    void loop()
    {
    double val = analogRead(tempPin);
    Serial.print(" Analog 0: ");
    Serial.print(int(val));
    double voltage = val * 5.0/1024;
    double temp = voltage * 100;
    Serial.print(" Temperature: ");
    Serial.println(temp);
    delay(1000);
    {

    ОтветитьУдалить
  23. Поместите его в setup - и оно будет именно "одноразовым", исполняться только по reset.

    ОтветитьУдалить
  24. а как сделать чтоб температура выводилась на дисплэй постоянно т.е. в лупе а светодиод при превышении 25 С включился один раз?
    Спасибо.

    ОтветитьУдалить
  25. А что плохого будет, если его включать каждый раз?...

    Можно пойти и сложным путем:

    float current_temp;
    int temp_over_25 = 0;

    void setup() {
    ...
    }

    void measure_indicate() {
    ...
    /* текущая температура записывается в current_temp */
    }

    void loop() {
    indicate();
    if (!temp_over_25 && current_temp > 25.0) {
    action();
    temp_over_25 = 1;
    }
    }

    ОтветитьУдалить
  26. а куда светодиод вписывать,что-то не могу разобраться?

    ОтветитьУдалить
  27. действие, производимое однократно, можно поместить в процедуру action().

    ОтветитьУдалить
  28. что то не получается у меня ничего.
    А нельзя ли этот скетч поправить?

    float temp;
    int tempPin=0;
    int controlPin=8;
    int powerPin=13;
    void setup()
    {
    Serial.begin(9600);
    pinMode(powerPin, OUTPUT);
    digitalWrite(powerPin, HIGH);

    }

    void loop()
    {
    temp = analogRead(tempPin) * 5.0/1024 * 100;
    Serial.print(" Temperature: ");
    Serial.println(temp);
    delay(1000);

    if (temp > 25)
    {
    digitalWrite(controlPin, HIGH);
    delay(5000);
    digitalWrite(controlPin, LOW);
    delay(1000);
    }

    }

    температура должна постоянно вычисляться,но как только превышает +25С - должен загореться светодиод(5 секунд) и на этом вся программа заканчивается, а для того чтоб возобновить работу скетча нужно нажать на сброс.И ещё вопрос.
    Как можно вывести кнопку сброса самой ардуины отдельно?Спасибо.

    ОтветитьУдалить
  29. заменить последний блок на:

    if (temp > 25.0) {
    digitalWrite(controlPin, HIGH);
    delay(5000);
    digitalWrite(controlPin, LOW);
    while(1) {};
    }

    Внешняя кнопка сброса включается между пинами с надписью "RESET" и "GND", в нормальном состоянии должна быть разомкнута.

    ОтветитьУдалить
  30. подскажите пож-та а почему так прыгают показания?

    22.75 C
    0.74 B
    24.22 C
    0.75 B
    25.20 C
    0.76 B
    26.17 C
    0.77 B
    26.66 C
    0.84 B
    33.98 C
    0.73 B
    23.24 C
    0.83 B
    33.50 C
    0.75 B
    25.20 C
    0.74 B
    23.73 C
    0.70 B
    20.31 C
    0.76 B
    26.17 C

    ОтветитьУдалить
  31. Не знаю.

    Но вообще-то не должны - проверьте схему подключения, попробуйте поменять датчик.

    Рекомендую DS18B20 - там, по крайней мере, цифровой интерфейс, меньше влияние побочных факторов типа проводов.

    ОтветитьУдалить