Страницы

воскресенье, 3 июня 2018 г.

Страшная снаружи, добрая внутри

После установки натяжного потолка в одной из комнат нам пришлось распрощаться с 5-рожковой люстрой 5x100 Вт и диммером "Сапфир" c плавным управлением яркостью с любого пульта ДУ. Лампы накаливания сильно греются и потолку от них нехорошо, поэтому мы начали искать замещающее решение с использованием светодиодных ламп. Света в сумме хотелось не меньше, такого же лампово-теплого по спектру и по-прежнему подвластного пульту.
Оставлю за кадром решения, которые мы рассматривали, но в итоге направления осталось два:
  • диммер для светодиодных ламп и использование недешевых диммируемых ламп
  • некий китайский контроллер, монтируемый в люстру и позволяющий последовательно зажигать до 3 каналов, повторным нажатием выключателя. Главный его недостаток - он не помнит установленным последним уровень освешенности. Достоинство решения - использование любых светодиодных ламп.
Параллельно с этим руки зачесались спаять что-нибудь самодельно-кустарное, получив все желаемые функции быстро и уже начать пользоваться люстрой, продолжая поиск более красивого и функционального аналога.

Итак, для очередного проекта выходного дня нам понадобились:
  • 4 настенных светильника для ванной комнаты. Для самоделки удобны тем, что будучи соединенными попарно винтами образуют корпуса для электроники между подставками
  • 4 светодиодные лампы с патроном e27 на 5, 10, 20 и 20 Вт
  • алюминиевая трубка, алюминиевая полоса, винты и гайки м4
  • клеммы винтовые 2,5мм, провода
  • зарядка от телефона USB, которую мы лишили корпуса
  • arduino nano
  • 4 реле модуля, управляемых 5В логикой
  • ИК-приемник VS1838B
  • микродинамик

Собранная люстра обладает следующими функциями:
  • включение-выключение обычным клавишным выключателем - вся электроника "нормально-разомкнута"
  • управление с любого пульта ДУ (используется 2 кнопки - прибавить яркость, убавить яркость)
  • 11 уровней яркости: 5Вт, 10Вт, 15Вт(5+10), 20Вт, 25Вт(20+5), 30 Вт(20+10), 35Вт(20+10+5), 40Вт(20+20), 45Вт(20+20+5), 50Вт(20+20+10), 55Вт(20+20+10+5)
  • "плавный старт" - если в люстре в последний раз были включены, например, 3 лампы, то при повторном включении они будут последовательно включены с интервалом в 2 секунды - по нарастанию их мощности - чтобы не ослепить входящего в комнату человека
  • обучение под любой пульт ДУ - троекратное нажатие последовательности любых трех кнопок на любом пульте вводит люстру в режим обучения - издается звуковой сигнал. В режиме обучения ожидается нажатие любых двух кнопок - они будут запомнены как новые кнопки управления яркостью
  • запоминание установленного уровня яркости в энергонезависимой памяти через 30 сек после последней регулировки
  • автовыключение света через 6 часов
  • звуковое подтверждение всех операций по регулировке, обучению, сохранении настроек

Код программы выглядит так:


#include <IRremote.h> 
#include <EEPROM.h>

int relay1 = 5; // 5W
int relay2 = 6; // 10W
int relay3 = 7; // 20W
int relay4 = 8; // 20W

int buzzer = 9; // speaker or led

unsigned long history[9];

unsigned long power_up = 0xFFA05F;
unsigned long power_down = 0xFF20DF;

unsigned long timer = millis();
boolean not_rec = false;

int receiverPin = 11; 
IRrecv irrecv(receiverPin);
  
decode_results results;

byte power;
int mem_addr = 0;

long EEPROMReadlong(long address) {
  long four = EEPROM.read(address);
  long three = EEPROM.read(address + 1);
  long two = EEPROM.read(address + 2);
  long one = EEPROM.read(address + 3);
  
  return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}

void EEPROMWritelong(int address, long value) {
  byte four = (value & 0xFF);
  byte three = ((value >> 8) & 0xFF);
  byte two = ((value >> 16) & 0xFF);
  byte one = ((value >> 24) & 0xFF);
  
  EEPROM.write(address, four);
  EEPROM.write(address + 1, three);
  EEPROM.write(address + 2, two);
  EEPROM.write(address + 3, one);
}

