кто на новенького

Арт
Offline
Зарегистрирован: 29.11.2015

Всем привет!!! Смотрел на жалюзи и прямо просится автоматизация процесса. Залез в инет нашёл разные темы и наткнулся на реализации моей затеи на ардуино, заказал с Китая эту игрушку, теперь я на форуме))
Я знаю, что примерно через год, я так же как и вы буду пинать новичков, но всё же.

По теме, у меня два балкона, на каждом по четыре жалюзи, хочу реализовать закрытие и открытие (без поднятия) с помощью двух ик-пультов на каждый балкон соответственно. Пока думаю организовать одновременное срабатывания и без датчика положения жалюзи

Реализацию вижу на шаговых моторах с шаговым драйвером.

Вопрос, можно ли использовать один ардуино, или на каждый балкон по штуке? Как запаралелить эти моторы, на каждый мотор тащить питание или их можно просто от одного источника запитать?

http://m.geektimes.ru/post/258112/

Araris
Offline
Зарегистрирован: 09.11.2012

Жалюзи у Вас такие, как в статье на Гиктаймс ?

Если да, то могу поделиться своим опытом автоматизации таких жалюзи на базе шаговиков 28BYJ-48 и драйверов на ULN2003, без датчика положения и без изменений в конструкции жалюзи.

На каждый балкон понадобится отдельный Ардуино, балконы ведь в разных комнатах ?

Арт
Offline
Зарегистрирован: 29.11.2015

Да, жалюзи такие же, балконы смежные, отделены перегородкой.

Если можно дай подробности

Araris
Offline
Зарегистрирован: 09.11.2012

Подробности дам завтра, фотографии конструкции на карточке, а кардридер на работе. Пока что покажу схему:

Arduino Nano, два мотора 28BYJ-48 (не показаны), два ULN2003, WiFi-адаптер ESP8266, пищалка, две кнопки и фоторезистор.

Если балконы смежные и разместить Ардуино возле перегородки, то думаю можно будет и одним обойтись. Брать для такой схемы нужно Мегу, 4 пина * 8 моторов = 32 пина на подключение драйверов моторов.

Арт
Offline
Зарегистрирован: 29.11.2015

Ваууу)) чувствую я здесь надолго))

А uln 2003 нельзя между собой в параллель поставить, Только с каждого на Ардуино?

Вай фай зачем? С телефона жалюзи управляешь?

Арт
Offline
Зарегистрирован: 29.11.2015

У нас в универе был предмет схемотехника, и что то мне подсказывает эти знания мне надо освежить

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Из двух вариантов:

1. Одна Мега 2560.

2. Две Мини 328 (или две нано).

Для управления двумя разными устройствами я бы выбрал второй.

Araris
Offline
Зарегистрирован: 09.11.2012

Продолжим. Немного "конструктива":

Общий вид. Сверху слева, там, где открывается в режиме проветривания створка окна, шлейф делает гибкую петлю (забыл сфотографировать это место сбоку), чтобы давать возможность верхней части створки окна отходить от рамы.

Самое важное фото. Наконечник с "палки" снят, просверлены отверстия в обеих деталях, дальше всё видно. Шаговик двумя шурупами прикручен к коробочке, коробочка приклеена к раме окна. Короба, кстати, тоже все приклеены.

От левого мотора вниз идёт ещё кусок короба. На конце его кнопка, чуть выше - ещё одна поменьше, она для неполного прикрытия жалюзи (в качестве защиты от солнца).

Чёрная коробка смонтирована на двух металлических уголках, прикреплённых шурупами к раме. Левый уголок видно. Из коробки выходит в сторону стекла фоторезистор на двух проводках. Проводки пропущены в зазор между жалюзи и рамой окна. Питание приходит сверху, с потолка. Рядом с разъёмом питания есть отверстие для подключения кабеля USB к Ардуино Nano для перепрошивок. Разъём питания выведен наружу специально, чтобы при перепрошивке иметь возможность его отключать.

Пока всё, позже подготовлю и выложу скетч.

Арт
Offline
Зарегистрирован: 29.11.2015

Отлично! Жду скетч

Araris
Offline
Зарегистрирован: 09.11.2012

Угощайтесь :

//
// Я выпилил из скетча всё, что касается 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; }
} 

Вся эта штуковина работает уже с полгода, калибровку положения жалюзи делал один раз. Снятию жалюзи для помывки не мешает.

Будут вопросы - вопрошайте )).

Арт
Offline
Зарегистрирован: 29.11.2015

спасибо, буду разбираться и внедрять)

Арт
Offline
Зарегистрирован: 29.11.2015

Здесь у вас есть типа виртуального стенда, где можно собрать схему, залить скетч и проверить?

Zaliv
Offline
Зарегистрирован: 05.03.2011

Арт пишет:

Здесь у вас есть типа виртуального стенда, где можно собрать схему, залить скетч и проверить?

Нет, это фантастика.

Будешь показывать код, см это
http://arduino.ru/forum/obshchii/vstavka-programmnogo-koda-v-temukomment...

 

Araris
Offline
Зарегистрирован: 09.11.2012

Арт пишет:
А uln 2003 нельзя между собой в параллель поставить, Только с каждого на Ардуино? Вай фай зачем? С телефона жалюзи управляешь?

Вот здесь понятно про паралельное подключение шаговиков  - http://www.cnc-club.ru/forum/viewtopic.php?f=3&t=3252

Wi-Fi у меня - канал для связи с прочими компонентами квартиры, в том числе и с телефона управлять можно (но эта доблесть к "умной" квартире как раз не относится, "умность" в том, что она сама без меня управляет).