Электронный термометр - датчик BMP180 в связке с 7-ми сегментным дисплеем

03.12.2016 08:48

jazon



        Всем доброго времени суток, ну что же... просто начнем!!! Эта статья о подключении датчика барометрического давления BMP180 к одноплатному компьютеру Arduino Uno, но не так то всё просто, как хотелось бы на первый взгляд. Выводить измеренные и преобразованные значения температуры мы будем на 7-ми сегментный дисплей на базе драйвера MAX7219. И выводиться они будут в качестве бегущей строчки из двух преобразованных единиц измерения - это градусы Цельсия и градусы Фаренгейта.

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

  • Напряжение питания 3.3 вольт
  • Потребляемый ток 5 мКА при частоте опроса 1 Hz        
  • Интерфейс подключения - I2C
  • Разброс точности по измеренному давлению +-0.12 hPa (+-1 метр по высоте)   
  • Диапазон измерения температуры от -40*С до +85*С
  • Диапазон измерения давления: от 300 до 1100 hPа

Пришло время подключить датчик к плате Arduino Uno, но так как у нас замеренные и преобразованные значения будут выводиться на 7-ми сегментный дисплей, то на нижеследующей схеме мы покажем полное подключение всех приборов, а также распиновку датчика BMP180:


        

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

  • Выводить на дисплей только преобразованные значения температуры (ниже описывается почему)
  • Значения температуры должны быть реализованы с десятыми долями градуса(как это видно на картинке превью)
  • Реализовать визуализацию в виде бегущей строки
  • Обязательно наличие диагностических сообщений о состоянии датчика при включении и инициализации
  • Используем библиотеку SFE_BMP180.h для работы с датчиком

Почему же всё таки выводить только значения температуры... Конечно, хотелось бы охватить полностью все возможности датчика BMP180, но для визуализации на 7-ми сегментном дисплее, потребуется задействовать более сложные символы для отображения в сообщениях "бегущей строки", чем те которые можно реально создать используя генератор символов для модулей визуализации данного типа. Поэтому было решено отображать только преобразованные значения температуры, но и вообще в целом, обычно редко кто смотрит на показания атмосферного давления когда к примеру собирается на прогулку. И всё же - если кто-то захочет более тонко изучить работу с датчиком BMP180, то в подключаемой библиотеке есть два примера, которые могут более детально разъяснить как получать и преобразовывать давление и температуру при помощи данного датчика.    

В итоге, был создан вот такой скетч, с комментариями разработчика:

#include "SFE_BMP180.h"
#include "Wire.h"
#include "LedControl.h"

//Прототип функции для прокрутки сообщения msgScrolling()
void msgScrolling(byte msg[], int msgSize, int dScroll);
//Прототип функции по обработке данных температуры 
//и подготовке сообщения
void TmsgPreparation(double value, bool degree);

//Создаём объект LC класса LedControl для работы с дисплеем
LedControl LC = LedControl(12, 11, 10, 8);

//Создаём объект BMP180Sensor класса SFE_BMP180 для работы с датчиком
SFE_BMP180 BMP180Sensor;

//Время задержки отрисовки символов при прокрутке
const int delayScrolling = 300; 
//Сообщение "Инициализация датчика успешна"
byte msgInitSuccess[18] = {91, 79, 21, 91, 29, 5, 16, 21, 16, 15, 0, 91, 28, 13, 13, 79, 91, 91};
//Сообщение "Инициализация датчика безуспешна"
byte msgInitFail[15] = {91, 79, 21, 91, 29, 5, 16, 21, 16, 15, 0, 71, 119, 48, 14};

//Массив численных символов без точки
byte Digit[10]  = {126, 48, 109, 121, 51, 91, 95, 112, 127, 123};

//Массив численных символов с точкой
byte pDigit[10] = {254, 176, 237, 249, 179, 219, 223, 240, 255, 251};

//Символьные знаки
byte deg = 99;  //Символ "градуса"
byte C   = 78;  //Символ "С" - Цельсий
byte F   = 71;  //Символ "F" - Фаренгейт
byte S   = 1;   //Символ "-" - знак минуса(если измеренное значение ниже нуля)

void setup() 
{
  //Устройство(7-ми сегментный дисплей) выводим из спящего режима
  LC.shutdown(0, false);
  //Установить яркость дисплея на 8
  //Всего возможных режимов яркости от 0 до 15
  LC.setIntensity(0, 8);
  //Очистить дисплей
  LC.clearDisplay(0);

  //Инициализация датчика на шине I2C
  if(BMP180Sensor.begin())
  {
    //Сообщение на прокрутку - инициализация датчика успешна
    msgScrolling(msgInitSuccess, sizeof(msgInitSuccess), delayScrolling);
  }
  else
  {
    while(true)
    {
      //Сообщение на прокрутку - инициализация датчика безуспешна
      msgScrolling(msgInitFail, sizeof(msgInitFail), delayScrolling);
    }    
  }
}

void loop() 
{
  char status;
  double T, tCelsius, tFahrenheit;

  //Начинаем измерения температуры
  //функция startTemperature() возвращает количество миллисекунд
  //с начала процесса измерения, если процесс измерения прошел правильно,
  //сохраним это значение в переменную status
  //Если функция возвращает 0 то процесс измерения прошел неправильно
  status = BMP180Sensor.startTemperature();
  if(status != 0)
  {
    //Подождать то самое количество миллисекунд 
    delay(status);
    //Получить замеренное и обработанное значение температуры
    //в градусах Цельсия, и сохранить в переменной Т
    //функция getTemperature() возвращает 1 если вызов её
    //прошел успешно, и 0 если функция не смогла инициализировать 
    //переменную Т измеренным значением
    status = BMP180Sensor.getTemperature(T);
    if(status != 0)
    {
      //Присвоение значения переменным 
      //и обработка градуировок для шкалы Фаренгейта
      tCelsius = T;
      tFahrenheit = (9.0 / 5.0) * T + 32.0;
    }
  }
  //Обработка значения и
  //подготовка сообщения - температура
  TmsgPreparation(tCelsius, false);
  TmsgPreparation(tFahrenheit, true);
}

//Здесь собственно сама функция для прокрутки готовых сообщений
//справа - налево
/*
 * Параметры:
 *  byte msg[] - указатель на массив символьных данных
 *  int msgSize - размер массива символьных данных
 *  int dScroll - задержка прокрутки(сдвига сообщения влево) 
 *  в миллисекундах
 */ 
void msgScrolling(byte msg[], int msgSize, int dScroll)
{
  for(int i = 0; i < 8 + msgSize; i ++)
  {
    for(int n = i, j = 0; n >= 0, j < msgSize; n --, j ++)
    {
      LC.setRow(0, n, msg[j]);
      LC.setRow(0, n - 1, B00000000);
    }
    delay(dScroll);
  }
}

/*
 * Здесь функция подготовки сообщения, прежде чем оно будет выведено 
 * для прокрутки на дисплее. Параметры:
 *    double value - значение температуры двойной степени точности
 *    bool degree - флаг для определения шкалы градуирования
 *     если false - значит градусы Цельсия
 *     если true  - значит градусы Фаренгейта
 */
