Также приведу информацию по настройке BT-модулей для работы друг с другом.
Итак, поехали!
С чего начать?
Начнем с того, что для того чтобы начать свои первые поделки на платформе 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-модулей будут описаны ниже.
Оба модуля встроены в шилд, то есть проблем с подключением к плате Arduino возникнуть не должно.
Начинаем сборку
Подключаем фоторезистор
Сопротивление фоторезистора зависит от света, попадающего на него. Используя фоторезистор в связке с обычным резистором, мы получаем делитель напряжения, в котором напряжение проходящее через фоторезистор, изменяется, в зависимости от уровня освещенности.
Механизм получения полезной информации от датчика очень прост: функция analogRead(pin_number) вернет значение, которое будет представлять степень освещенности. Чувствительностью датчика можно управлять играясь с резисторами разных номиналов; на мой взгляд, 10кОм — оптимальный номинал.
int lightPin= 0; //номер входа, к которому подключен фоторезистор
void setup()
{
}
void loop()
{
int light = analogRead(lightPin);
}
Датчик давления и температуры
Для измерения температуры, давления, а также высоты над уровнем моря будем использовать барометр BMP085.
Для подключения датчика BMP085 к Arduino нам понадобится 4 контакта:
- Vcc – подключаем к питанию +5в
- SDA – SDA на плате Arduino (A4)
- SCL – SCL на плате Arduino (A5)
- GND – подключаем к земле
Для снятия значений с датчика необходимо подключить билиотеку Adafruit.
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP085_U.h>
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);
}
}
Подключение дисплея
Контакты дисплея LCD1602 подключаются аналогично, как и для BMP085:
- ЖК SDA -> Arduino SDA (A4)
- ЖК SCL -> Arduino SCL (А5)
- ЖК GND -> Arduino GND
- ЖК VCC -> Arduino 5V
/* В примере показано использование LCD экрана 1602. */
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
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
Подключение Bluetooth
А теперь самое интересное. «Насаживаем» наши шилды с bt-модулем на нашу Arduino:
Master-устройство будет подключаться к Slave-устройству, которое будет ждать входящего подключения. На одной из плат устанавливаем переключатель в H, это и будет наш master. На другой плате — в L, это будет slave.
Прикрепив модули к 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
Этап первый. Конфигурация.
- Посылаем AT+ORGL, тем самым возвращая модуль к его изначальной конфигурации
- Имя модуля можно изменить, послав AT+NAME=myname.
- AT+RMAAD — удаляем информацию о предыдущих «спариваниях».
- AT+PSWD=1234 — устанавливаем пароль
- AT+ROLE=1 — говорим устройству, что оно будет работать в master режиме.
- AT+CMODE=1 — говорим устройству, что оно будет подключаться к любым адресам.
Этап второй. Подключение.
- Отправляем команду AT, чтобы удостовериться, что модуль подключен и готов к работе.
- AT+INIT — инициализация. Если в ответ получаем ERROR(17) — ничего страшного, значит команда инициализации уже посылалась, продолжаем работу.
- AT+INQ — начинаем поиск доступных BT-устройств, ответ будет содержать список из адресов
- AT+LINK=<адрес> — тут происходит непосредственно подключение к slave-устройству. Команда на подключение может, например, выглядеть так: AT+LINK=14,2,110007.
После выполнения последней команды диоды начнут мигать с меньшей частотой, что говорит об успешном подключении.
Финишная прямая
На этом почти всё. Остается написать скетчи, в которых мы считываем-отправляем-принимаем-отображаем погодные данные. При желании можно отказаться от макетной платы и приступить к пайке, а затем поместить конструкции в корпуса.
Скетчи:
#include <SoftwareSerial.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
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 <SoftwareSerial.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP085_U.h>
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();
}
На этом всё. Спасибо за внимание!
Комментарии (11)
Hellsy22
30.12.2015 16:40+1Я бы не советовал использовать BMP085 для измерения температуры — у него точность ±2?С, а для комнатной температуры разница между 21 и 25 градусами очень существенна. Отдельно позабавила установка сенсора в закрытом корпусе рядом с тепловыделяющими элементами — вы его еще прямо на регулятор напряжения приклейте — такого намеряете, что — ух!
В общем, добавьте к схеме хотя бы DHT22, который вы выносился наружу на гибкой трубке-антенне или чем-то таком. А давление лучше показывать в миллиметрах ртутного столба.JoOice
30.12.2015 17:11Спасибо за совет.
На самом деле, изначально использовал DHT22, однако перед самым «дедлайном» он вышел из строя и поэтому было принято решение использовать bmp для считывания температуры.
AlNinyo
30.12.2015 20:55Хм. Добавлю 5 копеек. Когда-то тоже пробовал делать метеостанцию подобную. Только я не стал заморачиваться с BT, а тупо вынес уличный датчик (BMP180) на балкон с помощью проводов. Да, метеостанция получилась стационарной, но вполне себе хорошей.
Мне больше понравился такой расклад по датчикам: дома DHT11/22 (температура + влажность, у меня только 11 были, к сожалению), на улице BMP180 (температура + атм. давление), плюс там, где надо только температуру мерить, можно использовать DS18B20. А если заморочиться, и часть этих датчиков подключить к ESP8266 какому-нибудь, да организовать передачу данных от них по wi-fi… К сожалению, у меня этого пока не получилось из-за глюков модулей и кривых рук.
Про давление в мм рт. ст. вам совершенно правильно выше написали, кстати. Если что, Па надо поделить на 133,3 и получатся мм рт. ст. (простите, если что, но мало ли вы не в курсе были).Hellsy22
30.12.2015 21:01Могу выложить кое-как нарисованную рабочую схему для ESP, если хотите.
Изначально я планировал написать четыре статьи, раскрывающие вопрос от и до, но почти три месяца возился с отладкой, пока не добился стабильной работы, а потом уже и статьи писать расхотелось.
mlu
31.12.2015 01:44Вместо BMP180 + DHT можно попробовать BME280. Там температура, давление и влажность сразу в одном сенсоре.
Alexeyslav
31.12.2015 12:52Ни паскали, ни милиметры ртутного столба не очень наглядны. Есть более наглядная форма представления атмосферного давления — милибары. Суть — нормальное атмосферное давление это ровно 1000 милибар. Один взгляд — и видно больше нормы или меньше.
Да, если уж на то пошло то для барометра нужна еще компенсация барометрической высоты, атмосферное давление-то измеряется относительно уровня моря. Да и коррекция показаний тоже не помешала бы, точность у этих датчиков не очень хоть и разрешения хватает чтобы отличать высоты в 30см.AlNinyo
31.12.2015 13:07+1На вкус и цвет :) Лично мне, как школьному учителю физики, абсолютно по барабану, в чём будет давление показываться. Но большинству нормальных людей, имхо, мм рт. ст. всё же привычнее. Не забывайте, что куча домашних барометров основной шкалой именно мм имеет. А вообще, можно сделать совсем просто — в зависимости от показаний выводить либо картинку, либо просто текст из серии «пасмурно/ясно/чёрт_знает_что».
alexpp
Как обстоят дела у устройства с проиводействием осадкам и низким температурам? Т.е. работает ли оно на улице?
Если выбрали Ардуино — то почему не взяли Nano и модуль BT без огромной макетки? На модуле нужны то всего 4 контакта…
IronHead
Это же БГУИР )
JoOice
На улице работает, тестировал в условиях 0-5 градусов + мокрый снег.
Uno выбрал по той причине, что большинство гайдов по ардуино приводятся именно по Uno модели, BT с шилдом был выбран также из-за легкости подсоединения, так как ранее я не имел дела с таковым.
После полученного опыта уже буду смотреть в сторону более компактных компонентов.