Беспроводной датчик температуры, влажности и атмосферного давления на nRF52832

в 15:07, , рубрики: Arduino IDE, BME280, diy или сделай сам, nrf52832, Беспроводные технологии

Приветствую всех читателей Habr! Сегодняшняя статья будет о датчике температуры, влажности и атмосферного давления c длительным скором работы от одной батарейки. Датчик работает на микроконтролере nRF52832 (даташит). Для получения температуры, влажности и атмосферного давления использован сенсорс BME280 — даташит. Датчик работает от батареек CR2430/CR2450/CR2477. Потребление в режиме передачи составляет 8мА, в режиме сна 5мкА. Итак обо всем попорядку.

Беспроводной датчик температуры, влажности и атмосферного давления на nRF52832 - 1


Это Arduino проект, программа написана в Arduino IDE, Для работы сенсора BME280 использована библиотека Adafruit Industries — гитхаб сенсоры | гитхаб BME280. Для работы с платами nRF52832 в Arduino IDE использован проект Sandeep Mistry — гитхаб. Передача данных на контролер Умного Дома осуществляется по протоколу Mysensors — гитхаб плат | протокол.

Плата датчика разрабатывалась в программе Диптрейс. Размеры платы 36.8мм Х 25мм.

Беспроводной датчик температуры, влажности и атмосферного давления на nRF52832 - 2

Перечень используемых компонентов

  • С1 — cap0603 100nF
  • C2 — cap0603 100nF
  • R1 — res0603 332
  • R2 — res0603 85b
  • R3 — res0603 113
  • R4 — res0603 912
  • R5 — res0603 113
  • R6 — res0603 512
  • R7 — res0603 512
  • RGBL1 — led0805 rgb
  • SWD — PPHF 2x3 6p 1.27mm
  • U1 — YJ-16048 nRF52832
  • U2 — BME280
  • CONNECT — тактовая кнопка KLS7-TS5401
  • RESET — тактовая кнопка KLS7-TS5401
  • Держатель батареи KW-BS-2450-2-SMT
  • Переключатель dsc0012

Плата заказывалась через сайт jlcpcb.com — 2$ за 5 штук в любом цвете.

Беспроводной датчик температуры, влажности и атмосферного давления на nRF52832 - 3

Беспроводной датчик температуры, влажности и атмосферного давления на nRF52832 - 4

Ссылка на архив с герберфайлами

Датчик работает по протоколу Mysensors. Добавить любое устройство в сеть Mysensors достаточно просто. Давайте разберемся на примере этого датчика, пояснения по коду самого датчиком BME280 я опушу, там ничего не меняется при работе с сети Mysensors.

#define MY_DEBUG // Вывод дебага
#define MY_RADIO_NRF5_ESB // Выбор типа радиопередатчика(возможные варианты rfm69, rfm95, nrf24l01, nrf51-52)
#define MY_RF24_PA_LEVEL (NRF5_PA_MAX) // выбор уровня радиосигнала
#define MY_DISABLED_SERIAL // отключение сериала
#define MY_PASSIVE_NODE // режим работы ноды(устройство в сети mysensors), PASSIVE означает что отключены все процедуры контроля транспортного уровня, обновление по воздуху, шифрование, безопасность
#define MY_NODE_ID 1  // ручное назначение идентификатора ноды
#define MY_PARENT_NODE_ID 0 // ручное назначение идентификатора гейта или ретранслятора
//#define MY_PARENT_NODE_IS_STATIC // атрибут PARENT_NODE отвечаюший за отключение функционала автоматического перестроения маршрута
//#define MY_TRANSPORT_UPLINK_CHECK_DISABLED // отключение контроля работоспособности транспортного уровня

#include <MySensors.h> // - библиотека MySensors

#define TEMP_CHILD_ID 0 // назначение идентификатора сенсора температуры
#define HUM_CHILD_ID 1 // назначение идентификатора сенсора влажности
#define BARO_CHILD_ID 2 // назначение идентификатора сенсора атмосферного давления
#define CHILD_ID_VOLT 254 // назначение идентификатора сенсора заряда батареи

MyMessage voltMsg(CHILD_ID_VOLT, V_VOLTAGE);  // инициализация сообщения для сенсора заряда батареи
MyMessage temperatureMsg(TEMP_CHILD_ID, V_TEMP);  // инициализация сообщения для сенсора температуры
MyMessage humidityMsg(HUM_CHILD_ID, V_HUM);  // инициализация сообщения для сенсора влажности
MyMessage pressureMsg(BARO_CHILD_ID, V_PRESSURE);  // инициализация сообщения для атмосферного давления

