Домашняя метеостанция на GY-BMP280-3.3 и Ds18b20

Домашняя метеостанция на GY-BMP280-3.3 и Ds18b20 Анемометр

Домашняя метеостанция на gy-bmp280-3.3 и ds18b20

Я хочу собрать своими руками прибор, который будет измерять атмосферное давление и температуру. Датчик температуры должен быть выносным и герметичным, так как должен измерять температуру на некотором расстоянии от прибора. Хотелось бы иметь такой переносной прибор с рабочим диапазоном от -30°С до 50°С. Но для это надо, чтобы все компоненты были способны работать в этом диапазоне температур. Компоненты, способные работать в расширенном температурном диапазоне, стоят дороже, да и купить их сложнее.

Исполнить мою мечту в реальность мне поможет плата

GY-BMP280-3.3

, о которой я рассказал в статье «

Плата GY-BMP280-3.3 для измерения барометрического давления и температуры

».

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

Приступим.

Первый этап. Устанавливаем на компьютер бесплатную программную оболочку Arduino IDE для написания программ (скетчей), их компиляции и последующей записи в микроконтроллер Mega328P, установленный на плату Arduino Uno. Рекомендую вам скачать оболочку Arduino IDE версию ARDUINO 1.6.5. Почему? Изначально проект ARDUINO был один, теперь разработчики разошлись и продолжают развивать систему ARDUINO, но каждый по-своему, с небольшими нюансами. Я использовал версию ARDUINO 1.6.5. Она должна быть установлена и проверена на совместную работу с платой Arduino Uno на простейших примерах.

Второй этап. Проверяем плату GY-BMP280-3.3 для измерения барометрического давления и температуры. Берем 4 проводка, соединяем ими GY-BMP280-3.3 и Arduino Uno, как изображено на фотографии и схеме. Кривые тонкие разноцветные линии – это проводники.

Приступим к проверке платы GY-BMP280-3.3. Для этого нужно установить библиотеку в Arduino IDE, написанную программистами, работающими на сайте ianemometers.ru. Как правило, после установки библиотеки в Arduino IDE, появляются примеры (образцы) кода. Незначительно изменив образец кода, мы сможем компилировать его в данные, понятные микроконтроллеру, а потом отправить их в память микроконтроллера. Найти пример (образец) вы сможете, обратив внимание на две фотографии экрана, расположенные ниже.

После записи данных в микроконтроллер платы Arduino Uno он сразу начинает выполнять программу (код) и отсылать данные по USB кабелю на компьютер, к которому подключена плата Arduino Uno. А результат измерений платы GY-BMP280-3.3 мы сможем увидеть в окне Arduino IDE, называемом «монитором последовательного порта».

Результат измерений платы GY-BMP280-3.3 мы сможем увидеть в стандартной программе Windows Hyper Terminal, предварительно закрыв оболочку Arduino Uno и настроив сеанс в программе Hyper Terminal. То есть мы можем получать результаты работы платы GY-BMP280-3.3, подключив Arduino Uno к любому компьютеру USB кабелем, на котором установлен драйвер для платы Arduino Uno. Библиотек для работы с GY-BMP280-3.3 несколько. У меня все заработало с библиотекой с

сайта

. Файл, который вы скачаете с этого сайта, будет иметь такой вид: bd7e4a37c1f4dba2ebde9b9cd49f45ce.zip. Его нужно переименовать в вид: iarduino_Pressure_BMP.zip. Теперь нам надо установить библиотеку iarduino_Pressure_BMP в оболочку Arduino IDE.

Запускаем Arduino IDE, заходим в меню Эскиз/Include Librari/Add.ZIP Library… далее выбираем файл iarduino_Pressure_BMP.zip и нажимаем кнопку Open. Так же надо установить библиотеки:

iarduino.ru/file/198.html

,

iarduino.ru/file/49.html

. После установки библиотек перегружаем оболочку Arduino IDE, то есть закрываем её и запускаем заново. Затем выбираем в меню Файл/Образцы/iarduino Pressure BMP (датчики давления)/example.

Видим в окне код.

Код надо будет немного изменить.

В пятой строке удалить два слеша «//» и в одиннадцатой строке добавить (0x76) или (0x77). (0x76) – это адрес платы барометра. У моей платы GY-BMP280-3.3, подключенной к шине I2C, адрес оказался равным (0x76). Как узнать номер устройства, подключенного к шине I2C? Ответ на этот вопрос вы получите, прочитав статью полностью.

Итак, мы исправили код в окне, теперь запускаем проверку и компиляцию кода в меню Эскиз/ Проверить/Скомпилировать. Если проверка и компиляция кода пройдет успешно, то в меню Эскиз/Вгрузить запускаем запись программы в Arduino Uno.

Если загрузка пройдет удачно, то, открыв монитор последовательного порта в меню: Инструменты/ Монитор последовательного порта, мы увидим данные, отправляемые платой GY-BMP280-3.3.

На следующем снимке экрана результат работы платы GY-BMP280-3.3 на компьютере, на котором не установлена оболочка Arduino IDE. Данные получает программа PuTTY.

В это же время был сфотографирован лабораторный барометр-анероид, который находился рядом с платой GY-BMP280-3.3. Сравнив показания приборов, вы сами можете сделать выводы о точности работы платы GY-BMP280-3.3. Барометр-анероид аттестован государственной лабораторией.

Третий этап

. Проверка LCD дисплея с интерфейсным модулем I2C. Находим LDC дисплей с интерфейсным модулем, который подключается по шине I2C к Arduino UNO.

Проверяем его работу на примерах из оболочки Arduino IDE. Но перед этим определяем адрес интерфейсного модуля. У моего интерфейсного модуля адрес – 0x3F. Этот адрес я вставил в строчку скетча: LiquidCrystal_I2C lcd(0x3F,16,2);

Я определил этот адрес с помощью скетча «сканера адреса устройств I2C», описанного в

статье

.

Я запустил оболочку Arduino IDE, из статьи скопировал программный код и вставил его окно Arduino IDE.

