[Из песочницы] Bluetooth-метеостанция на Arduino для начинающих

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

Также приведу информацию по настройке BT-модулей для работы друг с другом.

image

Итак, поехали!


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

Подготовка


Для изготовления прототипов устройств понадобятся следующие элементы:

  • Фоторезистор 500 кОм (x1)
  • Барометр BMP085 (x1)
  • BT-модуль HC-05 (x2)
  • LCD дисплей, ЖК экран LCM 1602 i2c (x1)
  • Набор эл. компонентов (макетная плата, резисторы, диоды) (x1)
  • Arduino UNO R3 (x2)


Для прошивки будем использовать родную Arduino IDE.

Хочу обратить внимание на то, что хотя бы один модуль должен быть HC-05 (не 06!), это важно. Дело в том, что HC-06 не поддерживает режим master, т.о. взяв оба модуля 06 нам не удастся настроить их на работу друг с другом. Я решил взять оба модуля HC-05 чтобы заодно избежать проблем с совместимостью, если таковые возникнут.

Нюансы настройки BT-модулей будут описаны ниже.

image

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


Подключаем фоторезистор


Сопротивление фоторезистора зависит от света, попадающего на него. Используя фоторезистор в связке с обычным резистором, мы получаем делитель напряжения, в котором напряжение проходящее через фоторезистор, изменяется, в зависимости от уровня освещенности.

Механизм получения полезной информации от датчика очень прост: функция analogRead (pin_number) вернет значение, которое будет представлять степень освещенности. Чувствительностью датчика можно управлять играясь с резисторами разных номиналов; на мой взгляд, 10кОм — оптимальный номинал.

image

Пример кода
int lightPin= 0; //номер входа, к которому подключен фоторезистор
void setup()
{
}
void loop()
{
  int light = analogRead(lightPin);
}



Датчик давления и температуры


Для измерения температуры, давления, а также высоты над уровнем моря будем использовать барометр BMP085.

image

Для подключения датчика BMP085 к Arduino нам понадобится 4 контакта:

  • Vcc — подключаем к питанию +5в
  • SDA — SDA на плате Arduino (A4)
  • SCL — SCL на плате Arduino (A5)
  • GND — подключаем к земле


image

Для снятия значений с датчика необходимо подключить билиотеку Adafruit.

Пример кода

#include 
#include 
#include 
Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085);
void setup(void)
{
/* Initialise the sensor */
  if(!bmp.begin())
  {
    /* There was a problem detecting the BMP085 ... check your connections */
    Serial.print("Ooops, no BMP085 detected ... Check your wiring or I2C ADDR!");
    while(1);
  }
}
void loop(void)
{
  sensors_event_t event;
  bmp.getEvent(&event);
  if (event.pressure)
  {
    float pressure = event.pressure;
    
    float temperature;
    bmp.getTemperature(&temperature);

    float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA;
    float altitude = bmp.pressureToAltitude(seaLevelPressure, event.pressure); 
  }
}



Arduino со всеми подключенными датчиками
image


Подключение дисплея


Контакты дисплея LCD1602 подключаются аналогично, как и для BMP085:

  • ЖК SDA → Arduino SDA (A4)
  • ЖК SCL → Arduino SCL (А5)
  • ЖК GND → Arduino GND
  • ЖК VCC → Arduino 5V


image

Пример кода
/* В примере показано использование LCD экрана 1602. */  
#include 
#include   
LiquidCrystal_I2C lcd(0x27,16,2); // Параметры: Адрес устройства, размер экрана  
void setup()
{
    lcd.init();
    // Инициализация lcd
    lcd.backlight();
    // Включаем подсветку
    // Курсор находится в начале 1 строки
    lcd.print("Hello");
    // Выводим текст
    lcd.setCursor(0, 1);
    // Устанавливаем курсор в начало 2 строки
    lcd.print("World!");
    // Выводим текст
}  
void loop()
{ }
// Подробнее: http://xrobot.by/modules/lcd_4_4#ixzz3vQXoFKOj



А теперь самое интересное. «Насаживаем» наши шилды с bt-модулем на нашу Arduino:

image

Master-устройство будет подключаться к Slave-устройству, которое будет ждать входящего подключения. На одной из плат устанавливаем переключатель в H, это и будет наш master. На другой плате — в L, это будет slave.

image

Прикрепив модули к Arduino можно начинать настройку. Для настройки master-a нужно будет послать некоторый набор команд в bt-модуль, будем делать это с помощью Serial Monitor (Ctrl+Shift+M). При обмене сообщениями рекомендуется выставить Baud rate → 38400 & «Both NL&CR».

Послав команду «AT» и нажав отправить, мы ожидаем ответ «OK». Если это так — плата подключена правильно, можно продолжать. Если нет — стоит вернуться на пару шагов назад и проверить корректность подключения bluetooth-модуля.

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

AT — просто вернет «OK», значит всё в порядке
AT+NAME? — вернет имя модуля. Мы также можем задать своё имя, послав, например, AT+NAME=WEATHER_MONITOR
AT+ROLE? — одна из ключевых команд, вернет роль устройства, master/slave. Задать значение можно с помощью AT+ROLE=0 — перевести в режим slave, либо AT+ROLE=1 — режим master.
AT+PSWD? — вернёт пин-код, используемый для подключения.
AT+ADDR? — вернёт адрес устройства, например »14:2:110007». Стоит заметить, что при использовании адреса в посылаемых AT-командах двоеточия »:» нужно заменять запятыми »,», т.о.»14:2:110007» → »14,2,110007».

Настройка Slave’a


Тут никаких телодвижений не требуется, поэтому просто подключаем плату к питанию.

Настройка Master’a


Этап первый. Конфигурация.

  1. Посылаем AT+ORGL, тем самым возвращая модуль к его изначальной конфигурации
  2. Имя модуля можно изменить, послав AT+NAME=myname.
  3. AT+RMAAD — удаляем информацию о предыдущих «спариваниях».
  4. AT+PSWD=1234 — устанавливаем пароль
  5. AT+ROLE=1 — говорим устройству, что оно будет работать в master режиме.
  6. AT+CMODE=1 — говорим устройству, что оно будет подключаться к любым адресам.


Этап второй. Подключение.

  1. Отправляем команду AT, чтобы удостовериться, что модуль подключен и готов к работе.
  2. AT+INIT — инициализация. Если в ответ получаем ERROR (17) — ничего страшного, значит команда инициализации уже посылалась, продолжаем работу.
  3. AT+INQ — начинаем поиск доступных BT-устройств, ответ будет содержать список из адресов
  4. AT+LINK=<адрес> — тут происходит непосредственно подключение к slave-устройству. Команда на подключение может, например, выглядеть так: AT+LINK=14,2,110007.


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

Результат работы


Скетчи:

Код метео-монитора
#include 
#include 
#include   

LiquidCrystal_I2C lcd(0x27,16,2); // Параметры: Адрес устройства, размер экрана  

#define rxPin 2
#define txPin 3

SoftwareSerial btSerial(rxPin, txPin);

bool isDisplayingMode = true;
 
void setup()
{
   lcd.init();
   lcd.backlight();
   // define pin modes for tx, rx pins:
   pinMode(rxPin, INPUT);
   pinMode(txPin, OUTPUT);
   btSerial.begin(38400); 
   Serial.begin(38400);
   Serial.println("Serial started");
   
           
   lcd.print("    Waiting");
   lcd.setCursor(0, 1);
   lcd.print(" for connection");
   
   btSerial.println("AT");
   while (!btSerial.available());
   while (btSerial.available() > 0)
        char c = btSerial.read();
   btSerial.println("AT+INIT");
   while (!btSerial.available());
   while (btSerial.available() > 0)
        char c = btSerial.read();
   btSerial.println("AT+INQ");
   while (!btSerial.available());
   while (btSerial.available() > 0)
        char c = btSerial.read();
   btSerial.println("AT+LINK=2014,5,191146");  
   while (!btSerial.available());
   while (btSerial.available() > 0)
        char c = btSerial.read();   
}

void loop()
{   
  int i = 0;
  char someChar[32] = {0};
  // when characters arrive over the serial port...
  bool availible = Serial.available();
  if(availible) {
    do{
        someChar[i++] = Serial.read();
      //As data trickles in from your serial port you are grabbing as much as you can, 
      //but then when it runs out (as it will after a few bytes because the processor 
      //is much faster than a 9600 baud device) you exit loop, which then restarts, 
      //and resets i to zero, and someChar to an empty array.So please be sure to keep this delay 
      delay(3);                  
 
    }while (Serial.available() > 0);

    lcd.clear();
    Serial.println(i);
    btSerial.println(someChar);
    Serial.println(someChar);
  }
 
  lcd.setCursor(0, 0);
  while(btSerial.available()) 
      {
        if (isDisplayingMode)
        {
          lcd.clear();
          isDisplayingMode = false;
        }
        char c = (char)btSerial.read();
        Serial.print(c);          
        if (c != 13 && c != 10)
          lcd.print(c);
        if (c == '\n')
          lcd.setCursor(0, 1);
      }
}



