Это Arduino проект, программа написана в Arduino IDE, Для работы сенсора BME280 использована библиотека Adafruit Industries — гитхаб сенсоры | гитхаб BME280. Для работы с платами nRF52832 в Arduino IDE использован проект Sandeep Mistry — гитхаб. Передача данных на контролер Умного Дома осуществляется по протоколу Mysensors — гитхабплаты | протокол|библиотека.
Плата датчика разрабатывалась в программе Диптрейс. Размеры платы 36.8мм Х 25мм.
- С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 штук в любом цвете.
Ссылка на архив с гербер файлами
Датчик работает по протоколу 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)); // отправка данных о температуре, точность 1 знак после запятой send(humidityMsg.set(humidity, 0)); // отправка данных о влажности, точность 0 знаков после запятой send(pressureMsg.set(pressure, 0)); // отправка данных о давлении, точность 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: %d\n"), (int)temperature);
CORE_DEBUG(PSTR("MY_HUMIDITY: %d\n"), (int)humidity);
CORE_DEBUG(PSTR("MY_PRESSURE: %d\n"), (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Д редакторе:
Напечатан был на 3D принтере ANYCUBIC FOTON, использовалась смола белого цвета этого же производителя, толщина слоя была выбрана средняя — 50микрон. Время печати корпуса и крышки 3 часа.
Сеть MySensors в которой работает датчик обменивается данными с системой умного дома Мажордомо. Зарегистрированный датчик в модуле Майсенсорс Мажордомо выглядит так:
Для желающих сделать себе такой же в статье даны ссылки на всё необходимое.
Место где всегда с радостью помогут всем кто хочется познакомиться с MYSENSORS (установка плат, работа с микроконтролерами nRF5 в среде Arduino IDE, советы по работе с протоколом mysensors — @mysensors_rus
Комментарии (25)
enjoyneering
12.06.2019 18:43Проясните пожалуйста, mysensors это протокол типа MQTT?
Berkseo Автор
12.06.2019 18:46типа зиг би или ble mesh скорее, конечно сильно притянуто. Сеть централизованная, но транспортный уровень автоматически строит маршруты…
enjoyneering
12.06.2019 19:15понятно. на ваш взгляд что лучше mysensors или MQTT?
Berkseo Автор
12.06.2019 19:18У меня сначала были esp-шки и mqtt, теперь mysensors и потихоньку осваиваю зигби
sav13
13.06.2019 05:28Вопрос некорректный.
MQTT работает поверх TCP/IP, а это значит должна быть плата с Ethernet или WiFi.
Для батарейных устройств это ИМХО избыточно.
Но если сильно нужно, то есть Mysensors MQTT Gateway — на ардуине с Ethernet либо ESP8266/32olartamonov
13.06.2019 11:41Вопрос корректный, ответ некорректный.
Во-первых, есть MQTT-SN, во-вторых, можно поднять MQTT over 6LoWPAN over BLE, например.
sav13
13.06.2019 12:22MQTT-SN работает поверх UDP. Что принципиально это меняет на транспортном уровне?
Теоретически можно пустить любой протокол верхнего уровня через любой транспорт, только чтобы соединиться с MQTT брокером понадобиться шлюз.
olartamonov
14.06.2019 07:54То, что фраза «а это значит должна быть плата с Ethernet или WiFi» неверна.
Не должна.
только чтобы соединиться с MQTT брокером понадобиться шлюз
Чтобы соединиться с MQTT-брокером, понадобится MQTT-брокер, а железка, на которой он работает, может всё так же не иметь ни Wi-Fi, ни Ethernet. Ничего другого не понадобится.
igrushkin
13.06.2019 10:01а как Вы паяли вме280? Я вот не решаюсь, предпочитаю модули
qoj
13.06.2019 13:01Мне вот тоже интересно. Я паял модуль и судя по всему перегрел датчик, т.к. данные с него начали приходить не совсем корректные, да и вести себя стал тоже странно. А тут прям на плату запаян.
Berkseo Автор
13.06.2019 13:03Довольно просто, развожу немного флюс геля, немного паяльной пасты, ставлю датчик, зажимаю обратным пинцетом, другим пинцетом подправляю позицию, включаю фен, как разогреется быстро круговыми движениями греем, примерно около 5 сек. всё
A__D
13.06.2019 10:35mysku.ru/blog/diy/73421.html
Копипаста.wholeman
13.06.2019 10:50+1Из правил «Не следует копировать на «Хабр» тексты, опубликованные другими людьми на других ресурсах, но можно копировать собственные тексты, если они не нарушают правила ресурса.» Кажется, раньше было требование, чтобы текст не был где-то опубликован до Хабра. Впрочем, формально и оно выполнено: здесь пост появился в 18:07, там — в 20:14.
osmanpasha
А что на другом конце BLE?
Berkseo Автор
Там не ble, режим совместимости с nrf24l01, на другом конце шлюз.
osmanpasha
Понятно, спасибо. Интересная статья.
wholeman
А почему предпочли такое решение, ведь устройств с поддержкой BLE намного больше, и использование этого протоколу сделало бы датчик значительно универсальнее?
Berkseo Автор
потому что не все могут работать в режиме совместимости с аутентичным нордиковским нрф24l01
wholeman
Извиняюсь — не вполне понятно выразился. Вопрос был не о наличии шлюза (оно понятно), а об использовании режима совместимости вместо BLE.