Запустил компиляцию, потом записал код в плату Arduino UNO, к которой были подключены плата GY-BMP280-3.3 и LDC дисплей с интерфейсным модулем I2C. Затем в мониторе последовательного порта получил следующий результат. У моего интерфейсного модуля адрес – 0x3F.

Четвертый этап

. Проверка датчика температуры DS18b20. Подключаем его по следующей схеме.

Библиотека OneWire Arduino Library для работы с датчиком температуры DS18b20 у нас уже установлена.

Открываем образец DS18x20_Temperature, компилируем, загружаем, смотрим результат измерения в мониторе последовательного порта. Если все работает, приступаем к следующему этапу.

Пятый этап. Сборка домашней метеостанции на GY-BMP280-3.3 и Ds18b20.
Собираем устройство по схеме:

Код для устройства я получил, объединив все примеры в одно и настроив вывод на экран дисплея LDC. Вот что у меня получилось:

//  Раскомментируйте для программной реализации шины I2C:   //
//  #define pin_SW_SDA 3  //Назначение любого вывода Arduino для работы в качестве линии SDA программной шины I2C.
//  #define pin_SW_SCL 9  // Назначение любого вывода Arduino для работы в качестве линии SCL программной шины I2C.
//  Раскомментируйте для совместимости с большинством плат: //
#include 
#include     // Библиотека iarduino будет использовать методы и функции библиотеки Wire.
#include  //  Библиотека для работы LDC типа 1602 по шине I2C
//  Ссылки для ознакомления:                                //
//  Подробная информация о подключении модуля к шине I2C:   // http://wiki.iarduino.ru/page/i2c_connection/
//  Подробная информация о функциях и методах библиотеки:  // http://wiki.iarduino.ru/page/trema-modul-pressure-meter
                                                            //
#include   // Подключаем библиотеку iarduino_Pressure_BMP для работы с BMP180 или BMP280.
iarduino_Pressure_BMP sensor(0x76); // Объявляем объект sensor для работы с датчиком давления, используя функции и методы библиотеки iarduino_Pressure_BMP.
LiquidCrystal_I2C lcd(0x3F,16,2);
OneWire  ds(10);
void setup(){  
    lcd.init();
    lcd.backlight(); 
    Serial.begin(9600);    // Инициируем передачу данных в монитор последовательного порта на скорости 9600 бод.
    delay(1000);             // Ждём завершения переходных процессов при подаче питания
    sensor.begin(73);   // Инициируем работу с датчиком. Текущая высота будет принята за 73 м.- высота города Бузулука над уровнем моря 
}             //
void loop (){
//  Считываем данные и выводим: температуру в °С, давление в мм. рт. ст., изменение высоты относительно указанной в функции begin(по умолчанию 0 метров).
lcd.setCursor(0,0); // определяем точку вывода "P = " на LDC
lcd.print("P = ");
lcd.print(sensor.pressure/1000,3); // делим значение Р выданное BMP280 на 1000 и задаём вывод 3 знаков после запятой
lcd.setCursor(12,0); // определяем точку вывода "kPa" на LDC
lcd.print("kPa");
lcd.setCursor(0,1);
lcd.print("T=");
lcd.print(sensor.temperature,1); // задаём вывод 1 знака после запятой
lcd.setCursor(6,1);
// lcd.print("C");
// lcd.setCursor(9,1);
// lcd.print("H= ");
// lcd.print(sensor.altitude,1);
    if(sensor.read(1))       {Serial.println((String)"CEHCOP BMP"   sensor.type   ": t P = "   sensor.pressure   "tMM.PT.CT, t T = "   sensor.temperature   " *C, tt B = " sensor.altitude " M.");}
    else                     {Serial.println("HET OTBETA OT CEHCOPA");}
//  Считываем данные и выводим: температуру в °С и давление в Па, давление в мм. рт. ст., изменение высоты относительно указанной в функции begin(по умолчанию 0 метров).
    if(sensor.read(2))       {Serial.println((String)"CEHCOP BMP"   sensor.type   ": t P = "   sensor.pressure   "tPa, tt T = "       sensor.temperature   " *C, tt B = " sensor.altitude " M.");}
    else                     {Serial.println("HET OTBETA OT CEHCOPA");}
    byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;
  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }
  Serial.print("ROM =");
  for( i = 0; i < 8; i  ) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }
  if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return;
  }
  Serial.println();
  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS18S20");  // or old DS1820
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    default:
      Serial.println("Device is not a DS18x20 family device.");
      return;
  } 
  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);        // start conversion, with parasite power on at the end
  
  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it. 
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad
  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i  ) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();
  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // "count remain" gives full 12 bit resolution
      raw = (raw & 0xFFF0)   12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    // at lower res, the low bits are undefined, so let's zero them
    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
    //// default is 12 bit resolution, 750 ms conversion time
  }
  celsius = (float)raw / 16.0;
  fahrenheit = celsius * 1.8   32.0;
  Serial.print("  Temperature = ");
  Serial.print(celsius);
  Serial.print(" Celsius, ");
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");
lcd.setCursor(8,1); // определяем точку вывода "Tds=" на LDC
lcd.print("Tds="); 
lcd.print(celsius,1);
    delay(3000);
}

Вот что у меня получилось:

Плата GY-BMP280-3.3 выдаёт давление в паскалях, что не очень удобно. Решить задачу, как заставить плату GY-BMP280-3.3 выдавать данные по давлению в килопаскалях, я не смог. Я решил эту задачу в строке вывода на дисплей LDC.

lcd.print(sensor.pressure/1000,3); // делим значение Р выданное BMP280 на 1000 и задаём вывод 3 знаков после запятой
Плата GY-BMP280-3.3 выдаёт также значения высоты над уровнем моря.

sensor.begin(73); // Инициируем работу с датчиком. Текущая высота будет принята за 73 м.- высота города Бузулука над уровнем моря
Если вы будете отдыхать на море и измените «sensor.begin(73);» на «sensor.begin(0);» в коде, а затем откомпилируете и запищите программу в домашнюю метеостанцию на GY-BMP280-3.3 и Ds18b20, и сделаете вывод высоты на LDC дисплей, то вы получите еще и высотомер.
// lcd.setCursor(9,1);
// lcd.print(“H= “);
// lcd.print(sensor.altitude,1); // Выводим значения высоты в метрах с одним знаком после запятой
Питание на схему подаётся в моём варианте по кабелю USB. Вы можете применить низковольтный повышающий импульсный преобразователь 5В/600 мА и ваша метеостанция станет портативной. Такого типа источник питания хорошо описан в статье.

Удачных компиляций!

Метеостанция

Для сборки метеостанции использовали фоторезистор и датчик

(температура и влажность). Схема подключения:

Код на C
/* Meteo.ino
 *
 * Программа, регистрирующая влажность, температуру и яркость
 * Отправляет результаты на COM port
 * Формат вывода: H=1.0;T=1.0;LL=1;
 */

//Пин фоторезистора (аналоговый)
int lightPin = 0;

// Пин DHT-11 (цифровой)
int DHpin = 8; 

// Массив, хранящий данные DHT-11
byte dat[5]; 

// Первоначальная настройка
void setup()
{
	Serial.begin(9600); 
	pinMode(DHpin,OUTPUT); 
}

 /*
 * Выполняется после setup()
 * Основной бесконечный цикл
 */
void loop()
{
	delay(1000); // Замер примерно 1 раз в секунду
	int lightLevel = analogRead(lightPin); //Получаем уровень освещённости 

	temp_hum(); // Получаем температуру и влажность в переменную dat
	// И выводим результат
	Serial.print("H="); 
	Serial.print(dat[0], DEC);   
	Serial.print('.'); 
	Serial.print(dat[1],DEC);	
	Serial.print(";T="); 
	Serial.print(dat[2], DEC);	
	Serial.print('.'); 
	Serial.print(dat[3],DEC);	 
	Serial.print(";LL="); 
	Serial.print(lightLevel);
	Serial.println(";");
}

// Получить данные от DHT-11 в dat
void temp_hum() 
{ 
	digitalWrite(DHpin,LOW);
	delay(30);  
	digitalWrite(DHpin,HIGH); 
	delayMicroseconds(40);
	pinMode(DHpin,INPUT);
	while(digitalRead(DHpin) == HIGH);
	delayMicroseconds(80);
	if(digitalRead(DHpin) == LOW); 
	delayMicroseconds(80);
	for(int i=0;i<4;i  )
	{
	  dat[i] = read_data();
	}
	pinMode(DHpin,OUTPUT);
	digitalWrite(DHpin,HIGH);
} 

// Получить часть данных от DHT-11
byte read_data() 
{
	byte data; 
	for(int i=0; i<8; i  ) 
	{ 
		if(digitalRead(DHpin) == LOW) 
		{ 
			while(digitalRead(DHpin) == LOW); 
			delayMicroseconds(30);
			if(digitalRead(DHpin) == HIGH) 
			{
				data |= (1<<(7-i));
			}
			while(digitalRead(DHpin) == HIGH); 
		}
	} 
	return data; 
} 


После загрузки кода на Arduino она начинает посылать данные на COM порт в следующем формате:

H=34.0;T=24.0;LL=605;

Где:

Надо как-то его хранить в Caché. Для этого напишем хранимый класс Arduino.Info:

И добавим туда метод, который будет принимать данные в формате Arduino, и преобразовывать их объекты класса Arduino.Info:

/// Получаем поток данных в формате H=34.0;T=24.0;LL=605;n 
/// И преобразуем их в объекты класса Arduino.Info
ClassMethod ReceiveSerial(port = {..#SerialPort})
{
	try {
		open port:(:::" 0801n0":/BAUD=9600)
		set old = $IO
		use port
		for {
			read x //Читаем одну строку
			set Humidity = $Piece($Piece(x,";",1),"=",2)
			set Temperature =  $Piece($Piece(x,";",2),"=",2)
			set Brightness =  $Piece($Piece(x,";",3),"=",2)
			if (x '= "") {
				do ..AddNew(Temperature,Humidity,Brightness) // Добавляем данные
			}
		}
	} catch anyError {
		close port
	}
}


После этого нам нужно запустить Arduino и выполнить в терминале метод ReceiveSerial:

write ##class(Arduino.Info).ReceiveSerial()

Этот метод в бесконечном цикле будет собирать и сохранять данные, приходящие от Arduino.

Метеостанция (arduino pro mini, bme280, lcd1602)

Введение

Что можно вывести на двухстрочный экран, кроме «Hello world!»? Почему бы не отображать температуру влажность и давление?

Датчики предлагаемые как учебное пособие к arduino (DHT11, DHT22) показывают температуру и влажность воздуха. В учебных целях (для университета) понадобилось наблюдать так же и за давлением. Естественно на кафедре есть барометр, но почему бы не собрать свой? К тому же можно в дальнейшем накапливать показания в автоматическом режиме, и это неплохой опыт в изучении arduino.

Так или иначе из Китая были заказаны комплектующие и собрано данное устройство.

Необходимые комплектующие

Arduino Pro Mini
I2C для LCD (можно было заказать сразу в сборе, но так вышло чуть чуть дешевле)
LCD 1602
BME280

Для отправки скетча в arduino был использован USB-UART. Так же можно было использовать Raspberry Pi или компьютер с COM портом.

Схема подключения для прошивки и код программы

Из Китая USB-UART пришёл с набором проводков:

image

Их вполне хватило. Перемычку оставил на 3.3 вольта, несмотря на то что моя версия arduino питается от 5 вольт.

UART — Arduino
5v — VCC
TXD — RXD
RXD — TXD
GND — GND
CTS — DTR (опционально, у меня не работал, возможно потому что напряжение сигналов осталось 3.3В)

Если не подключать DTR, то после отправки прошивки arduino нужно перезагрузить встроенной кнопкой, начнётся активный обмен данными в обе стороны (о чём свидетельствуют светодиоды на USB-UART), после успешной загрузки прошивки, она сама перезагрузится.

Необходимые сторонние библиотеки:

SparkFunBME280
LiquidCrystal I2C

Непосредственно код, с комментариями из примеров (на случай, если кому то понадобится что то менять).

Код
#include <stdint.h>
#include "SparkFunBME280.h"
#include "Wire.h"
#include "SPI.h"
#include <LiquidCrystal_I2C.h>

//Global sensor object
BME280 mySensor;
LiquidCrystal_I2C lcd(0x3F,16,2); //Адрес дисплея, в моём случае 0x3F

void setup()
{
  lcd.init();
  lcd.backlight();

	//***Driver settings********************************//
	//commInterface can be I2C_MODE or SPI_MODE
	//specify chipSelectPin using arduino pin names
	//specify I2C address.  Can be 0x77(default) or 0x76
	//For I2C, enable the following and disable the SPI section
  
  mySensor.settings.commInterface = I2C_MODE;
  mySensor.settings.I2CAddress = 0x76; //Адрес датчика, в моём случае не стандартный
	
	//For SPI enable the following and dissable the I2C section
	//mySensor.settings.commInterface = SPI_MODE;
	//mySensor.settings.chipSelectPin = 10;

	//***Operation settings*****************************//
	
	//renMode can be:
	//  0, Sleep mode
	//  1 or 2, Forced mode
	//  3, Normal mode
	mySensor.settings.runMode = 3; //В примере предлагают использовать Forced mode, но при обновлении раз в секунду достаточно Normal mode
	
	//tStandby can be:
	//  0, 0.5ms
	//  1, 62.5ms
	//  2, 125ms
	//  3, 250ms
	//  4, 500ms
	//  5, 1000ms
	//  6, 10ms
	//  7, 20ms
	mySensor.settings.tStandby = 5; //Очевидно чаще не нужно
	
	//filter can be off or number of FIR coefficients to use:
	//  0, filter off
	//  1, coefficients = 2
	//  2, coefficients = 4
	//  3, coefficients = 8
	//  4, coefficients = 16
	mySensor.settings.filter = 0;
	
	//tempOverSample can be:
	//  0, skipped
	//  1 through 5, oversampling *1, *2, *4, *8, *16 respectively
	mySensor.settings.tempOverSample = 1;

	//pressOverSample can be:
	//  0, skipped
	//  1 through 5, oversampling *1, *2, *4, *8, *16 respectively
    mySensor.settings.pressOverSample = 1;
	
	//humidOverSample can be:
	//  0, skipped
	//  1 through 5, oversampling *1, *2, *4, *8, *16 respectively
	mySensor.settings.humidOverSample = 1;
  	
	//Calling .begin() causes the settings to be loaded
	mySensor.begin();
}

void loop()
{
  //Буквы можно вывести один раз, а далее менять показания, но показания при изменении количества значащих цифр могут сдвигать строку.
  lcd.setCursor(0,0);
  lcd.print("H=");
  lcd.print((uint8_t)mySensor.readFloatHumidity());
  lcd.print("%");
  lcd.print(" T=");
  lcd.print(mySensor.readTempC());
  lcd.setCursor(13,0);
  lcd.print(" P:");

  lcd.setCursor(0,1);
  int mmH=mySensor.readFloatPressure()/133;
  lcd.print(mmH);
  lcd.print("mmH ");
  lcd.print(mySensor.readFloatPressure());
  lcd.setCursor(14,1);
  lcd.print("Pa");

  delay(1000);

}

Адрес датчика можно угадать, их всего два.

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

В данном случае:

И адрес будет 0x3F т.к. A0 — A2 разомкнуты:

image

Светодиод который обведён в овал лучше можно выпаять.

Схема подключения

Резистор выбирался как половина от сопротивления датчика (между VVC и GND), чтобы падения напряжения на нём было 1.7 вольта. Так же схему можно запитать от входа RAW, другим напряжением (например от кроны).

image

На фотографии видно, что для компактности можно взять питание на датчик и дисплей с другого пина. Так же там видно ответвление оранжево-жёлтой пары проводов, на них висит резистор на 100 Ом, для уменьшения яркости подсветки (можно оставить джампер, но будет резать глаза).

image

В моём случае всё питается от старого компьютерного блока питания. Можно питать от USB. Все комплектующие были приклеены оказавшемся под рукой клеем «Момент».

Итог

На рабочем месте появился 1602 прикрученный к столу, который показывает давление, влажность, температуру. Arduino можно перепрошить не снимая (возможно станет бегущей строкой).

Метеостанция на arduino со сверхнизким энергопотреблением

При проектировании устройств определённый уровень энергопотребления достигают за счёт как аппаратного обеспечения (выбирая компоненты с низким потреблением), так и программного (отключая внутренние периферийные модули микроконтроллера, когда они не нужны). Кроме того, регулируют напряжение питания и частоту, чтобы они соответствовали стандартным требованиям.

Для снижения энергопотребления за счёт ПО мы будем использовать библиотеку платформы Arduino, подключаемую заголовочным файлом low-power.h. Эта библиотека позволяет отключать внутреннюю периферию микроконтроллера, используя время или прерывания.

Также будут использоваться библиотеки для упрощения работы с датчиками BMP180, BH1750 и DHT22 и дисплеем от Nokia 5110.

Приступим к краткому описанию кода. Сначала включаем необходимые библиотеки, упомянутые выше.

Далее мы определяем вывод микросхемы ATmega328P, эквивалентный выводу платы Arduino, к которому подключён датчик DHT. А также уточняем, что будет использоваться именно датчик DHT22; этот идентификатор мы будем передавать в качестве аргумента экземпляру библиотеки датчиков DHT, который создадим позже.

Далее создаём объект класса ЖК‑дисплея (LCD) и передаём ему в качестве аргументов номера выводов микросхемы ATmega328P, эквивалентные выводам платы Arduino, к которым подключён дисплей. Затем создаём объект класса датчика BMP180. После этого создаём пару шрифтов для отображения данных на дисплее.

Следом идёт функция setup(void), в которой мы инициализируем ЖК‑дисплей, датчик интенсивности света и датчик давления.

Функция loop() довольно простая. Сначала мы считываем показание интенсивности света, и если интенсивность превышает 30 люмен (это означает дневное время), то мы собираем показания остальных датчиков и отображаем их на ЖК‑дисплее (LCD). Функция sleepForTwoMinutes() («Заснуть на две минуты») создана с помощью библиотеки низкого энергопотребления. Функция отключает всю аппаратную периферию после получения показаний датчиков, чтобы сохранить заряд батарей.

Полный код для этого проекта доступен для скачивания по ссылке.

Настройки в коде

RESET_CLOCK 0       // сброс часов на время загрузки прошивки (для модуля с несъёмной батарейкой). Не забудь поставить 0 и прошить ещё раз!
SENS_TIME 30000     // время обновления показаний сенсоров на экране, миллисекунд
LED_MODE 0          // тип RGB светодиода: 0 - главный катод, 1 - главный анод

// управление яркостью
BRIGHT_CONTROL 1      // 0/1 - запретить/разрешить управление яркостью (при отключении яркость всегда будет макс.)
BRIGHT_THRESHOLD 350  // величина сигнала, ниже которой яркость переключится на минимум (0-1023)
LED_BRIGHT_MAX 255    // макс яркость светодиода СО2 (0 - 255)
LED_BRIGHT_MIN 10     // мин яркость светодиода СО2 (0 - 255)
LCD_BRIGHT_MAX 255    // макс яркость подсветки дисплея (0 - 255)
LCD_BRIGHT_MIN 10     // мин яркость подсветки дисплея (0 - 255)

BLUE_YELLOW 1       // жёлтый цвет вместо синего (1 да, 0 нет) но из за особенностей подключения жёлтый не такой яркий
DISP_MODE 1         // в правом верхнем углу отображать: 0 - год, 1 - день недели, 2 - секунды
WEEK_LANG 1         // язык дня недели: 0 - английский, 1 - русский (транслит)
DEBUG 0             // вывод на дисплей лог инициализации датчиков при запуске. Для дисплея 1602 не работает! Но дублируется через порт!
PRESSURE 1          // 0 - график давления, 1 - график прогноза дождя (вместо давления). Не забудь поправить пределы гроафика
CO2_SENSOR 1        // включить или выключить поддержку/вывод с датчика СО2 (1 вкл, 0 выкл)
DISPLAY_TYPE 1      // тип дисплея: 1 - 2004 (большой), 0 - 1602 (маленький)
DISPLAY_ADDR 0x27   // адрес платы дисплея: 0x27 или 0x3f. Если дисплей не работает - смени адрес! На самом дисплее адрес не указан

// пределы отображения для графиков
TEMP_MIN 15
TEMP_MAX 35
HUM_MIN 0
HUM_MAX 100
PRESS_MIN -100
PRESS_MAX 100
CO2_MIN 300
CO2_MAX 2000

Облачные сервисы

Изначально у меня не было цели делиться данными со сторонними сервисами, но во время отладки появилась мысль о том, что неплохо было бы иметь референсные данные с местных метеостанций, для контроля корректности работы своей. Первым, и с самой большой коллекцией примеров и API оказалась WeatherUnderground.

from wunderground_pws import WUndergroundAPI, units

from secure_data import wu_api_key, wu_reference_station_id

""" ... """

wu_current = wu.current()

""" ... """

wu_humidity=wu_current['observations'][0]['humidity'],
wu_pressure=int(int(wu_current['observations'][0]['metric_si']['pressure'])/1.33),
wu_dew_point=wu_current['observations'][0]['metric_si']['dewpt'],
wu_wind_speed=wu_current['observations'][0]['metric_si']['windSpeed'],
wu_wind_gust=wu_current['observations'][0]['metric_si']['windGust'],
wu_wind_direction=wu_current['observations'][0]['winddir'],
wu_wind_heading=deg_to_heading(int(wu_current['observations'][0]['winddir']))

Однако, если была возможность быстро получать данные, то почему бы ими не поделиться, подумал я? Данные в WU передаются через GET-запрос, поэтому для удобства предварительно подготавливаем данные

def prepare_wu_format(data, timestamp=None):
    payload = f"&dateutc={timestamp}" if timestamp else "&dateutc=now"
    payload  = "&action=updateraw"
    payload  = "&humidity="   "{0:.2f}".format(data['humidity'])
    payload  = "&tempf="   str(celsius_to_fahrenheit(data['temperature']))
    payload  = "&baromin="   str(mmhg_to_baromin(data['pressure']))
    payload  = "&dewptf="   str(celsius_to_fahrenheit(data['dew_point']))
    payload  = "&heatindex="   str(celsius_to_fahrenheit(heat_index(temp=data['temperature'], hum=data['humidity'])))
    payload  = "&humidex="   str(celsius_to_fahrenheit(humidex(t=data['temperature'], d=data['dew_point'])))
    payload  = "&precip="   str(data['precip'])
    payload  = "&uv"   str(data['uv'])
    return payload

затем отправляем:

Подключение датчика к микроконтроллеру arduino

От измерительного устройства к Arduino поступает цифровой сигнал, передающий сразу обе величины (температуру и влажность).

Передача данных от датчика к микроконтроллеру имеет такую последовательность:

  • От микроконтроллера Arduino к датчику поступает запрос путем смены сигнала с 0 на 1;
  • Получив запрос, DHT11 выдает Arduino информацию посредством изменения битовой кодировки;
  • При согласовании запроса и ответа от DHT11 на Arduino поступает отчет в размере 5 байт о состоянии температуры и влажности.

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

Метеостанция на Arduino

Ниже представлена принципиальная схема самодельной метеостанции на основе датчика DHT11 и микроконтроллера Arduino.

Принципиальная схема метеостанции на Ардуино
Рисунок 3: Принципиальная схема метеостанции на Ардуино

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

Следует отметить, что предложенная выше программа уже включает в себя возможность получения значения с дробной частью. Поэтому если возможности вашего монитора ограничены или вы не хотите загромождать его лишними нулями после запятой при использовании  датчика DHT11, вам придется немного изменить предложенную программу, дополнив ее функцией плавающей точки – dtostrf.

Получаем данные в caché

Теперь подключим клавиатуру (keypad) к Caché и будем передавать данные. Это может быть использовано как, например, дополнительная аутентификация пользователя с помощью

и рутины ZAUTHENTICATE.mac

Код на C
/*  Keypadtest.ino
 *
 *  Пример использования библиотеки Keypad
 *  Подключите Keypad к выводам Arduino указанным в
 *  rowPins[] and colPins[].
 *
 */

// Репозиторий библиотеки:
// https://github.com/Chris--A/Keypad
#include <Keypad.h>

const byte ROWS = 4; // Четыре строки
const byte COLS = 4; // Три столбцы
// Карта соответствия кнопок и символов
char keys[ROWS][COLS] = {
	{'1','2','3','A'},
	{'4','5','6','B'},
	{'7','8','9','C'},
	{'*','0','#','D'}
};
// Подключите разьёмы keypad 1-8 (сверху-вниз) к Arduino разьёмам 11-4. 1->11, 2->10, ... , 8->4 
// Подключите keypad ROW0, ROW1, ROW2 и ROW3 к этим выводам Arduino
byte rowPins[ROWS] = { 7, 6, 5, 4 };
// Подключите keypad COL0, COL1 and COL2 к этим выводам Arduino
byte colPins[COLS] = { 8, 9, 10, 11 }; 

// Инициализация Keypad
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

#define ledpin 13

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

void loop()
{
	char key = kpd.getKey(); // Поолучаем нажатую кнопку
	if(key)
	{
		switch (key)
	{
		case '#':
			Serial.println();
		default:
			Serial.print(key);
	}
	}
}


В Caché напишем метод, подключающийся к com порту и получающий строку данных:

/// Получение одной строки данных (до конца строки)
ClassMethod ReceiveOneLine() As %String
{
	port = "COM1"
	set str=""
	try {
		open port:(:::" 0801n0":/BAUD=9600)
		set old = $io // Запоминаем текущее устройство ввода-вывода
		use port
		read str // Читаем, пока не встретим символ конца строки
		use old
		close port
	} catch ex {
		close port
	}
	return str
}

В терминале выполним:

write ##class(Arduino.Habr).ReceiveOneLine()

И он перейдёт в режим ожидания, пока на keypad не нажмём “#” (по нажатию на который будет передан конец строки), после чего в терминале будет выведена введённая строка.

Итак, это были основы взаимодействия с устройством, теперь перейдём к метеостанции.

Радиодатчики

Там, где не дотянуться Wi-Fi, нужно использовать альтернативные варианты передачи данных. В моём случае – это использование LoRa-модулей (в связке, например, с Arduino Nano.

Таких устройств у меня два – это датчик скорости и направления ветра (компас). Пока не буду останавливаться на этом в текущей статье, если будет интерес – напишу отдельно. Второе устройство – это вольтметр и два амперметра, для контроля работы ветряка, зарядки АКБ и потребления.

И, код, соответственно:

iot/arduino/*_meter/*_meter.ino

// Required includes
#include <SPI.h>
#include <LoRa.h>

// LoRA config
const int LORA_SEND_RETRIES = 5; // сколько раз посылать сообщение
const int LORA_SEND_DELAY = 20;  // задержка между пакетами
const int LORA_POWER = 20;       // мощность передатчика на максимум 
const int LORA_RETRIES = 12;     // сколько раз пытаться инициализировать модуль
const int LORA_DELAY = 500;      // задержка между попыткой инициализации


// Инициализируем модуль
void init_LoRa() 
{
    bool success = false;
    for (int i=0; i < LORA_RETRIES; i  )
    
    {
        if (LoRa.begin(433E6)) // используем 433Мгц
        {
            success = true;
            break;
        }
        delay(LORA_DELAY);
    }
    if (!success)
    {
        #ifdef DEBUG
        Serial.println("LoRa init failed.");
        #endif
        stop(4);
    }
    
    LoRa.setTxPower(LORA_POWER);  // aplify TX power
    #ifdef DEBUG
    Serial.println("LoRa started!");
    #endif  
}
#endif

// Посылаем пакет с данными строкой
void LoRa_send(power_data data)
{
    String packet = DEVICE_ID   ","   String(data.avg_voltage,2)   ",";
    packet  = String(data.avg_current,2)   ","   String(data.avg_power,2)   ","  String(data.avg_consumption,2);
    for (int i=0; i < LORA_SEND_RETRIES; i  )
    {
        LoRa.beginPacket();  // just open packet
        LoRa.print(packet);  // send whole data
        LoRa.endPacket();    // end packet
        delay(LORA_SEND_DELAY);
    }  
}

Достаточно просто, не правда ли?

Скетч

Прошейте контроллер скетчем через Arduino IDE.

weather-station.ino
// библиотека для работы I²C#include <Wire.h>// библиотека для работы с метеосенсором#include <TroykaMeteoSensor.h>// Подключаем библиотеку для работы с дисплеем#include <QuadDisplay2.h>// библиотека для работы с модулями IMU#include <TroykaIMU.h>// библиотека для работы с протоколом 1-Wire#include <OneWire.h>// библиотека для работы с датчиком DS18B20#include <DallasTemperature.h>// библиотека для работы с SPI#include <SPI.h>// библиотека для работы с SD-картами#include <SD.h>
 
// сигнальный пин датчика DS18B20#define ONE_WIRE_BUS 5// даём разумное имя для CS пина microSD-карты#define SD_CS_PIN  8
 
// создаём объект для работы с метеосенсором
TroykaMeteoSensor meteoSensor;// создаём объект класса QuadDisplay, передаём номер пина CS, включаем режим работы с SPI
QuadDisplay qd(10,true);// создаём объект для работы с барометром
Barometer barometer;// создаём объект для работы с библиотекой OneWire
OneWire oneWire(ONE_WIRE_BUS);// создадим объект для работы с библиотекой DallasTemperature
DallasTemperature sensor(&oneWire);
 
// перечисляем имена операций, которые мы будем выводить на дисплейenum{
  SAVE_SD,// имя для операции, которая записывает на SD данные
  IN,// имя для операции, которая выводит на дисплей надпись "In"
  TEMP_IN,// имя для операции, которая выводит на дисплей температуру с метеосенсора
  CEL,// имя для операции, которая выводит на дисплей символ °C
  HUM_IN,// имя для операции, которая выводит на дисплей влажность с метеосенсора
  PPM,// имя для операции, которая выводит на дисплей символ %
  BAR_IN,// имя для операции, которая выводит на дисплей давление с барометра в миллиметрах ртутного столба
  MER,// имя для операции, которая выводит на дисплей надпись "Hg"
  EMPTY,// имя для операции, которая очищает дисплей
  OUT,// имя для операции, которая выводит на дисплей надпись "Out"
  TEMP_OUT    // имя для операции, которая выводит на дисплей температуру с датчика DS18B20};// создаем массив, в котором будем хранить последовательность операцийint chain[]={
  IN,
  TEMP_IN,
  CEL,
  HUM_IN,
  PPM,
  BAR_IN,
  MER,
  EMPTY,
  OUT,
  TEMP_OUT,
  CEL,
  EMPTY,
  SAVE_SD
};
 
// создаем объект класса long для хранения счетчикаunsignedlong respite_Time =0;
 
// создаем объект для регулировки времени показа значений на экранеint slowdown_qd =1000;// создаем объект для хранения номера выполняемой операцииint number_qd =0;
 
// создаем объект для записи данных на SD строкой
String  dataString ="";
 
void setup(){// инициализация дисплея
  qd.begin();// инициализируем метеосенсора
  meteoSensor.begin();// инициализация барометра
  barometer.begin();// инициализируем работу с датчиком DS18B20
  sensor.begin();// устанавливаем разрешение датчика от 9 до 12 бит
  sensor.setResolution(12);// инициализируем карту памяти
  SD.begin(SD_CS_PIN);// собираем верхнюю строчку с наименованием данных
  dataString ="TEMP_IN (ºC)tHUM_IN (%)tBAR_IN (mmHg)tTEMP_OUT (ºC)";// вызываем функцию сохранения данных на SD
  saveSD(dataString);}
 
void loop(){// запускаем бесконечный счетчик. Его содержимое будет обрабатываться с периодом равным slowdown_qdif(millis()- respite_Time > slowdown_qd){// запускаем процесс, который будет выполнять операции согласно последовательности в chainswitch(chain[number_qd]){case IN:
        qd.displayDigits(QD_I, QD_n, QD_NONE, QD_NONE);break;case TEMP_IN:
        showData(meteoSensor.getTemperatureC());break;case CEL:
        qd.displayDigits(QD_NONE, QD_NONE, QD_DEGREE, QD_C);break;case HUM_IN:
        showData(meteoSensor.getHumidity());break;case PPM:
        qd.displayDigits(QD_NONE, QD_NONE, QD_DEGREE, QD_UNDER_DEGREE);break;case BAR_IN:
        qd.displayInt(barometer.readPressureMillimetersHg());break;case MER:
        qd.displayDigits(QD_NONE, QD_NONE, QD_H, QD_9);break;case EMPTY:
        qd.displayClear();break;case OUT:
        qd.displayDigits(QD_O, QD_u, QD_t, QD_NONE);break;case TEMP_OUT:// переменная для хранения температурыfloat temperature;// отправляем запрос на измерение температуры
        sensor.requestTemperatures();// выводим значение с датчика DS18B20 на экран
        qd.displayFloat(sensor.getTempCByIndex(0),1);break;case SAVE_SD:// собираем в строку сначала температура с метеосенсора
        dataString = String(meteoSensor.getTemperatureC()) "t";// потом влажность
        dataString  = String(meteoSensor.getHumidity()) "t";// давление
        dataString  = String(barometer.readPressureMillimetersHg()) "t";// и температура с датчика DS18B20
        dataString  = String(sensor.getTempCByIndex(0)) "t";// вызываем функцию сохранения данных на SD
        saveSD(dataString);break;}
    number_qd  ;// проверяем не превысил ли номер операции количество операцийif(number_qd >sizeof(chain)/sizeof(int)-1)
      number_qd =0;
    respite_Time = millis();}}
 
// функция работы датчика температуры и влажностиvoid showData(float data){// считываем данные с датчикаint stateSensor = meteoSensor.read();switch(stateSensor){// выводим показания на дисплейcase SHT_OK:
      qd.displayFloat(data,1);break;// выводим сообщение "Errd", если ошибка данных или сенсор не подключёнcase SHT_ERROR_DATA:
      qd.displayDigits(QD_E, QD_r, QD_r, QD_d);// выводим сообщение "ErrC", если ошибка контрольной суммыcase SHT_ERROR_CHECKSUM:
      qd.displayDigits(QD_E, QD_r, QD_r, QD_C);break;}}
 
// функция сохранения данных на карту памятиvoid saveSD(String data){// создаем файл для записи данных
  File dataFile = SD.open("datalog.txt", FILE_WRITE);// если файл существует и открылсяif(dataFile){// сохраняем данные
    dataFile.println(data);// закрываем файл
    dataFile.close();}else{// если файл не доступен выводим ошибку на дисплей
    qd.displayDigits(QD_E, QD_r, QD_r, QD_S);}}

Создание базы данных

Начнем с самого начала, а именно с проектирования и создания базы данных.

Базы данных это свой мир и изучать его можно долго, поэтому бегло коснёмся только тех вещей, которые нам непосредственно необходимы.

Все SQL скрипты находятся в каталоге weather-station/server/php-sql/

С чего начинается проектирование БД? С логического и физического представления.

Логическое представление или схема базы данных:

Физическая схема опирается на конкретную СУБД и типы данных. Проще разбирать на конкретном примере. SQL скрипт make_tables.sql раскрывает логическую и физическую схемы.

В каждой таблице должно быть поле типа

id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT

Имя поля может отличаться в разных БД, но смысл один — это уникальный идентификатор, ключ записи. На будущее, если вы видите базу данных в таблицах которой нет подобного счётчика, знайте, эту БД проектировал человек весьма далёкий от программирования, скорее всего гуманитарий.

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

В нашем проекте две таблицы. В таблице arduino_dht хранятся данные от датчика(ов) типа DHT (температура, влажность), в таблице arduino_bmp хранятся данные от датчика(ов) типа BMP (температура, давление). Если вы в будущем захотите иметь, например, датчик газов или детектор движения, то создаёте дополнительные таблицы, не поленитесь.

Если в таблице будут храниться данные от несколько однотипных датчиков, то как же их различить? Для этого в каждой таблице вводится поле

idSensor INTEGER

Фактически это CLIENT_ADDRESS который мы прописывали в файле client/client.ino для каждого экземпляра удаленного датчика-клиента и в server/server.ino для датчика, который подключен непосредственно к серверу — центральному блоку.

В промышленных системах должна быть ещё одна таблица — соответствия idSensor и его словесного, человекочитаемого описания. Например, датчик с idSensor = 2 это «Температура, влажность в квартире» и т.д. Но в нашем проекте не будем усложнять, просто помните, что :

Далее. В таблицах хранятся следующие данные:

  • ipRemote — IP адрес метеостанции (сервера) с которого пришли данные, полезно для отладки и мониторинга,
  • dateCreate — дата время создания записи,
  • millis — полезно для отладки, это время в миллисекундах с момента начала выполнения скетча на Arduino,
  • temperature — температура,
  • humidity — влажность,
  • voltage — напряжение питания,
  • pressure — давление,
  • errors — количество ошибок (не используется). Задумывалось для хранения количества ошибок при передаче и т.п., чтобы можно было удаленно оценить состояние всей системы.

Как видим таблицы arduino_dht и arduino_bmp очень похожи, отличие только в полях pressure и humidity, и возникает желание свалить всё в одну кучу (таблицу). Но делать так не велит первая нормальная форма, множество начинающих программистов пытались её обойти, но ни у одного не получилось, и мы не будем. Это как не замечать закон всемирного тяготения, до поры до времени вполне может получиться.

Таблица arduino_error_log полезна при отладке — это журнал ошибок и прочих системных сообщений.

Создание БД и её пользователя с правами описано в make_db.sql

Софт. выбор компонентов

Выбор железа и софта тесно взаимосвязан как «курица и яйцо». С чего начать, с железа, с софта? Если у вас хорошее железо, но к нему нет драйверов, библиотек и софта (IDE, утилиты для прошивки и т.п.), то оно бесполезно, и наоборот.

Поэтому рассказываю еще раз про выбор между nRF24L01 и ESP8266 для связи удаленных датчиков с центральным блоком.

Дело в том, что ESP8266 это не просто тупой WiFi адаптер, он имеет на борту микроконтроллер по мощности и объему памяти превосходящий Ардуино. По умолчанию ESP8266 имеет прошивку в виде набора AT команд, в этом случае ESP используется как простой модем.

Однако все эти продвинутые прошивки имеют недостатки, которые не позволили (в сумме с железячными вопросами о которых я уже писал) применить ESP8266 в данном проекте:

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

В свою очередь стандартные AT-прошивки так же имеют минусы:

С другой стороны радиомодуль nRF24L01 прост и понятен, для работы с ним есть супер либа RadioHead и никаких проблем с программированием. Библиотека хорошо документирована, что немаловажно.

RadioHead позволяет передавать структуры данных (а не только отдельные числа), что и реализовано в данном проекте. Забегая вперед скажу, RadioHead может надёжно передавать данные, с повторами если не дошло с первого раза. Все эти вещи библиотека берет на себя.

Для энергосбережения использую библиотеку Low Power Library, она проста и содержит только то, что нужно.

Вот кусок кода:

// по умолчанию устанавливается 2.402 GHz (канал 2), 2Mbps, 0dBm
rfdata.init();
// передача данных на сервер (с повторами, если потребуется)
rfdata.sendtoWait((uint8_t*)&dhtData, sizeof(dhtData), SERVER_ADDRESS);
// засыпаем
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);`

Всё!

В случае же применения ESP8266 в заоконном датчике, я был бы вынужден создавать WiFi точку доступа и каким-то образом передавать данные (где прошивки, где софт?). Либо позволить датчику напрямую слать данные на веб-сервер, а центральный блок (который в этом случае перестаёт играть роль «центрального») учить читать данные оттуда, чтобы их отобразить на табло.

Другими словами я пошёл путем большей автономии от WiFi интернета и PHP MySQL сервера. Вы можете начать «клепать» метеостанцию уже сейчас не имея доступа в интернет и/или хостинга для сервера, в этом случае ESP8266 вам не нужен, просто добавите его потом.

Для считывания данных с датчиков типа DHT есть библиотека Adafruit DHT Sensor Library. Работа с ней проста и понятна.

Для датчика давления подходит библиотека Adafruit BMP085 Unified, которая требует наличия библиотеки абстрактного уровня Adafruit Sensor.

В составе всех библиотек есть примеры скетчей.

Вот и всё пожалуй с теоретической частью. «Наши цели ясны, задачи определены. За работу, товарищи!»

Характеристики датчика dht11

Общий вид датчика DHT11
Рис. 1: общий вид датчика DHT11

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

  • Напряжение питания от 3 до 5 В;
  • Потребляет от источника питания ток в 2,5 мА;
  • Способен измерять влажность окружающего пространства в пределах от 20 до 80%;
  • Температурные колебания измеряет в пределах от 0 до 50°С;
  • Погрешность при измерении влажности составляет 5%, а при измерении температуры в пределах 2%;
  • Частота измерений составляет одно измерение в секунду;
  • Габариты датчика составляют 12×15,5*5,5 мм.

Датчик DHT11 имеет пластиковый корпус и оснащается четырьмя контактами, такое количество выводов обеспечивает удобство подсоединения к устройствам обработки данных. В работе самодельной метеостанции все четыре вывода не используются, из них вам понадобится только три VCC, GND, DATA. Запитать датчик вы можете от любого источника с уровнем напряжения на выходе от 3 до 5 В.

В некоторых схемах можно встретить подключение резистора на 5 – 10 кОм к выводу передачи данных от датчика к микроконтроллеру. Следует отметить, что в данной ситуации этого делать не нужно, так как резистор уже входит в состав платы.

Модуль датчика DHT11
Рис. 2: модуль датчика DHT11

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

Про анемометры:  Умный дом, опыт построения, бег по граблям (MajorDomo, Tasmota и Алиса) / Хабр
Оцените статью
Анемометры
Добавить комментарий