Хочу поделиться своими наработками. Я написал программу для управления освещением и вытяжкой в ванной, сделал сенсорное управление кранами на кухне и в ванной, а так же RGB светодиодной лентой для подсветки кухонного гарнитура.
Я только начинаю изучать Arduino и программирование так что не судите строго. Может кому то пригодится или знающие люди подскажут может можно как-то оптимизировать код программы.
Логика работы сенсоров кранов:
если поднести руки (или любой предмет) вода включится, после того как убрали руки вода выключится через 2 с. Если в зону действия сенсора на долго попал какой-то предмет, например поставили кастрюлю в раковину, вода выключится через 30 с. И включится только после того как предмет будет убран и сенсор будет активирован снова.
Логика управления освещением и вытяжкой:
при открытии двери свет включается и начинается выдержка 40 с до выключения света. Если фиксируется движение, начало выдержки обновляется. При закрытии двери начинается выдержка 5 с, при которой не фиксируется движение. Я добавил эту выдержку потому, что иногда при выходе из ванной и закрытии двери срабатывает датчик движения и включается свет, когда в ванной никого нет.
Когда защитная выдержка заканчивается, начинается фиксация движения. И если датчик движения сработал три раза в течении 40 с после первой сработки, свет останется включенным до открытия двери.
После закрытия двери, если нет движения, свет выключится через 15 с.
Управление RGB лентой и её блоком питания я сделал с помощью переменного резистора. Программный код, с некоторыми переделками, я взял с сайта http://alexgyver.ru/arduino-algorithms/
В своём проекте я использовал Arduino Nano.
Код: Выделить всё
#define SENSOR1_PIN 2 //вход управления 1м электроклапаном
#define SENSOR2_PIN 3 //вход управления 2м электроклапаном
#define FLAP1_PIN 4 //выход управления 1м электроклапаном
#define FLAP2_PIN 5 //выход управления 2м электроклапаном
#define GERKON_PIN 6 //вход геркон
#define SENSOR_PIN 7 //вход датчик движения
#define LAMP_PIN 8 //выход управления освещением
#define R_PIN 9 //выход управления красным цветом
#define G_PIN 10 //выход управления зеленым цветом
#define B_PIN 11 //выход управления голубым цветом
#define RGB_POWER 12 //выход вкл/выкл БП для RGB
#define FAN_BUTTON_PIN 15 //вход управления вентилятором (кнопка)
#define FAN_PIN 16 //выход управления вентилятором
#define DEBOUNCE_DELAY 100 //время антидребезга кнопки вентилятора в мс
#define FAN_ON_TIME 10 //время работы вентилятора после выкл. света, мин
#define ON_FLAP1_ON 30 //время работы 1го клапана при активации сенсора, сек
#define ON_FLAP1_OFF 2 //время работы 1го клапана при деактивации сенсора, сек
#define ON_FLAP2_ON 30 //время работы 2го клапана при активации сенсора, сек
#define ON_FLAP2_OFF 2 //время работы 2го клапана при деактивации сенсора, сек
#define DEBOUNCE1_SENSOR 300 //задержка сработки для 1го электроклапана, мс
#define DEBOUNCE2_SENSOR 300 //задержка сработки для 2го электроклапана, мс
#define DELAY_LAMP_OFF_OPEN 40 //задержка выключения света после открытия двери, сек
#define DELAY_LAMP_OFF_CLOSE 15 //задержка выключения света после закрытия двери, сек
#define DELAY_DOOR_CLOSE 5 //задержка регистрации движения после закрытия двери, сек
#define DEBOUNCE_GERKON 80 //время антидребезга геркона в мс
#define INTERVAL 250 //интервал мигания светодиода индик. сработки датчика движения, мс
//переменные для управления кранами
unsigned long int timeSensor1On, timeSensor1Off, timeSensor2On, timeSensor2Off;
bool flap1On = false; //переменная для хранения состояния 1го электроклапана
bool flap2On = false; //переменная для хранения состояния 2го электроклапана
bool sensor1 = LOW; //переменная для хранения состояния сенсора 1го электроклапана
bool sensor2 = LOW; //переменная для хранения состояния сенсора 2го электроклапана
bool lastSensor1 = HIGH; //переменная для хранения последнего состояния сенсора 1го электроклапана
bool lastSensor2 = HIGH; //переменная для хранения последнего состояния сенсора 2го электроклапана
//////////////////////////////////////////////////////////////////////////////////
//переменные для управления освещением
bool lamp; //переменная для хранения состояния освещения (вкл/выкл)
bool gerkon_flag; //вспомогательная переменная для хранения состояния сработки геркона
bool sensor_flag; //вспомогательная переменная для хранения состояния датчика движения
bool motionClose; //переменная для индикации движения при закрытой двери
bool motionOpen; //переменная для индикации движения при открытой двери
unsigned long doorClosedTime, doorOpenedTime, doorClosedDelay; //переменные для хранения времени момента закрытия и открытия двери
//переменные для функции светодиодной индикации сработки датчика движения
unsigned long int previousMillis = 0;
bool ledMotionState;
byte motionCount = 0;
////////////////////////////////////////////////////////////////////////////////////
//переменные для управления вентилятором
bool fanState;
bool fanButtonState;
bool lastFanButtonState;
unsigned long int lastDebounceTime = 0, fanButtonOffTime, workTime = (long)FAN_ON_TIME * 60 * 1000;
/////////////////////////////////////////////////////////////////////////////////////
//переменные для функции задержки (антидребезга) геркона
bool _bounse3S = 1;
bool _bounse3O = 1;
unsigned long _bounse3P = 0UL;
/////////////////////////////////////////////////////////////////////////////////////
//переменные для функций задержек (антидребезга) сенсоров электроклапанов
bool _bounse1S = 1;
bool _bounse1O = 1;
unsigned long _bounse1P = 0UL;
bool _bounse2S = 1;
bool _bounse2O = 1;
unsigned long _bounse2P = 0UL;
/////////////////////////////////////////////////////////////////////////////////////
//переменные для управления RGB
byte bright = 100; // яркость, от 0 до 100 (можно повесить на второй потенциометр при желании)
byte R, G, B;
void setup() { //конфигурация портов (вход или выход)
pinMode(SENSOR1_PIN, INPUT_PULLUP); //подтягиваем вход к плюсу через внутренний резистор 20к
pinMode(FLAP1_PIN, OUTPUT);
pinMode(SENSOR2_PIN, INPUT_PULLUP);
pinMode(FLAP2_PIN, OUTPUT);
pinMode(GERKON_PIN, INPUT_PULLUP);
pinMode(LAMP_PIN, OUTPUT);
pinMode(SENSOR_PIN, INPUT);
pinMode(FAN_BUTTON_PIN, INPUT_PULLUP);
pinMode(FAN_PIN, OUTPUT);
pinMode(R_PIN, OUTPUT);
pinMode(G_PIN, OUTPUT);
pinMode(B_PIN, OUTPUT);
pinMode(RGB_POWER, OUTPUT);
pinMode(13, OUTPUT);
}
//управление светодиодной лентой RGB (взято с сайта http://alexgyver.ru/arduino-algorithms/)
void rgbLed () {
int colorPot = analogRead(0); // получаем значение с потенциометра (0 - 1023)
// разбиваем диапазон 0 - 1023 на участки, и играемся с цветом согласно текущему значению
if (colorPot >= 10) { //если значение больше 10, вкл. БП, иначе выкл.
digitalWrite(RGB_POWER, HIGH);
} else {
digitalWrite(RGB_POWER, LOW);
}
if (colorPot <= 10) {
R = 0;
G = 0;
B = 0;
} else if (colorPot > 10 && colorPot <= 20) {
byte k = map(colorPot, 10, 20, 0, 255);
R = 255;
G = 255;
B = 255;
} else if (colorPot > 20 /*&& colorPot <= 145*/) {
byte k = map(colorPot, /*10, 145*/20, 1023, 0, 255);
R = 255 - k;
G = 255 - k;
B = 255 - k;
} /*else if (colorPot > 145 && colorPot <= 290) {
byte k = map(colorPot, 145, 290, 0, 255);
R = 0;
G = 0;
B = k;
} else if (colorPot > 290 && colorPot <= 435) {
byte k = map(colorPot, 290, 435, 0, 255);
R = 0;
G = k;
B = 255;
} else if (colorPot > 435 && colorPot <= 580) {
byte k = map(colorPot, 435, 580, 0, 255);
R = k;
G = 255;
B = 255;
} else if (colorPot > 581 && colorPot <= 725) {
byte k = map(colorPot, 581, 725, 0, 255);
R = 255 - k;
G = k;
B = 0;
} else if (colorPot > 725 && colorPot <= 870) {
byte k = map(colorPot, 725, 870, 0, 255);
R = 0;
G = 255 - k;
B = k;
} else if (colorPot > 870) {
byte k = map(colorPot, 870, 1023, 0, 255);
R = k;
G = 0;
B = 255 - k;
}*/
// подаём ШИМ на светодиод, учитывая яркость
analogWrite(R_PIN, (bright * R / 100));
analogWrite(G_PIN, (bright * G / 100));
analogWrite(B_PIN, (bright * B / 100));
}
//функция вкл/выкл вентилятора
//вентилятор вкл/выкл кнопкой при включенном свете. После выключения света,
//если вентилятор был включен, он выключится через время FAN_ON_TIME
void fan(bool fanOff) {
bool reading = !digitalRead(FAN_BUTTON_PIN);
if (reading != lastFanButtonState) {
lastDebounceTime = millis() + DEBOUNCE_DELAY;
}
if (lastDebounceTime <= millis()) {
if (reading != fanButtonState) {
fanButtonState = reading;
if (!fanButtonState) {
fanState = !fanState;
}
}
}
lastFanButtonState = reading;
if (fanOff && fanState) { //если свет выключился и вентилятор включен
fanButtonOffTime = millis() + workTime; //записываем время выключения света (с опережением millis())
}
if (fanButtonOffTime <= millis()) { //если прошло время FAN_ON_TIME
fanState = LOW; //выключить вентилятор
}
digitalWrite(FAN_PIN, fanState);
}
//функция антидребезга для геркона
//сформирована в программе FLProg
bool bounseGerkon () {
bool _bounceTmpD1 = digitalRead (GERKON_PIN);
if (!_bounse3S)
{
if (millis() >= (_bounse3P + DEBOUNCE_GERKON))
{
_bounse3O = _bounceTmpD1;
_bounse3S = 1;
}
}
else
{
if ( _bounceTmpD1 != _bounse3O )
{
_bounse3S = 0;
_bounse3P = millis();
}
}
return _bounse3O;
}
//------------------------функция управления освещением, начало---------------------------
/* Логики работы: открыли дверь - включился свет, начался отсчет выдержки. По окончании
выдержки свет выключается, если при открытой двери есть движение, отсчет выдержки
обновляется. Если закрыть дверь при включенном свете - начинается отсчет второй вы-
держки, по истечении которой свет выключится. Если есть движение при закрытой двери
и включенном свете и при сработке датчика движени три раза в течении DELAY_LAMP_OFF_OPEN, свет не выключится
до следующего открывания двери не зависимо будет ещё срабатывать датчик движения или нет.
*/
void lite() {
if (bounseGerkon () && !gerkon_flag) { //если геркон разомкнулся (дверь открылась) и до этого был замкнут
gerkon_flag = HIGH; //изменение переменной (дверь была открыта)
lamp = HIGH; //включить свет
motionClose = LOW; //сброс индикатора движения
doorOpenedTime = millis() + (long)DELAY_LAMP_OFF_OPEN * 1000; //начало отсчета до выключения света при открытой двери
}
if (bounseGerkon () && gerkon_flag && digitalRead(SENSOR_PIN)) { //если дверь открыта и есть движение
lamp = HIGH; //включить свет
doorOpenedTime = millis() + (long)DELAY_LAMP_OFF_OPEN * 1000; //обновление отсчета до выключения света при открытой двери
}
if (bounseGerkon () && gerkon_flag && doorOpenedTime <= millis()) { //если дверь открыта и прошло время выдержки
lamp = LOW; //выключить свет
}
if (!bounseGerkon () && gerkon_flag) { //если геркон замкнулся (дверь закрылась) и до этого он был разомкнут
gerkon_flag = LOW; //изменение переменной (дверь была закрыта)
doorClosedTime = millis() + (long)DELAY_LAMP_OFF_CLOSE * 1000; //начало отсчета до выключения света при закрытой двери
doorClosedDelay = millis() + (long)DELAY_DOOR_CLOSE * 1000; //начало задержки фиксации движения при закрытой двери
}
if (!bounseGerkon () && !gerkon_flag) { //если дверь закрыта
if (doorClosedDelay <= millis()) { //и если прошло время задержки до выключения света
if (digitalRead(SENSOR_PIN) && !sensor_flag) { //подсчет кол-ва сработок датчика движения при закрытой двери
sensor_flag = HIGH; //для предотвращение ложного включения света до следующего
motionCount ++; //открытия двери
motionClose = HIGH; //
if (!lamp) { //если свет выключен
lamp = HIGH; //включить свет
doorOpenedTime = millis() + (long)DELAY_LAMP_OFF_OPEN * 1000; //на время DELAY_LAMP_OFF_OPEN
}
}
if (!digitalRead(SENSOR_PIN) && sensor_flag) { //если датчик движения деактивировался
sensor_flag = LOW; //изменить переменную
}
if (motionCount >= 3) {
motionCount = 3; //ограничение кол-ва сработок датчика движения при закрытой двери до 3
}
}
}
//если дверь закрыта, прошло время до выключения света и счетчик сработок меньше трех
if (!bounseGerkon () && !gerkon_flag && doorOpenedTime <= millis() && motionCount <= 2) {
lamp = LOW; //выключить свет
}
//если дверь закрыта и движения не было и прошло время до выключения света при закрытой двери
if (!bounseGerkon () && !gerkon_flag && !motionClose && doorClosedTime <= millis()) {
lamp = LOW; //выключить свет
}
digitalWrite(LAMP_PIN, lamp); //передать состояние переменной lamp на пин LAMP_PIN
if (!lamp) { //если свет выключен
motionCount = 0; //обнулить счетчик сработок датчика движения
}
//индикация сработки датчика движения
//эту индикацию я сделал только для удобства мониторинга сработки датчика движения
if (digitalRead(SENSOR_PIN)) { //если сработал датчик движения
digitalWrite(13, HIGH); //включить светодиод
} else {
if (motionClose) { //мигание при сработке при закрытой двери
if (millis() - previousMillis >= INTERVAL) { //
previousMillis = millis(); //
if (ledMotionState == LOW) { //
ledMotionState = HIGH; //
} else { //
ledMotionState = LOW; //
} //
digitalWrite(13, ledMotionState); //
} //
} else { //при деактивации датчика движ., если не было сработки при закрытой двери,
digitalWrite(13, LOW); //выключить светодиод
}
}
}
//------------------------функция управления освещением, конец---------------------------
//функция задержки сработки для 1го электроклапана
//сформирована в программе FLProg
bool bounse1flap () {
bool _bounceTmpD1 = (digitalRead (SENSOR1_PIN));
if (!_bounse1S)
{
if (millis() >= (_bounse1P + DEBOUNCE1_SENSOR))
{
_bounse1O = _bounceTmpD1;
_bounse1S = 1;
}
}
else
{
if ( _bounceTmpD1 != _bounse1O )
{
_bounse1S = 0;
_bounse1P = millis();
}
}
return _bounse1O;
}
//функция задержки сработки для 2го электроклапана
//сформирована в программе FLProg
bool bounse2flap () {
bool _bounceTmpD1 = (digitalRead (SENSOR2_PIN));
if (!_bounse2S)
{
if (millis() >= (_bounse2P + DEBOUNCE2_SENSOR))
{
_bounse2O = _bounceTmpD1;
_bounse2S = 1;
}
}
else
{
if ( _bounceTmpD1 != _bounse2O )
{
_bounse2S = 0;
_bounse2P = millis();
}
}
return _bounse2O;
}
//функция управления 1м краном
void flap1() {
sensor1 = bounse1flap (); //считывание состояние сенсора 1 с задержкой
if (sensor1 == LOW && lastSensor1 == HIGH) { //если активен и последнее сост. - неактивен, то ...
digitalWrite(FLAP1_PIN, HIGH); //включаем 1й электроклапан
lastSensor1 = LOW; //записываем последнее состояние сенсора 1
timeSensor1On = millis() + (ON_FLAP1_ON * 1000); //записываем время сработки сенсора 1 +
} //"опережаем время" на ON_FLAP1_ON для того,чтобы решить проблему
//с переполнением millis() т.е. "опережение времени" на ON_FLAP1_ON и ON_FLAP1_OFF
//(взято с сайта http://alexgyver.ru/arduino-algorithms/ от WakeUp4L1fe)
if (sensor1 == HIGH && lastSensor1 == LOW) { //если неактивен и последнее состояние сенсора 1 - активен
timeSensor1Off = millis() + (ON_FLAP1_OFF * 1000); //записываем время деактивации сенсора 1 +
lastSensor1 = HIGH; //"опережаем время" на ON_FLAP1_OFF
}
if ((timeSensor1On <= millis() && sensor1 == LOW) || //если после активации сенсора 1 прошло время ON_FLAP1_ON
(timeSensor1Off <= millis() && lastSensor1 == HIGH)) { //или если после деактивации сенсора 1 прошло время ON_FLAP1_OFF
digitalWrite(FLAP1_PIN, LOW); //то выключить кран
}
}
//функция управления 2м краном
void flap2() {
sensor2 = bounse2flap ();
if (sensor2 == LOW && lastSensor2 == HIGH) {
digitalWrite(FLAP2_PIN, HIGH);
lastSensor2 = LOW;
timeSensor2On = millis() + (ON_FLAP2_ON * 1000);
}
if (sensor2 == HIGH && lastSensor2 == LOW) {
timeSensor2Off = millis() + (ON_FLAP2_OFF * 1000);
lastSensor2 = HIGH;
}
if ((timeSensor2On <= millis() && sensor2 == LOW) ||
(timeSensor2Off <= millis() && lastSensor2 == HIGH)) {
digitalWrite(FLAP2_PIN, LOW);
}
}
//основной цикл программы
void loop() {
lite();
fan(lamp);
flap1();
flap2();
rgbLed ();
}