Код метео-сенсора
#include 
#include 
#include 
#include 

Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085);
int lightSensorPin = 0;

#define rxPin 2
#define txPin 3

SoftwareSerial btSerial(rxPin, txPin);

struct SensorData
{
  float Pressure;
  float Temperature;
  float Altitude;
  float Lightness;
  void DisplaySensorData()
{
    Serial.print("Light:       ");
    Serial.print(this->Lightness, 2);
    Serial.println("%");
    Serial.print("Altitude:    "); 
    Serial.print(this->Altitude); 
    Serial.println(" m");
    Serial.print("Temperature: ");
    Serial.print(this->Temperature);
    Serial.println(" C");
    /* Display atmospheric pressue in hPa */
    Serial.print("Pressure:    ");
    Serial.print(this->Pressure);
    Serial.println(" hPa");
    Serial.println("");
}

void DisplaySensorDataInTwoRows()
{
    Serial.print("Temp: "); Serial.print(this->Temperature); Serial.println(" C");
    Serial.print("Pr: "); Serial.print(this->Pressure); Serial.println(" Pa");
    delay(1000);    
    Serial.print("Alt: "); Serial.print(this->Altitude); Serial.println(" m");
    Serial.print("Light: "); Serial.print(this->Lightness); Serial.println(" %");
    delay(1000);
}
void SendDataToRemote()
{
    btSerial.print("Temp: "); btSerial.print(this->Temperature); btSerial.print(" C\n");
    btSerial.print("Pr:   "); btSerial.print(this->Pressure); btSerial.print(" hPa\n");
    delay(5000);    
    btSerial.print("Alt:   "); btSerial.print(this->Altitude); btSerial.print(" m\n");
    btSerial.print("Light: "); btSerial.print(this->Lightness); btSerial.print(" %  \n");
    delay(5000);
}
};



void displaySensorDetails(void)
{
  sensor_t sensor;
  bmp.getSensor(&sensor);
  
  Serial.println("------------------------------------");
  Serial.print  ("Sensor:       "); Serial.println(sensor.name);
  Serial.print  ("Driver Ver:   "); Serial.println(sensor.version);
  Serial.print  ("Unique ID:    "); Serial.println(sensor.sensor_id);
  Serial.print  ("Max Value:    "); Serial.print(sensor.max_value); Serial.println(" hPa");
  Serial.print  ("Min Value:    "); Serial.print(sensor.min_value); Serial.println(" hPa");
  Serial.print  ("Resolution:   "); Serial.print(sensor.resolution); Serial.println(" hPa");  
  Serial.println("------------------------------------");
  Serial.println("");
  delay(500);
}

void setup(void) 
{
   // define pin modes for tx, rx pins:
   pinMode(rxPin, INPUT);
   pinMode(txPin, OUTPUT);
   btSerial.begin(38400);
   
  Serial.begin(9600);
  Serial.println("Pressure Sensor Test"); Serial.println("");
  
   
  /* Initialise the sensor */
  if(!bmp.begin())
  {
    /* There was a problem detecting the BMP085 ... check your connections */
    Serial.print("Ooops, no BMP085 detected ... Check your wiring or I2C ADDR!");
    while(1);
  }
    
  /* Display some basic information on this sensor */
  displaySensorDetails();
}
int counter = 0;
void loop(void) 
{
  /* Get a new sensor event */ 
  sensors_event_t event;
  bmp.getEvent(&event);
  SensorData data;
  /* Display the results (barometric pressure is measure in hPa) */
  if (event.pressure)
  {
    data.Pressure = event.pressure;
    
    float temperature;
    bmp.getTemperature(&temperature);
    data.Temperature = temperature;

    /* Then convert the atmospheric pressure, and SLP to altitude         */
    /* Update this next line with the current SLP for better results      */
    float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA;
    float altitude = bmp.pressureToAltitude(seaLevelPressure, event.pressure);
    data.Altitude = altitude;  
    
    int lightValue = analogRead(lightSensorPin);
    float lightValueInPercent = 1.0 * lightValue / 1024 * 100;
    data.Lightness = lightValueInPercent;
    
    //data.DisplaySensorDataInTwoRows();    
  }
  else
  {
    Serial.println("Sensor error");
  }
  Serial.println("");
  data.SendDataToRemote();
}



На этом всё. Спасибо за внимание!

© Geektimes