void TmsgPreparation(double value, bool degree)
{
  //Приводим к абсолютному значению и сохраняем в переменной T
  //Это унифицирует значение и сократит код функции вдвое
  double T = abs(value);
  //Здесь значение фильтруется, если есть отклонение на пол градуса
  //от нуля то всё равно формировать сообщение что температура равна 0
  //Итак, если замеренная температура меньше 0.5 то ставим 0
  if(T < 0.5)
  {
    //Резервируем символьный массив на три символа
    byte preMsg[3];
    //Первый символ - это естественно 0
    preMsg[0] = Digit[0];
    //Второй символ - это сгенерированный символ "градус"
    preMsg[1] = deg;
    //Проверка флага градуировки
    if(degree)
    {
      //Если true - то это градусы Фаренгейта
      //и третий символ в массиве будет содержать
      //сгенерированный символ F(код 71)
      preMsg[2] = F;
    }
    else
    {
      //Иначе, если true - то это градусы Цельсия
      //и третий символ в массиве будет содержать
      //сгенерированный символ С(код 78)
      preMsg[2] = C;
    }
    //Отправляем указатель на проинициализированный символьный массив preMsg[]
    //в качестве первого параметра функции для прокрутки сообщений
    //второй параметр - это размер символьного массива preMsg[]
    //третий параметр - задержка сдвига(прокрутки влево) в миллисекундах  
    msgScrolling(preMsg, sizeof(preMsg), delayScrolling);
    //После прокрутки сообщения покинуть тело функции
    return;
  }
  //Если больше 0.5 но меньше 1
  if(T < 1)
  {
    byte preMsg[4];
    preMsg[0] = pDigit[0];
    preMsg[1] = Digit[int(float(T) * 10)];
    preMsg[2] = deg;
    if(degree)
    {
      preMsg[3] = F;
    }
    else
    {
      preMsg[3] = C;
    }
    //Здесь нужно знать показания температуры
    //Отрицательные или положительные,
    //если отрицательные - добавить отрисовку знака минус
    if(value < 0)
    {
      byte rdyMsg[sizeof(preMsg) + 1];
      //В первый байт массива символов сообщения 
      //записываем знак
      rdyMsg[0] = S;
      //Далее - слияние массивов в готовое сообщение
      for(int i = 1; i < sizeof(rdyMsg); i ++)
      {
        //
        rdyMsg[i] = preMsg[i - 1];
      }
      msgScrolling(rdyMsg, sizeof(rdyMsg), delayScrolling);
      return;
    }
    else
    {
      msgScrolling(preMsg, sizeof(preMsg), delayScrolling);
      return;
    }
  }
  //Если больше 1 но меньше 10
  if(T < 10)
  {
    byte preMsg[4];
    preMsg[0] = pDigit[int(float(T))];
    preMsg[1] = Digit[int((float(T) - int(T)) * 10)];
    preMsg[2] = deg;
    if(degree)
    {
      preMsg[3] = F;
    }
    else
    {
      preMsg[3] = C;
    }
    if(value < 0)
    {
      byte rdyMsg[sizeof(preMsg) + 1];
      //В первый байт массива символов сообщения 
      //записываем знак
      rdyMsg[0] = S;
      //Далее - слияние массивов в готовое сообщение
      for(int i = 1; i < sizeof(rdyMsg); i ++)
      {
        //
        rdyMsg[i] = preMsg[i - 1];
      }
      msgScrolling(rdyMsg, sizeof(rdyMsg), delayScrolling);
      return;
    }
    else
    {
      msgScrolling(preMsg, sizeof(preMsg), delayScrolling);
      return;
    }
  }
  //Если больше 10 но меньше 100
  if(T < 100)
  {
    byte preMsg[5];
    preMsg[0] = Digit[int(float(T)) / 10];
    preMsg[1] = pDigit[int(float(T)) % 10];
    preMsg[2] = Digit[int((float(T) - int(float(T))) * 10)];
    preMsg[3] = deg;
    if(degree)
    {
      preMsg[4] = F;
    }
    else
    {
      preMsg[4] = C;
    }
    if(value < 0)
    {
      byte rdyMsg[sizeof(preMsg) + 1];
      //В первый байт массива символов сообщения 
      //записываем знак
      rdyMsg[0] = S;
      //Далее - слияние массивов в готовое сообщение
      for(int i = 1; i < sizeof(rdyMsg); i ++)
      {
        //
        rdyMsg[i] = preMsg[i - 1];
      }
      msgScrolling(rdyMsg, sizeof(rdyMsg), delayScrolling);
      return;
    }
    else
    {
      msgScrolling(preMsg, sizeof(preMsg), delayScrolling);
      return;
    }
  }
  //Если больше 100 но меньше 1000
  if(T < 1000)
  {
    byte preMsg[6];
    preMsg[0] = Digit[int(float(T)) / 100];
    preMsg[1] = Digit[int((float(T)) / 10) % 10];
    preMsg[2] = pDigit[int(float(T)) % 10];
    preMsg[3] = Digit[int((float(T) - int(float(T))) * 10)];
    preMsg[4] = deg;
    if(degree)
    {
      preMsg[5] = F;
    }
    else
    {
      preMsg[5] = C;
    }
    if(value < 0)
    {
      byte rdyMsg[sizeof(preMsg) + 1];
      //В первый байт массива символов сообщения 
      //записываем знак
      rdyMsg[0] = S;
      //Далее - слияние массивов в готовое сообщение
      for(int i = 1; i < sizeof(rdyMsg); i ++)
      {
        //
        rdyMsg[i] = preMsg[i - 1];
      }
      msgScrolling(rdyMsg, sizeof(rdyMsg), delayScrolling);
      return;
    }
    else
    {
      msgScrolling(preMsg, sizeof(preMsg), delayScrolling);
      return;
    }
  }
}

        Еще хотелось бы сказать, что в стандартной библиотеке LedControl не было найдено функций прокрутки, может быть мы будем теми кто дополнит эту библиотеку? Что же, всё зависит от пожеланий наших посетителей, комментируйте, пробуйте, задавайте вопросы... Ждем ваших отзывов. Видео работы скетча также прилагается к этой статье, смотрим результаты работы скетча:





Расскажи о нас

Сообщение

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