Хабрахабр

Термометр & гигрометр на ATMEGA 328P-MU — Поднимаем уровень ардуино разработок

Сегодня хочу поделиться одним из своих Ардуино проектов. Когда-то, не очень что бы давно я где-то на просторах интернета узнал об Ардуино. Влился я в это дело довольно быстро, уровень вхождения там не высокий. Через некоторое время уже собрав кучу датчиков, сенсоров для умного дома стал ловить себя на мысли что как то это все не по уму. Модули, большие незамысловатые коробки, куча проводов и термоклея :). Смотря на свою коробку с датчиком темературы и например на датчик температуры того же Сяоми, понимал что хочется что бы выглядел как у Сяоми но в тоже время что бы его можно было как хочешь перепрограмировать как мой в коробке размерами 10см на 6см с проводами и термоклеем. И наверное тогда и было положено начало моим DIY ардуино проектам на PCB платах.

Это более «мелкая» версия(абсолютный аналог) изветного всем ардуинщикам процессора atmega328p-au (Arduino Uno, Pro Mini, Nano). В сегодняшней статье речь пойдет о датчике температуры и влажности на базе процессора atmega328p-mu. Что это? Если кто то ранее читал мои статьи то знает что я предпочитаю Mysensors. 4Ггц, 915, 868, 433 Мгц, а так же проводных сетей на интерфейсе 485, возможно не все упомянул, тк протокол постоянно развивается, все время что то добавляется. Это очень простая и хорошо проработанная и что немаловажно отлично описанная библиотека под Ардуино ИДЕ (и не только) для создания IOT радиосетей на частотах 2.

Делал без оглядки на корпус, по принципу, главное сделать датчик а уж корпус как нибудь да напечатаю,… да уж, не делайте так :). Первое что было сделано, это собственно сам датчик на PCB плате. Из «прибамбасов» это внешняя SPI флешка для прошивки по воздуху(для работы требуется бутлоадер ДуалОптибут, в последствии я перестал их(флешки) ставить на платы, тк пара прошивок по воздуху и полбатарейки нет) и «крипто микра» ATSHA204A так сказать для полного железного комплекта(в Mysensors для активации подписей, шифрования и тп достаточно просто указатать в начале скетча нужные #def). По сути сам датчик это та же ардуинка Про Мини, радиомодуль nRF24l01, датчик температуры и влажности SHT20, только без проводов и термоклея.

Платы заказывал в Китае на сайте jlcpcb.com, 2 бакса, любой цвет, и через 2 недели вы уже получаете на руки 10 штук «своего творения» :). Саму плату делал в программе Диптрейс, посмотрев видеоуроки на Ютубе, вначале показалось это чем то «адским», но на самом деле все оказалось не так уж и сложно.

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

К моменту как это понимание пришло уже узнал о другой технологии 3D печати — SLA. В начале разработки корпуса идея была печатать его на FDM 3D принтере, но чем дальше в лес тем становилось понятнее что все мои хотелки оно не способно воспроизвести. (Ссылк ана Али, не рекламная, не партнерская,… просто ссылка). Недолго думая и впечатлившись качеством печати была оформлена хотелка на Али — ANYCUBIC Фотон.

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

Это попытки напечатать туже модель на FDM принтере:

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

Все закупалось на Али (да, там полно оказывается не только ардуино модулей)

7uF — 4. SMD tantalum capacitor 4. 27mm — J1, J2
SMD resistor 20K Ohm — 20K | 5% — R1
SMD resistor 4. 7uF | 10v | 10% — C1
SMD ceramic capacitor 100nF | Y5V — 100nF | 50v | +80-20% — C2, C3, C4, C5, C6, C7
LED — LED SIDE — D1
Pin Header Female — 2x3P | 6pin | 1. 7K | 5% — R2, R3, R4
SMD resistor 470K Ohm — 470 | 1% — R5
SMD resistor 1M Ohm — 1M | 1% — R6
SMD resistor 18K Ohm — 18K | 5% — R7
SMD resistor 10K Ohm — 10K | 5% — R8
4-pin SMD side button — SW1, SW2
512-Kbit, 1. 7K Ohm — 4. 4GHz 1. 65V SPI Serial Flash Memory — AT25DF512C-SSHN-B — U1
Mini NRF24L01 + 2. 27 SMD — U2
ATMEGA328P-MU QFN32 — U3
CRYPTO AUTHENTICATION, 1 WIRE — ATSHA204A-STUCZ-T — U4
Humidity and Temperature Sensor IC — SHT20 — U5
BATTERY HOLDER FOR CR2477-1 — L-KLS5-CR2477-1 — U6 27MM RF — nRF24l01 1.