void set_power(int p) {
  timer = millis();
  not_rec = true;
  Serial.print("Power: ");
  Serial.println(p);
  boolean r1, r2, r3, r4;
  if (p >= 40) {r4 = LOW; p -= 20;} else r4 = HIGH;
  if (p >= 20) {r3 = LOW; p -= 20;} else r3 = HIGH;
  if (p >= 10) {r2 = LOW; p -= 10;} else r2 = HIGH;
  if (p >= 5)  {r1 = LOW; p -= 5;} else r1 = HIGH;
  
  digitalWrite(relay1, r1);
  digitalWrite(relay2, r2);
  digitalWrite(relay3, r3);
  digitalWrite(relay4, r4);  
  
  Serial.print("Relay 5W: ");
  Serial.println(!r1);
  Serial.print("Relay 10W: ");
  Serial.println(!r2);
  Serial.print("Relay 20W: ");
  Serial.println(!r3);
  Serial.print("Relay 20W: ");
  Serial.println(!r4);  
}

void init_power(int p) {
  
  int init_delay = 2000;
  Serial.print("Init power: ");
  Serial.println(p);
  boolean r1, r2, r3, r4;
  if (p >= 40) {r4 = LOW; p -= 20;} else r4 = HIGH;
  if (p >= 20) {r3 = LOW; p -= 20;} else r3 = HIGH;
  if (p >= 10) {r2 = LOW; p -= 10;} else r2 = HIGH;
  if (p >= 5)  {r1 = LOW; p -= 5;} else r1 = HIGH;
  
  boolean light = false;
  digitalWrite(relay1, r1);
  if (!r1) light = true;
  Serial.print("Relay 5W: ");
  Serial.println(!r1);
  if (light && !r2) delay(init_delay);
  digitalWrite(relay2, r2);
  if (!r2) light = true;
  Serial.print("Relay 10W: ");
  Serial.println(!r2);
  if (light && !r3) delay(init_delay);
  digitalWrite(relay3, r3);
  if (!r3) light = true;
  Serial.print("Relay 20W: ");
  Serial.println(!r3);
  if (light && !r4) delay(init_delay);
  digitalWrite(relay4, r4);  
  Serial.print("Relay 20W: ");
  Serial.println(!r4);  
}


void learn (unsigned long code) {
  delay(100);
  for (int i=0; i<8; i++) history[i] = history[i+1];
  history[8] = code;
  if (history[0] == history[3] && history[0] == history[6] &&
      history[1] == history[4] && history[1] == history[7] &&
      history[2] == history[5] && history[2] == history[8] &&
      history[0] != history[1] && history[1] != history[2]) {
      Serial.println("Learn mode enabled");  
      tone(buzzer, 880, 200);      
      delay(1000);
      irrecv.resume();
      
      while (!irrecv.decode(&results));
      tone(buzzer, 523, 200);
      power_up = results.value;
      delay(100);      
      irrecv.resume();
            
      delay(1000);

      while (!irrecv.decode(&results));
      tone(buzzer, 440, 200);
      power_down = results.value;
      delay(100);      
      irrecv.resume();

      EEPROMWritelong(mem_addr+1,power_up);
      EEPROMWritelong(mem_addr+5,power_down);      
  }
}  
  
void setup() {
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);

  digitalWrite(relay1, HIGH);
  digitalWrite(relay2, HIGH);
  digitalWrite(relay3, HIGH);
  digitalWrite(relay4, HIGH);

  pinMode(buzzer, OUTPUT);   
  tone(buzzer, 659, 500);
  
  Serial.begin(9600);
  
  power = EEPROM.read(mem_addr);
  init_power(int(power));
  
  irrecv.enableIRIn();
  for (int i=0; i<9; i++) history[i] = i;

  power_up = EEPROMReadlong(mem_addr+1);
  power_down = EEPROMReadlong(mem_addr+5);
}

void loop() {
  if (irrecv.decode(&results)) { 
    if(results.value == power_up){
      if (power < 55) {
        tone(buzzer, 523, 200);
        power += 5;
        set_power(int(power));
      }
      else {
        tone(buzzer, 329, 200);
      }  
    }
    if(results.value == power_down){
      if (power > 5) {
        tone(buzzer, 440, 200);
        power -= 5;
        set_power(int(power));
      }
      else {
        tone(buzzer, 329, 200);
      }   
    }
    learn (results.value);
    irrecv.resume();
  }
  if (not_rec && millis()-timer > 30000) {
    tone(buzzer, 262, 100);
    EEPROM.write(mem_addr, power); 
    Serial.println("Save to EEPROM: ");
    Serial.println(power);
    timer = millis();
    not_rec = false;  
  } 
  if (millis()-timer > 6*60*60*1000) {
    power = 0;
    set_power(int(power));
    tone(buzzer, 659, 1000);
    delay(1000);
    tone(buzzer, 440, 1000);
    delay(1000);       
  }  
}





Комментариев нет:

Отправить комментарий