Презентация датчиков и сенсоров в контролер умного дома:

  sendSketchInfo("BME280 Sensor", "1.0");  // Название датчика, версия ПО
  present(CHILD_ID_VOLT, S_MULTIMETER, "Battery");  // Презентация сенсора заряда батареи, тип сенсора, описание
  present(TEMP_CHILD_ID, S_TEMP, "TEMPERATURE [C or F]");  // Презентация сенсора температуры, тип сенсора, описание
  present(HUM_CHILD_ID, S_HUM, "HUMIDITY [%]");  // Презентация сенсора влажности, тип сенсора, описание
  present(BARO_CHILD_ID, S_BARO, "PRESSURE [hPa or mmHg]");  // Презентация сенсора атмосферного давления, тип сенсора, описание

Передача данных на контролер умного дома:

send(voltMsg.set(batteryVoltage));  // отправка данных о заряде батарейки в mW
sendBatteryLevel(currentBatteryPercent);   // отправка данных о заряде батарейки в %
send(temperatureMsg.set(temperature, 1));  // отправка данных о температуре
send(humidityMsg.set(humidity, 0));  // отправка данных о влажности
send(pressureMsg.set(pressure, 0));  // отправка данных о давлении

Ардуино код програмы

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
//#define MY_DEBUG
#define MY_RADIO_NRF5_ESB
#define MY_RF24_PA_LEVEL (NRF5_PA_MAX)
#define MY_DISABLED_SERIAL
#define MY_PASSIVE_NODE
#define MY_NODE_ID 1
#define MY_PARENT_NODE_ID 0
//#define MY_PARENT_NODE_IS_STATIC
//#define MY_TRANSPORT_UPLINK_CHECK_DISABLED

#include <MySensors.h>

bool sleep_flag;
bool metric = true;
bool last_sent_value;

uint16_t currentBatteryPercent;
uint16_t lastBatteryPercent = 1000;
uint16_t battery_vcc_min = 2150;
uint16_t battery_vcc_max = 2950;
uint16_t batteryVoltage;
uint16_t battery_alert_level = 25;

uint32_t default_sleep_time = 60000;
uint32_t SLEEP_TIME;
uint32_t newmillisforbatt;
uint32_t battsendinterval = 3600000;

float tempThreshold = 0.5;
float humThreshold = 5;
float presThreshold = 1;
float pres_mmThreshold = 1;
float temperature;
float pressure;
float pressure_mm;
float humidity;
float lastTemperature = -1;
float lastHumidity = -1;
float lastPressure = -1;
float lastPressure_mm = -1;

#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme;

#define TEMP_CHILD_ID 0
#define HUM_CHILD_ID 1
#define BARO_CHILD_ID 2
#define CHILD_ID_VOLT 254

MyMessage voltMsg(CHILD_ID_VOLT, V_VOLTAGE);
MyMessage temperatureMsg(TEMP_CHILD_ID, V_TEMP);
MyMessage humidityMsg(HUM_CHILD_ID, V_HUM);
MyMessage pressureMsg(BARO_CHILD_ID, V_PRESSURE);


void preHwInit() {
  pinMode(21, INPUT);
  pinMode(25, OUTPUT);
  digitalWrite(25, HIGH);
  pinMode(26, OUTPUT);
  digitalWrite(26, HIGH);
  pinMode(27, OUTPUT);
  digitalWrite(27, HIGH);
}


void before() {
  NRF_POWER->DCDCEN = 1;
  NRF_NFCT->TASKS_DISABLE = 1;
  NRF_NVMC->CONFIG = 1;
  NRF_UICR->NFCPINS = 0;
  NRF_NVMC->CONFIG = 0;
  if (NRF_SAADC->ENABLE) {
    NRF_SAADC->TASKS_STOP = 1;
    while (NRF_SAADC->EVENTS_STOPPED) {}
    NRF_SAADC->ENABLE = 0;
    while (NRF_SAADC->ENABLE) {}
  }
  pinMode(BLUE_LED, OUTPUT);
  pinMode(RED_LED, OUTPUT);
  digitalWrite(BLUE_LED, HIGH);
  digitalWrite(RED_LED, HIGH);
  digitalWrite(27, LOW);
}

void setup()
{
  digitalWrite(27, HIGH);
  bme_initAsleep();
  wait(100);
  sendBatteryStatus();
  wait(100);
}


void presentation()  {
  sendSketchInfo("EFEKTA BME280 Sensor", "1.2");
  present(CHILD_ID_VOLT, S_MULTIMETER, "Battery");
  present(TEMP_CHILD_ID, S_TEMP, "TEMPERATURE [C or F]");
  present(HUM_CHILD_ID, S_HUM, "HUMIDITY [%]");
  present(BARO_CHILD_ID, S_BARO, "PRESSURE [hPa or mmHg]");
}