Для работы с датчиком SHT20 был использован пример библиотеки DFRobot. Код программы достаточно прост. Впринципе любой скетч, для любого датчика можно за 5 минут преватить в скетч для работы в сети Mysensors.

Листинг кода

#include <Wire.h>
#include "DFRobot_SHT20.h"
DFRobot_SHT20 sht20; // https://github.com/DFRobot/DFRobot_SHT20 #define MY_DEBUG
//#define MY_DISABLED_SERIAL
#define MY_RADIO_RF24
#define MY_PASSIVE_NODE
#define MY_NODE_ID 200
#define MY_PARENT_NODE_ID 0
#define MY_PARENT_NODE_IS_STATIC
#define MY_TRANSPORT_UPLINK_CHECK_DISABLED
//#define MY_OTA_FIRMWARE_FEATURE
//#define MY_SIGNING_ATSHA204
//#define MY_SIGNING_ATSHA204_PIN A3
//#define MY_SIGNING_REQUEST_SIGNATURES #define TEMP_SENS_ID 1
#define HUM_SENS_ID 2
#define SETTING_LED_SENS_ID 100
#define DELAY_TIME_SENS_ID 101
#define BATTARY_SEND_SENS_ID 102
#define BATTARY_DATA_SENS_ID 103 #define BAT_COOF 3.04
#define BAT_MIN 195
#define BAT_MAX 295
#define ON 1
#define OFF 0 float humd;
float temp;
float oldhumd;
float oldtemp;
float tempThreshold = 0.5;
float humThreshold = 10.0;
static uint32_t lightMillis;
static uint32_t previousMillis;
uint32_t send_batteryTime;
uint32_t w_battetyTime = 0;
static uint8_t led_pin = 4;
static uint8_t mode_pin = 2; // interrupt
uint32_t delayTime;
int8_t battery;
int8_t old_battery;
uint8_t set_led;
boolean sleep_mode;
boolean configMode = 0;
int8_t timer_status = 0;
bool flag_mode_button = 0;
bool sleep_flag = 0;
bool listen_flag = 0; #include <MySensors.h> MyMessage msg_temp(TEMP_SENS_ID, V_TEMP);
MyMessage msg_hum(HUM_SENS_ID, V_HUM);
MyMessage msg_setting_led(SETTING_LED_SENS_ID, V_VAR1);
MyMessage msg_delay_time(DELAY_TIME_SENS_ID, V_VAR1);
MyMessage msg_battary_send(BATTARY_SEND_SENS_ID, V_VAR1);
MyMessage powerMsg(BATTARY_DATA_SENS_ID, V_VAR1); void preHwInit()
{ pinMode(led_pin, OUTPUT); digitalWrite(led_pin, OFF); pinMode(mode_pin, INPUT_PULLUP);
} void before()
delayTime = loadState(101); if (delayTime > 60) { delayTime = 3; saveState(101, delayTime); } send_batteryTime = loadState(102); if (send_batteryTime > 48) { send_batteryTime = 6; saveState(102, send_batteryTime); } digitalWrite(led_pin, ON);
} void presentation()
{ sendSketchInfo("Temp & Hum Sensor CR2477", "1.0"); wait(100); present(TEMP_SENS_ID, S_TEMP, "TEMPERATURE DATA"); wait(100); present(HUM_SENS_ID, S_HUM, "HUMIDITY DATA"); wait(100); present(SETTING_LED_SENS_ID, S_CUSTOM, "LED MODE"); wait(100); present(DELAY_TIME_SENS_ID, S_CUSTOM, "DELAY TIME/MIN"); wait(100); present(BATTARY_SEND_SENS_ID, S_CUSTOM, "BATTERY SEND TIME/H"); wait(100); present(BATTARY_DATA_SENS_ID, S_CUSTOM, "BATTERY DATA");
} void setup()
{ //attachInterrupt(0, configListener, RISING); digitalWrite(led_pin, OFF); wait(500); digitalWrite(led_pin, ON); wait(75); digitalWrite(led_pin, OFF); wait(50); digitalWrite(led_pin, ON); wait(75); digitalWrite(led_pin, OFF); wait(50); digitalWrite(led_pin, ON); wait(75); digitalWrite(led_pin, OFF); TRANSPORT_DEBUG(PSTR("MyS: OPERATING MODE\n")); wait(100); readBatLev(); wait(100); sht20.initSHT20(); wait(100); send_data(); wait(100); send(msg_delay_time.set(delayTime)); wait(100); send(msg_setting_led.set(set_led)); wait(100); send(msg_battary_send.set(send_batteryTime));
} void loop()
{ if (configMode == 0) { if (sleep_flag == 0) { timer_status = sleep(digitalPinToInterrupt(mode_pin), FALLING, delayTime * 60 * 1000, false); //timer_status = sleep(digitalPinToInterrupt(mode_pin), RISING, delayTime * 60 * 1000, false); sleep_flag = 1; } if (timer_status == -1) { w_battetyTime = w_battetyTime + (delayTime * 60 * 1000); if (w_battetyTime >= send_batteryTime * 60 * 60 * 1000) { readBatLev(); w_battetyTime = 0; } send_data(); sleep_flag = 0; } if (timer_status == 0) { if (digitalRead(2) == LOW && flag_mode_button == 0) //если кнопка нажата { flag_mode_button = 1; previousMillis = millis(); wait(50); } if (digitalRead(2) == LOW && flag_mode_button == 1) { if ((millis() - previousMillis > 0) && (millis() - previousMillis <= 2000)) { if (millis() - lightMillis > 50) { lightMillis = millis(); digitalWrite(led_pin, !digitalRead(led_pin)); } } if ((millis() - previousMillis > 2000) && (millis() - previousMillis <= 2500)) { digitalWrite(led_pin, OFF); } if ((millis() - previousMillis > 2500) && (millis() - previousMillis <= 4500)) { if (millis() - lightMillis > 25) { lightMillis = millis(); digitalWrite(led_pin, !digitalRead(led_pin)); } } if (millis() - previousMillis > 4500) { digitalWrite(led_pin, OFF); } } if (digitalRead(2) == HIGH && flag_mode_button == 1) //если кнопка НЕ нажата { if ((millis() - previousMillis > 0) && (millis() - previousMillis <= 2000)) { configMode = !configMode; flag_mode_button = 0; TRANSPORT_DEBUG(PSTR("MyS: CONFIGURATION MODE\n")); sleep_flag = 0; digitalWrite(led_pin, OFF); } if ((millis() - previousMillis > 2000) && (millis() - previousMillis <= 2500)) { flag_mode_button = 0; sleep_flag = 0; } if ((millis() - previousMillis > 2500) && (millis() - previousMillis <= 4500)) { flag_mode_button = 0; sleep_flag = 0; digitalWrite(led_pin, OFF); } if (millis() - previousMillis > 4500) { flag_mode_button = 0; sleep_flag = 0; wait(50); } } } } else { if (listen_flag == 0) { RF24_startListening(); listen_flag = 1; } if (millis() - lightMillis > 1000) { lightMillis = millis(); digitalWrite(led_pin, !digitalRead(led_pin)); } if (digitalRead(2) == LOW && flag_mode_button == 0) //если кнопка нажата { flag_mode_button = 1; //previousMillis = millis(); wait(50); } if (digitalRead(2) == LOW && flag_mode_button == 1) { } if (digitalRead(2) == HIGH && flag_mode_button == 1) //если кнопка НЕ нажата { configMode = !configMode; listen_flag = 0; flag_mode_button = 0; TRANSPORT_DEBUG(PSTR("MyS: OPERATING MODE\n")); digitalWrite(led_pin, OFF); wait(50); } }
} void receive(const MyMessage & message)
{ if (message.sensor == SETTING_LED_SENS_ID) { if (message.type == V_VAR1) { if (message.getByte() <= 1) { set_led = message.getBool(); saveState(100, set_led); send(msg_setting_led.set(set_led)); if (set_led == 0) { TRANSPORT_DEBUG(PSTR("MyS: STATUS LED: OFF\n")); } if (set_led == 1) { TRANSPORT_DEBUG(PSTR("MyS: STATUS LED: ON\n")); if (set_led == 1) { digitalWrite(led_pin, ON); wait(50); digitalWrite(led_pin, OFF); } } } } } if (message.sensor == DELAY_TIME_SENS_ID) { if (message.type == V_VAR1) { if (message.getULong() <= 60 && message.getULong() != 0) { delayTime = message.getULong(); saveState(101, delayTime); send(msg_delay_time.set(delayTime)); TRANSPORT_DEBUG(PSTR("MyS: THE NEW INTERVAL TEMP&HUM SEND VALUE IS SET: %d MIN.\n"), delayTime); if (set_led == 1) { digitalWrite(led_pin, ON); wait(50); digitalWrite(led_pin, OFF); } } else if (message.getULong() > 60) { delayTime = 60; saveState(101, delayTime); send(msg_delay_time.set(delayTime)); TRANSPORT_DEBUG(PSTR("MyS: THE NEW INTERVAL TEMP&HUM SEND VALUE IS SET: %d MIN.\n"), delayTime); if (set_led == 1) { digitalWrite(led_pin, ON); wait(50); digitalWrite(led_pin, OFF); } } else if (message.getULong() == 0) { delayTime = 1; saveState(101, delayTime); send(msg_delay_time.set(delayTime)); TRANSPORT_DEBUG(PSTR("MyS: THE NEW INTERVAL TEMP&HUM SEND VALUE IS SET: %d MIN.\n"), delayTime); if (set_led == 1) { digitalWrite(led_pin, ON); wait(50); digitalWrite(led_pin, OFF); } } } } if (message.sensor == BATTARY_SEND_SENS_ID) { if (message.type == V_VAR1) { if (message.getULong() <= 168) { send_batteryTime = message.getULong(); saveState(102, send_batteryTime); send(msg_battary_send.set(send_batteryTime)); TRANSPORT_DEBUG(PSTR("MyS: THE NEW INTERVAL BATTERY SEND IS SET: %d HOUR\n"), send_batteryTime); if (set_led == 1) { digitalWrite(led_pin, ON); wait(50); digitalWrite(led_pin, OFF); } } } }
} void send_data()
{ humd = sht20.readHumidity(); temp = sht20.readTemperature(); int t_humd = (int)humd; int t_temp = (int)temp; if (abs(temp - oldtemp) >= tempThreshold) { send(msg_temp.set(temp, 1)); oldtemp = temp; if (set_led == 1) { digitalWrite(led_pin, ON); wait(50); digitalWrite(led_pin, OFF); } } wait(100); if (abs(humd - oldhumd) >= humThreshold) { send(msg_hum.set(humd, 1)); oldhumd = humd; if (set_led == 1) { digitalWrite(led_pin, ON); wait(50); digitalWrite(led_pin, OFF); } } TRANSPORT_DEBUG(PSTR("MyS: DATA - TEMPERATURE: %d, HUMIDITY %d\n"), t_temp, t_humd);
} void readBatLev() { ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX0); wait(100); RF24_startListening(); wait(200); ADCSRA |= _BV(ADSC); while (bit_is_set(ADCSRA, ADSC)); uint8_t low = ADCL; uint8_t high = ADCH; long temp = (high << 8) | low; float vcc = temp * 1.1 / 1023 * BAT_COOF * 100; battery = map((int)vcc, BAT_MIN, BAT_MAX, 0, 100); if (battery < 0) { battery = 0; } if (battery > 100) { battery = 100; } TRANSPORT_DEBUG(PSTR("MyS: BATTERY LEVEL: %d, PREVIUS BATTERY LEVEL: %d\n"), battery, old_battery); TRANSPORT_DEBUG(PSTR("MyS: BATTERY LEVEL ADC: %d\n"), temp); /* if (old_battery != battery) { if (battery < old_battery) { old_battery = battery; wait(100); sendBatteryLevel(battery); wait(100); send(powerMsg.set(temp)); TRANSPORT_DEBUG(PSTR("MyS: SEND BATTERY LEVEL\n")); } else { battery = old_battery; } } */ wait(100); sendBatteryLevel(battery); wait(100); send(powerMsg.set(temp)); TRANSPORT_DEBUG(PSTR("MyS: SEND BATTERY LEVEL\n"));
}

На видео показан тест работы датчика:

Работа датчика с Мажордомо:

Контролером умного дома является Мажордомо(думаю многим известная система), написан модуль Mysensors(пожалуй одна из лучших реализаций поддержки протокола Mysensors в контролерах)

Все детали разработки, файлы плат, прошивка, 3d модели корпуса выложены на сайте www.openhardware.io. Проект естественно открытый и рекомендованный к повторению. На вопросы по данной разработке, по трудностям в ваших разработках на ардуинках и Mysensors всегда придут на помощь в нашем телеграмм чате — t.me/mysensors_rus.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть