кто на новенького
- Войдите на сайт для отправки комментариев
Всем привет!!! Смотрел на жалюзи и прямо просится автоматизация процесса. Залез в инет нашёл разные темы и наткнулся на реализации моей затеи на ардуино, заказал с Китая эту игрушку, теперь я на форуме))
Я знаю, что примерно через год, я так же как и вы буду пинать новичков, но всё же.
По теме, у меня два балкона, на каждом по четыре жалюзи, хочу реализовать закрытие и открытие (без поднятия) с помощью двух ик-пультов на каждый балкон соответственно. Пока думаю организовать одновременное срабатывания и без датчика положения жалюзи
Реализацию вижу на шаговых моторах с шаговым драйвером.
Вопрос, можно ли использовать один ардуино, или на каждый балкон по штуке? Как запаралелить эти моторы, на каждый мотор тащить питание или их можно просто от одного источника запитать?
Жалюзи у Вас такие, как в статье на Гиктаймс ?
Если да, то могу поделиться своим опытом автоматизации таких жалюзи на базе шаговиков 28BYJ-48 и драйверов на ULN2003, без датчика положения и без изменений в конструкции жалюзи.
На каждый балкон понадобится отдельный Ардуино, балконы ведь в разных комнатах ?
Да, жалюзи такие же, балконы смежные, отделены перегородкой.
Если можно дай подробности
Подробности дам завтра, фотографии конструкции на карточке, а кардридер на работе. Пока что покажу схему:
Arduino Nano, два мотора 28BYJ-48 (не показаны), два ULN2003, WiFi-адаптер ESP8266, пищалка, две кнопки и фоторезистор.
Если балконы смежные и разместить Ардуино возле перегородки, то думаю можно будет и одним обойтись. Брать для такой схемы нужно Мегу, 4 пина * 8 моторов = 32 пина на подключение драйверов моторов.
Ваууу)) чувствую я здесь надолго))
А uln 2003 нельзя между собой в параллель поставить, Только с каждого на Ардуино?
Вай фай зачем? С телефона жалюзи управляешь?
У нас в универе был предмет схемотехника, и что то мне подсказывает эти знания мне надо освежить
Из двух вариантов:
1. Одна Мега 2560.
2. Две Мини 328 (или две нано).
Для управления двумя разными устройствами я бы выбрал второй.
Продолжим. Немного "конструктива":
Общий вид. Сверху слева, там, где открывается в режиме проветривания створка окна, шлейф делает гибкую петлю (забыл сфотографировать это место сбоку), чтобы давать возможность верхней части створки окна отходить от рамы.
Самое важное фото. Наконечник с "палки" снят, просверлены отверстия в обеих деталях, дальше всё видно. Шаговик двумя шурупами прикручен к коробочке, коробочка приклеена к раме окна. Короба, кстати, тоже все приклеены.
От левого мотора вниз идёт ещё кусок короба. На конце его кнопка, чуть выше - ещё одна поменьше, она для неполного прикрытия жалюзи (в качестве защиты от солнца).
Чёрная коробка смонтирована на двух металлических уголках, прикреплённых шурупами к раме. Левый уголок видно. Из коробки выходит в сторону стекла фоторезистор на двух проводках. Проводки пропущены в зазор между жалюзи и рамой окна. Питание приходит сверху, с потолка. Рядом с разъёмом питания есть отверстие для подключения кабеля USB к Ардуино Nano для перепрошивок. Разъём питания выведен наружу специально, чтобы при перепрошивке иметь возможность его отключать.
Пока всё, позже подготовлю и выложу скетч.
Отлично! Жду скетч
Угощайтесь :
// // Я выпилил из скетча всё, что касается ESP8266 по одной лишь причине: у меня обмен данными // ведётся с помощью широковещательных UDP-пакетов, что далеко от общепринятой практики и // вряд ли кому пригодится. // // Управление жалюзи оставлено только с кнопок и закрытие по низкому уровню освещённости, // для краткости и лёгкости понимания скетча. // // (**ЗАМЕЧАНИЕ 1**) Ещё отмечу, что первоначально планировалось сделать независимое управление каждой жалюзи. // Поэтому в скетче присутствуют парные однотипные переменные и функции. // Опыт эксплуатации показал, что совсем оно и не нужно, поэтому некоторые моменты в скетче // теперь выглядят неоптимальными. Ну, что имею, то и выкладываю... // //#define DEBUG 1 #include <EEPROM.h> //// PINS #define BUTTON_PIN A0 // analog pin connected to open/close pushbutton #define BUTTON80_PIN A2 // analog pin connected to open 80% pushbutton #define LIGHTSENSOR_PIN A7 // analog pin connected to photoresistor #define ULN1_IN1 2 // digital pin connected to ULN1 IN1 #define ULN1_IN2 3 // digital pin connected to ULN1 IN2 #define ULN1_IN3 4 // digital pin connected to ULN1 IN3 #define ULN1_IN4 5 // digital pin connected to ULN1 IN4 #define ULN2_IN1 6 // digital pin connected to ULN2 IN1 #define ULN2_IN2 7 // digital pin connected to ULN2 IN2 #define ULN2_IN3 8 // digital pin connected to ULN2 IN3 #define ULN2_IN4 9 // digital pin connected to ULN2 IN4 #define SOUND_PIN 10 // digital pin connected to sound buzzer //// VARIABLES // timer unsigned long Every1MinuteTimer; // steppers // Это количество шагов для полного цикла полностью закрыто - открыто - крутим дальше в том же направлении и опять полностью закрыто. // То есть у меня полностью открыто (плоскость жалюзи горизонтальна) соответствует 50-ти процентам полного цикла. unsigned int StepsFor100Percent = 32768; // 360 deg = 4096 steps. // Это две очень важных переменные (могла бы быть и одна, см. **ЗАМЕЧАНИЕ 1**. // Поскольку у меня нет датчиков положения жалюзи, то именно эти переменные с точки зрения скетча // - единственная информация о положении жалюзи. Позднее, в setup(), их значения будут прочитаны из EEPROM. // Ещё дальше мы будем после каждого поворота жалюзи сохранять их в EEPROM. unsigned int Stepper1CurrentSteps = 0; unsigned int Stepper2CurrentSteps = 0; // Это, скорее, для удобства. float Stepper1CurrentPercent = 0; // 0...100. 0%-closed, 50%-open, 100%-closed. float Stepper2CurrentPercent = 0; // 0...100. 0%-closed, 50%-open, 100%-closed. // Это для внутреннего, так сказать, использования функциями DoOneStep1 и DoOneStep2 int Stepper1CurrentStep = 0; int Stepper2CurrentStep = 0; // buttons #define BUTTON_PIN_LEVEL 500 boolean ButtonState = false; boolean Button80State = false; // photoresistor int LightLevelToClose = 600; int LightLevel = 0; const int AvgLightLevelsSize = 10; float AvgLightLevels[AvgLightLevelsSize]; int AvgLightLevelCounter = 0; void setup() { pinMode(SOUND_PIN, OUTPUT); digitalWrite(BUTTON_PIN, HIGH); // set pullup on analog pin digitalWrite(BUTTON80_PIN, HIGH); // set pullup on analog pin pinMode(ULN1_IN1, OUTPUT); pinMode(ULN1_IN2, OUTPUT); pinMode(ULN1_IN3, OUTPUT); pinMode(ULN1_IN4, OUTPUT); pinMode(ULN2_IN1, OUTPUT); pinMode(ULN2_IN2, OUTPUT); pinMode(ULN2_IN3, OUTPUT); pinMode(ULN2_IN4, OUTPUT); #ifdef DEBUG Serial.begin(9600); Serial.println(""); Serial.println("SmartJalousie. 2015."); #endif PlayTone(SOUND_PIN,4000,50); delay(50); PlayTone(SOUND_PIN,3000,50); delay(50); PlayTone(SOUND_PIN,4000,50); // Читаем две очень важные переменные. Stepper1CurrentSteps = EEPROMReadInt(20); // address 20 Stepper2CurrentSteps = EEPROMReadInt(24); // address 24 // Это для самого первого чтения EEPROM. if ( (Stepper1CurrentSteps < 0) || (Stepper1CurrentSteps > StepsFor100Percent) ) { Stepper1CurrentSteps = 0; } if ( (Stepper2CurrentSteps < 0) || (Stepper2CurrentSteps > StepsFor100Percent) ) { Stepper2CurrentSteps = 0; } // Вычисляем. Stepper1CurrentPercent = (float(Stepper1CurrentSteps) / (float(StepsFor100Percent)/100)); Stepper2CurrentPercent = (float(Stepper2CurrentSteps) / (float(StepsFor100Percent)/100)); #ifdef DEBUG Serial.print("Stepper1CurrentSteps="); Serial.println(Stepper1CurrentSteps); Serial.print("Stepper2CurrentSteps="); Serial.println(Stepper2CurrentSteps); Serial.print("Stepper1CurrentPercent="); Serial.println(Stepper1CurrentPercent); Serial.print("Stepper2CurrentPercent="); Serial.println(Stepper2CurrentPercent); #endif // Инициализируем считалку уровня освещенности. int jj; for (jj = 0; (jj < AvgLightLevelsSize); jj++) { AvgLightLevels[jj] = 0; } for (jj = 0; (jj < AvgLightLevelsSize); jj++) { CalcAvgLightLevel(); } // Инициализируем её таймер. Every1MinuteTimer = millis(); } void loop() { ////////////////// Read buttons ButtonState = (analogRead(BUTTON_PIN) < BUTTON_PIN_LEVEL ); Button80State = (analogRead(BUTTON80_PIN) < BUTTON_PIN_LEVEL ); ////////// CHECK TO OPEN/CLOSE (ButtonState) if (ButtonState) { // Ещё раз напомню, у меня полностью открыто (плоскость жалюзи горизонтальна) соответствует 50-ти процентам. if ( (int(Stepper1CurrentPercent) <= 40) || (int(Stepper1CurrentPercent) >= 60) ) { rotate(50); } else { rotate(100); } ButtonState = false; delay(250); // debounce } ////////// CHECK TO 80% OPEN (Button80State) if (Button80State) { if ( (int(Stepper1CurrentPercent) <= 75) || (int(Stepper1CurrentPercent) >= 85) ) { rotate(80); } else { rotate(50); } Button80State = false; delay(250); // debounce } ////////// CHECK TO CLOSE (LightLevel) if ( LightLevel > LightLevelToClose ) { if ( (int(Stepper1CurrentPercent) > 10) && (int(Stepper1CurrentPercent) < 90) ) { rotate(100); } } //////////////////// RUN EVERY 1 MINUTE if ( (millis() - Every1MinuteTimer) > 60000 ) { CalcAvgLightLevel(); Every1MinuteTimer = millis(); } } //////////////////////////////////////////////////////////////// F U N C T I O N S void PlayTone(byte tonePin, int frequency, int duration) { int period = 1000000L / frequency; int pulse = period / 2; for (long i = 0; i < duration * 1000L; i += period) { digitalWrite(tonePin, HIGH); delayMicroseconds(pulse); digitalWrite(tonePin, LOW); delayMicroseconds(pulse); } } void CalcAvgLightLevel() { int tempCounter; int tempFound = 0; float tempTotal = 0; AvgLightLevels[AvgLightLevelCounter] = analogRead(LIGHTSENSOR_PIN); AvgLightLevelCounter++; for (tempCounter = 0; (tempCounter < AvgLightLevelsSize); tempCounter++) { if ( AvgLightLevels[tempCounter] > 0 ) { tempTotal += AvgLightLevels[tempCounter]; tempFound ++; } } if ( tempFound > 0 ) { if ( tempTotal == 0 ) { tempTotal = 1; } LightLevel = tempTotal / tempFound; } if ( AvgLightLevelCounter >= AvgLightLevelsSize ) { AvgLightLevelCounter = 0; } } void EEPROMWriteInt(int p_address, int p_value) // This function will write a 2 byte integer to the eeprom at the specified address and address + 1 { byte lowByte = ((p_value >> 0) & 0xFF); byte highByte = ((p_value >> 8) & 0xFF); EEPROM.write(p_address, lowByte); EEPROM.write(p_address + 1, highByte); } unsigned int EEPROMReadInt(int p_address) // This function will read a 2 byte integer from the eeprom at the specified address and address + 1 { byte lowByte = EEPROM.read(p_address); byte highByte = EEPROM.read(p_address + 1); if ( (((lowByte << 0) & 0xFF) == 0xFF) && (((highByte << 0) & 0xFF) == 0xFF) ) return 0; else return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00); } // Это были вспомогательные функции, а вот теперь идут вещи поважнее. // // Функция, так сказать, верхнего уровня. Управляет обеими жалюзи вместе, вызывая по очереди две функции "нижнего уровня". // Почему так, см. **ЗАМЕЧАНИЕ 1** // void rotate(int percent) // 0...100. 0%-alternate closed, 50%-open, 100%-normally closed. { PlayTone(SOUND_PIN,4000,50); unsigned long last_time; // для управления скоростью вращения движков. unsigned long currentMicros; // для управления скоростью вращения движков. boolean step_direct; float after; unsigned int steps_num = 0; if ( percent > 0 ) { after = (float(StepsFor100Percent)/100) * float(percent); if ( Stepper1CurrentSteps < after ) { step_direct = true; steps_num = after - Stepper1CurrentSteps; } else { step_direct = false; steps_num = Stepper1CurrentSteps - after; } #ifdef DEBUG Serial.print("before="); Serial.print((float(Stepper1CurrentSteps) / (float(StepsFor100Percent)/100))); Serial.print("% after="); Serial.print(percent); Serial.print("% steps="); Serial.println(steps_num); #endif } else { step_direct = false; steps_num = int(Stepper1CurrentSteps); } if ( steps_num < 1 ) { return; } last_time = micros() - 5000; while ( steps_num > 0 ) { currentMicros = micros(); if ( (currentMicros - last_time) >= 1000 ) // это я искусственно снижаю скорость вращения движков. { DoOneStep1(step_direct); DoOneStep2(step_direct); last_time = micros(); steps_num--; } } // while // По окончании вращения "обесточиваем" движки. digitalWrite(ULN1_IN1, LOW); digitalWrite(ULN1_IN2, LOW); digitalWrite(ULN1_IN3, LOW); digitalWrite(ULN1_IN4, LOW); digitalWrite(ULN2_IN1, LOW); digitalWrite(ULN2_IN2, LOW); digitalWrite(ULN2_IN3, LOW); digitalWrite(ULN2_IN4, LOW); // Сохраняемся. EEPROMWriteInt(20,Stepper1CurrentSteps); EEPROMWriteInt(24,Stepper2CurrentSteps); } // // Ну а здесь две функции "нижнего уровня", непосредственно вращающие движки. // Сделано неоптимально, можно было бы обойтись одной универсальной. // Почему так, опять же, см. **ЗАМЕЧАНИЕ 1** // void DoOneStep1(boolean direct) // true = open = clockwise // false = close = anticlockwise { switch(Stepper1CurrentStep) { case 0: digitalWrite(ULN1_IN1, LOW); digitalWrite(ULN1_IN2, LOW); digitalWrite(ULN1_IN3, LOW); digitalWrite(ULN1_IN4, HIGH); break; case 1: digitalWrite(ULN1_IN1, LOW); digitalWrite(ULN1_IN2, LOW); digitalWrite(ULN1_IN3, HIGH); digitalWrite(ULN1_IN4, HIGH); break; case 2: digitalWrite(ULN1_IN1, LOW); digitalWrite(ULN1_IN2, LOW); digitalWrite(ULN1_IN3, HIGH); digitalWrite(ULN1_IN4, LOW); break; case 3: digitalWrite(ULN1_IN1, LOW); digitalWrite(ULN1_IN2, HIGH); digitalWrite(ULN1_IN3, HIGH); digitalWrite(ULN1_IN4, LOW); break; case 4: digitalWrite(ULN1_IN1, LOW); digitalWrite(ULN1_IN2, HIGH); digitalWrite(ULN1_IN3, LOW); digitalWrite(ULN1_IN4, LOW); break; case 5: digitalWrite(ULN1_IN1, HIGH); digitalWrite(ULN1_IN2, HIGH); digitalWrite(ULN1_IN3, LOW); digitalWrite(ULN1_IN4, LOW); break; case 6: digitalWrite(ULN1_IN1, HIGH); digitalWrite(ULN1_IN2, LOW); digitalWrite(ULN1_IN3, LOW); digitalWrite(ULN1_IN4, LOW); break; case 7: digitalWrite(ULN1_IN1, HIGH); digitalWrite(ULN1_IN2, LOW); digitalWrite(ULN1_IN3, LOW); digitalWrite(ULN1_IN4, HIGH); break; default: digitalWrite(ULN1_IN1, LOW); digitalWrite(ULN1_IN2, LOW); digitalWrite(ULN1_IN3, LOW); digitalWrite(ULN1_IN4, LOW); break; } if ( direct ) { Stepper1CurrentStep--; Stepper1CurrentSteps++; Stepper1CurrentPercent = (float(Stepper1CurrentSteps) / (float(StepsFor100Percent)/100)); } else { Stepper1CurrentStep++; Stepper1CurrentSteps--; Stepper1CurrentPercent = (float(Stepper1CurrentSteps) / (float(StepsFor100Percent)/100)); } if ( Stepper1CurrentSteps < 0 ) { Stepper1CurrentSteps = 0; Stepper1CurrentPercent = 0; } if ( Stepper1CurrentSteps > StepsFor100Percent ) { Stepper1CurrentSteps = StepsFor100Percent; Stepper1CurrentPercent = (float(Stepper1CurrentSteps) / (float(StepsFor100Percent)/100)); } if ( Stepper1CurrentStep > 7 ) { Stepper1CurrentStep = 0; } if ( Stepper1CurrentStep < 0 ) { Stepper1CurrentStep = 7; } } void DoOneStep2(boolean direct) // true = open = clockwise // false = close = anticlockwise { switch(Stepper2CurrentStep) { case 0: digitalWrite(ULN2_IN1, LOW); digitalWrite(ULN2_IN2, LOW); digitalWrite(ULN2_IN3, LOW); digitalWrite(ULN2_IN4, HIGH); break; case 1: digitalWrite(ULN2_IN1, LOW); digitalWrite(ULN2_IN2, LOW); digitalWrite(ULN2_IN3, HIGH); digitalWrite(ULN2_IN4, HIGH); break; case 2: digitalWrite(ULN2_IN1, LOW); digitalWrite(ULN2_IN2, LOW); digitalWrite(ULN2_IN3, HIGH); digitalWrite(ULN2_IN4, LOW); break; case 3: digitalWrite(ULN2_IN1, LOW); digitalWrite(ULN2_IN2, HIGH); digitalWrite(ULN2_IN3, HIGH); digitalWrite(ULN2_IN4, LOW); break; case 4: digitalWrite(ULN2_IN1, LOW); digitalWrite(ULN2_IN2, HIGH); digitalWrite(ULN2_IN3, LOW); digitalWrite(ULN2_IN4, LOW); break; case 5: digitalWrite(ULN2_IN1, HIGH); digitalWrite(ULN2_IN2, HIGH); digitalWrite(ULN2_IN3, LOW); digitalWrite(ULN2_IN4, LOW); break; case 6: digitalWrite(ULN2_IN1, HIGH); digitalWrite(ULN2_IN2, LOW); digitalWrite(ULN2_IN3, LOW); digitalWrite(ULN2_IN4, LOW); break; case 7: digitalWrite(ULN2_IN1, HIGH); digitalWrite(ULN2_IN2, LOW); digitalWrite(ULN2_IN3, LOW); digitalWrite(ULN2_IN4, HIGH); break; default: digitalWrite(ULN2_IN1, LOW); digitalWrite(ULN2_IN2, LOW); digitalWrite(ULN2_IN3, LOW); digitalWrite(ULN2_IN4, LOW); break; } if ( direct ) { Stepper2CurrentStep--; Stepper2CurrentSteps++; Stepper2CurrentPercent = (float(Stepper2CurrentSteps) / (float(StepsFor100Percent)/100)); } else { Stepper2CurrentStep++; Stepper2CurrentSteps--; Stepper2CurrentPercent = (float(Stepper2CurrentSteps) / (float(StepsFor100Percent)/100)); } if ( Stepper2CurrentSteps < 0 ) { Stepper2CurrentSteps = 0; Stepper2CurrentPercent = 0; } if ( Stepper2CurrentSteps > StepsFor100Percent ) { Stepper2CurrentSteps = StepsFor100Percent; Stepper2CurrentPercent = (float(Stepper2CurrentSteps) / (float(StepsFor100Percent)/100)); } if ( Stepper2CurrentStep > 7 ) { Stepper2CurrentStep = 0; } if ( Stepper2CurrentStep < 0 ) { Stepper2CurrentStep = 7; } }Вся эта штуковина работает уже с полгода, калибровку положения жалюзи делал один раз. Снятию жалюзи для помывки не мешает.
Будут вопросы - вопрошайте )).
спасибо, буду разбираться и внедрять)
Здесь у вас есть типа виртуального стенда, где можно собрать схему, залить скетч и проверить?
Здесь у вас есть типа виртуального стенда, где можно собрать схему, залить скетч и проверить?
Нет, это фантастика.
Будешь показывать код, см это
http://arduino.ru/forum/obshchii/vstavka-programmnogo-koda-v-temukomment...
Вот здесь понятно про паралельное подключение шаговиков - http://www.cnc-club.ru/forum/viewtopic.php?f=3&t=3252
Wi-Fi у меня - канал для связи с прочими компонентами квартиры, в том числе и с телефона управлять можно (но эта доблесть к "умной" квартире как раз не относится, "умность" в том, что она сама без меня управляет).