void loop() {
  wait(10);
  bme.takeForcedMeasurement();
  wait(100);
  sendData();
  if (millis() - newmillisforbatt >= battsendinterval) {
    sleep_flag = 1;
    sendBatteryStatus();
  }
  if (sleep_flag == 0) {
    sleep(SLEEP_TIME);
    sleep_flag = 1;
  }
}


void blinky(uint8_t pulses, uint8_t repit, uint8_t ledColor) {
  for (int x = 0; x < repit; x++) {
    if (x > 0) {
      sleep(500);
    }
    for (int i = 0; i < pulses; i++) {
      if (i > 0) {
        sleep(100);
      }
      digitalWrite(ledColor, LOW);
      wait(20);
      digitalWrite(ledColor, HIGH);
    }
  }
}


void sendBatteryStatus() {
  wait(20);
  batteryVoltage = hwCPUVoltage();
  wait(2);

  if (batteryVoltage > battery_vcc_max) {
    currentBatteryPercent = 100;
  }
  else if (batteryVoltage < battery_vcc_min) {
    currentBatteryPercent = 0;
  }
  else {
    if (lastBatteryPercent == 1000) {
      currentBatteryPercent = (100 * (batteryVoltage - battery_vcc_min)) / (battery_vcc_max - battery_vcc_min) + 5;
    } else {
      currentBatteryPercent = (100 * (batteryVoltage - battery_vcc_min)) / (battery_vcc_max - battery_vcc_min) - 5;
    }
  }
  sendBatteryLevel(currentBatteryPercent);
  wait(100);
  if (lastBatteryPercent < battery_alert_level) {
  blinky(3, 1, RED_LED);
  }
  else {
    blinky((last_sent_value == true ? 2 : 1), 1, BLUE_LED);
  }
  sleep_flag = 0;
  newmillisforbatt = millis();
}


void sendData() {
  temperature = bme.readTemperature();
  wait(20);
  humidity = bme.readHumidity();
  wait(20);
  pressure = bme.readPressure() / 100.0F;
  if (!metric) {
    temperature = temperature * 9.0 / 5.0 + 32.0;
  } else {
    pressure = pressure * 0.75006375541921;
  }
  CORE_DEBUG(PSTR("MY_TEMPERATURE: %dn"), (int)temperature);
  CORE_DEBUG(PSTR("MY_HUMIDITY: %dn"), (int)humidity);
  CORE_DEBUG(PSTR("MY_PRESSURE: %dn"), (int)pressure);
  
    if (abs(temperature - lastTemperature) >= tempThreshold) {
      send(temperatureMsg.set(temperature, 1));
      lastTemperature = temperature;
      sleep(1000);
    }

    if (abs(humidity - lastHumidity) >= humThreshold) {
      send(humidityMsg.set(humidity, 0));
      lastHumidity = humidity;
      sleep(1000);
    }

    if (abs(pressure - lastPressure) >= presThreshold) {
      send(pressureMsg.set(pressure, 0));
      lastPressure = pressure;
      sleep(1000);
    }
  sleep_flag = 0;
}


void bme_initAsleep() {
  if (! bme.begin(&Wire)) {
    while (1);
  }
  bme.setSampling(Adafruit_BME280::MODE_FORCED,
                  Adafruit_BME280::SAMPLING_X1, // temperature
                  Adafruit_BME280::SAMPLING_X1, // pressure
                  Adafruit_BME280::SAMPLING_X1, // humidity
                  Adafruit_BME280::FILTER_OFF   );
  wait(1000);
}

Корпус для датчика разрабатывался в 3Д редакторе:

Беспроводной датчик температуры, влажности и атмосферного давления на nRF52832 - 5

Напечатан был на 3D принтере ANYCUBIC FOTON смолой белого цвета этого же производителя, толщина слоя была выбрана средняя — 50микрон. Время печати корпуса и крышки 3 часа.

Беспроводной датчик температуры, влажности и атмосферного давления на nRF52832 - 6

Беспроводной датчик температуры, влажности и атмосферного давления на nRF52832 - 7

Беспроводной датчик температуры, влажности и атмосферного давления на nRF52832 - 8

Сеть MySensors в которой работает датчик обменивается данными с системой умного дома Мажордомо. Зарегистрированный датчик в модуле Майсенсорс Мажордомо выглядит так:
Беспроводной датчик температуры, влажности и атмосферного давления на nRF52832 - 9

Беспроводной датчик температуры, влажности и атмосферного давления на nRF52832 - 10

Для желающих сделать себе такой же в статье даны ссылки на всё необходимое.

Место где всегда с радостью помогут всем кто хочется познакомится с MYSENSORS (установка плат, работа с микроконтролерами nRF5 в среде Arduino IDE, советы по работе с протоколом mysensors — @mysensors_rus

Автор: Berkseo

Источник