Очередная LED лампа на ардуино

StasOn
Offline
Зарегистрирован: 10.05.2017
Добрый день!
Появилась у меня задумка сделать светильник из битой матрицы от ноута. По яркости они получаются вроде ничего так. К тому-же можно соединить несколько штук, и получить неплохую лампу на потолок. Покурив я узнал, что яркость подсветки ноутбука регулируется напряжением поданным на один из контактов платы подсветки, в моем случае это от 0 до 3.3 в, а сама матрица питается напряжением 12 в. На плате управления матрицей уже есть драйвер и ничего доделывать не нужно. 
В общем, если прикрутить к матрице ардуину с маленькой программой, можно управлять яркостью матрицы хоть по блютуз, хоть с кнопок, хоть потенциометром.
Для начала я решил реализовать управление через терминал (в т.ч. блютуз). Сложностей не возникло, кроме функции плавного включения подсветки, но я разобрался (как смог).
Далее, я решил написать функцию ошибки, чтобы в случае неверно введенных данных, матрица моргала несколько раз. Функция была написана, хотя на мой взгляд как-то не оч.
Следующим делом нужно прикрутить кнопки, чтобы управлять яркостью без блютуза, включать и выключать свет. Я решил воспользоваться готовым решением - Титановый Велосипед для кнопки, автору моя благодарность за работу.
Кнопки будут следующие - +, - уровень яркости, и вкл/выкл. Простое нажатие увеличивает яркость на 5, а длинное на 15 (яркость регулируется от 0 до 180, т.е. от 0 до 3.3 в по моим расчетам), соответственно и уменьшение яркости. К кнопке вкл/выкл хочется привязать кроме обычной функции еще какие-нибудь эффекты, но это потом.
Немного напрягает, что по ходу написания скетча, многие действия нужно выводить в отдельные функции, заводить для них переменные и т.д. Получается, пишешь код 10 минут, а переделываешь 30. Иначе код разрастется от схожих функций, потом хрен че поймешь.
После кнопок, я решил что неплохо бы было включать и выключать свет на том уровне яркости, на котором она была в момент выключения, значит нужно использовать Епром. Впрочем, тут сложностей не возникло.
Тут я вспомнил, что нужно прикрутить маленький светик, для индикации состояния и питания прибора, и посадил его на д4. И тут я запутался. Соорудив простую конструкцию для включения светодиода, и вставив ее в Loop я обнаружил, что программа перестала нормально работать. Перепробовав много разных вариантов, перечитав много всякого, и уже собираясь бежать за помощью, я решил немного отдохнуть и зарубился в танки. После этого все встало на свои места - я вместо  if (sensorValueNew == 0) написал if (sensorValueNew = 0)... ППц...
Итак, придумываем далее.
Хочется, чтобы контроллер уходил в спячку при нажатии кнопки вкл/выкл, но при этом светил диод д4, и выходил из нее только кнопкой вкл/выкл, ну и в перспективе еще с терминала.
Путем копания интернета нашел способ переводить контроллер в сон, и успешно его слизал. Одна проблема, уходит в сон он нормально, а включается только после 2-х нажатий на вкл/выкл. В программу необходимо добавить флаг выхода из сна, и процедуру обновления данных. В общем, все нормально, флаг есть, вкл и выкл как задумано. 
Тут пришла ко мне мысль, что кнопками +,- не стоит убавлять яркость до нуля, т.к. контроллер не сможет выключится. Нужно ограничить нижний уровень на 5 %. Да и вообще, перевести всю эту бодягу на проценты, а то непонятно как-то. 
В общем вот, строго не судите, програмер я так себе..
#include <Button.h>
Button t;
#define long_3_Time 200 //время авто нажатий 
long timerButton_2 = 0; //сброс времени
#include <EEPROM.h>
#include <avr/sleep.h>
unsigned long tm = 0;
#define INTERRUPT_PIN  2 // Для Arduino UNO можно использовать 2 или 3 пин
#define TIME_BEFORE_SLEEP 10000 // Время до ухода в сон 10с
byte sensorValueCur = 0;  // Переменная для хранения текущих показаний
byte sensorValueNew = 0;  // Переменная для хранения новых показаний
byte sensorValueBut = 0; // Переменная для хранения вводимых значений с кнопок
boolean wakeUp = false; // флаг выхода из сна

void setup() {
  Serial.begin(9600);
  pinMode(3, OUTPUT); // тут подключена панель
  pinMode(4, OUTPUT); // тут подключен индикатор состояния
  ChangePWM ();
  t.NO();      //кнопка с нормально разомкнутыми контактами
  t.pullUp();  //подтяжка пинов кнопок к Vcc
  t.duration_bounce       (  50);//настройка длительности фильтра дребезга кнопки
  t.duration_click_Db     ( 250);//настройка длительности двойного щелчка кнопки
  t.duration_inactivity_Up(5000);//настройка длительности таймера неактивности ненажатой кнопки
  t.duration_inactivity_Dn(5000);//настройка длительности таймера неактивности нажатой кнопки
  t.duration_press        ( 750);//настройка длительности длинного нажатия кнопки
  t.button( 7, 8, 2);
  PowerOn();
}

void ErrorPWM() { //функция сигнализации ошибки
  static unsigned long lastTime = 0; //переменная для хранения текущего времени
  sensorValueNew = sensorValueCur;
  sensorValueCur = 0;
  for (int i = 0; i <= 2; i = ++i) {
    while (sensorValueCur < 180) { //пока текущее значение уровня меньше нового значения
      if (millis() - lastTime > 2) { //каждые 15 lastTime увеличиваем sensorValueCur на 1
        lastTime = millis();
        analogWrite(3, ++sensorValueCur);
      }
    }
    if (sensorValueCur == 180) analogWrite(3, sensorValueCur = 0);
  }
  analogWrite(3, sensorValueCur = sensorValueNew);
  Serial.println();
  Serial.println("Веедите от 0 до 180");
  OutSerial();
}

void ChangePWM() { //функция плавного изменения sensorValueCur
  static unsigned long lastTime = 0; //переменная для хранения текущего времени
  while (sensorValueCur > sensorValueNew) { //пока текущее значение уровня больше нового значения
    if (millis() - lastTime > 15) { //каждые 15 lastTime уменьшаем sensorValueCur на 1
      lastTime = millis();
      analogWrite(3, --sensorValueCur);
    }
  }
  while (sensorValueCur < sensorValueNew) { //пока текущее значение уровня меньше нового значения
    if (millis() - lastTime > 15) { //каждые 15 lastTime увеличиваем sensorValueCur на 1
      lastTime = millis();
      analogWrite(3, ++sensorValueCur);
      //      Serial.println(sensorValueCur);
    }
  }
}

void OutSerial() { //функция вывода sensorValueCur через Serial
  Serial.println();
  Serial.print("sensorValue = ");
  Serial.println(sensorValueCur);
}

void ChangeUP() { //функция обработки нажатия кнопки + pin8
  if ((sensorValueNew + sensorValueBut) > 180) {
    sensorValueNew = 180;
    ErrorPWM();
    ChangePWM();
  }
  else {
    sensorValueNew += sensorValueBut;
    ChangePWM();
  }
}

void ChangeDown() { //функция обработки нажатия кнопки - pin7
  if ((sensorValueNew - sensorValueBut) < 0) {
    ErrorPWM();
    sensorValueNew = 0;
    ChangePWM();
  }
  else {
    sensorValueNew -= sensorValueBut;
    ChangePWM();
  }
}

void PowerOn() Х //Обновление данных после включения или сна
EEPROM.get(0, sensorValueNew);
ChangePWM ();
}

void PowerOff() { //выключение контроллера, и выключение индикатора питания
  if (sensorValueNew == 0) {
    digitalWrite(4, HIGH);
    Serial.println(millis() - tm);
    Serial.println(TIME_BEFORE_SLEEP);
    wakeUp = !wakeUp;
    delay (500);
    // Проверим не пора ли спать
    if (millis() - tm > TIME_BEFORE_SLEEP) { // Пора
      //Разрешаем внешнее прерывание, которое разбудит МК
      attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), myISR, FALLING);
      set_sleep_mode(SLEEP_MODE_PWR_DOWN); // PowerDown - самый экономный режим
      sleep_mode(); // Переводим МК в сон
      // Проснулись, запрещаем обработку внешнего прерывания
      detachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN));
      tm = millis();
    }
  }
  else {
    digitalWrite(4, LOW);
    Serial.println("После sensorValue = ");
  }
}

void myISR() {

}

void loop() {
  if (wakeUp == true) { //флаг проснувшегося контроллера
    PowerOn();
    PowerOff();
    wakeUp = !wakeUp;
  }
  t.read();
  if (t.event_click_Dn (2) == 1) { //событие нажатия кнопки ON/OFF pin9
    if (sensorValueCur > 0) {
      EEPROM.put(0, sensorValueCur);
      sensorValueNew = 0;
      ChangePWM();
      PowerOff();
    }
    else {
      PowerOn();
      PowerOff();
    }
  }
  if (t.event_click_Dn (1) == 1) { //событие нажатия кнопки + pin8
    sensorValueBut = 5;
    ChangeUP();
  }
  if (t.event_click_Dn (0) == 1) { //событие нажатия кнопки - pin7
    sensorValueBut = 5;
    ChangeDown();
  }
  if (t.event_press_long (1) == 1) { //событие длинного нажатия кнопки + pin8
    sensorValueBut = 15;
    ChangeUP();
  }
  if (t.event_press_long (0) == 1) { //событие длинного нажатия кнопки - pin7
    sensorValueBut = 15;
    ChangeDown();
  }

  if (Serial.available()) // проверка наличия данных Serial
  {
    sensorValueNew = Serial.parseInt();

    if (sensorValueNew >= 0 && sensorValueNew <= 180)
    {
      PowerOff();
      ChangePWM ();
      OutSerial();
    }
    else
    {
      ErrorPWM();
    }
  }
}

Для тех, кто дочитал до конца, кто хочет покритиковать или оказать помощь:

- Осталось немного мусора в программе (позже почищу и приведу в порядок)

- Значения яркости так и остались натуральном виде (от 0 до 180), переделаю в проценты

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

- Хочу прикрутить какие-нибудь эффекты - типа плавное мерцание, стробоскоп, эффект неисправной лампы дневного света (как в халф лайв) и т.п. Если есть у кого, поделитесь.

- Хочу написать прогу для андроида, чтобы управлять по блютуз, но сам не шарю, разбираться пока некогда. Есть вопросы по передачи данных по блютуз (т.е. через serial), как лучше сделать? Мож протокол какой, и т.д.

Кому интересно, вот корявенькая схема:

Жду предложений и конструктивной критики.
andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Я что-то не понял, Вы пытаетесь управлять матрицей или лампой ее подсветки?

StasOn
Offline
Зарегистрирован: 10.05.2017

Ну уж точно не всей матрицей. Пытаюсь управлять подсветкой. Подсветка матриц давно делается на светодиодах, и драйвер установлен прямо на плате управления матрицей. Управлять уровнем яркости оч просто - напряжение (от 0 до 3.3в) поданное на один из контактов регулирует яркость. Матрица питается от напряжения 12в. Так что в плане компонентов все просто. Посмотреть как это выглядит можно сделав поиск в гугле типа "светильник из матрицы ноутбука".

Конечно можно было все реализовать простым тумблером, а для эффектов собрать мультивибратор, но мне захотелось размять мозги. Что из этого вышло, вы можете увидеть выше :-)

Думаю, перед тем как продолжить копать программу, сделаю корпус нормальный, платки для кнопок и светодиода.. А то напрягает куча кишков от отладочной платы.

Всех с наступающим! Успехов!

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

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

StasOn
Offline
Зарегистрирован: 10.05.2017

Ну, в названии темы я ничего про матрицу не писал, про банальное управление одним светодиодом частично согласен, но вот не смог я найти примеров, типа неисправной лампы, или плавно меняющейся самой по себе яркости. Для одного светодиода так заморачиваться было бы глупо, а для такого светильника вполне себе ничего, паять почти не надо.

К тому же, я написал, что это скорее разминка для мозгов, а тема, скорее дневник. По мере допиливания кода буду выкладывать.. Наверное ))

Ну и к тому-же, я претендую на некоторую оригинальность, подобных конструкций в инете мной обнаружено небыло ))))

AndreyD
AndreyD аватар
Offline
Зарегистрирован: 07.10.2018

StasOn пишет:

Ну, в названии темы я ничего про матрицу не писал, про банальное управление одним светодиодом частично согласен, но вот не смог я найти примеров, типа неисправной лампы, или плавно меняющейся самой по себе яркости. Для одного светодиода так заморачиваться было бы глупо, а для такого светильника вполне себе ничего, паять почти не надо.

Я в порядке изучения классов написал такой #1, может то, что вам нужно.

StasOn
Offline
Зарегистрирован: 10.05.2017

Спасибо!

Меня короч тут посещают мутные мысли, как-нибудь измерить эффект - типо 2 средние вспышки, затем короткое замыкание (учитываться не будет), затем хаотичное моргание с большой частотой, но заметной глазу, как в халф лайф, и в ужастиках довольно часто.. Как-то разобрать такое, и пристроить )) Думаю в одиночку я на такое не способен ( А эффект хорош!

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Нальешь, тебе все напишуть. 

AndreyD
AndreyD аватар
Offline
Зарегистрирован: 07.10.2018

StasOn пишет:

Спасибо!

Меня короч тут посещают мутные мысли, как-нибудь измерить эффект - типо 2 средние вспышки, затем короткое замыкание (учитываться не будет), затем хаотичное моргание с большой частотой, но заметной глазу, как в халф лайф, и в ужастиках довольно часто.. Как-то разобрать такое, и пристроить )) Думаю в одиночку я на такое не способен ( А эффект хорош!

Там ещё в моей теме "битовое моргание" #8.

AndreyD
AndreyD аватар
Offline
Зарегистрирован: 07.10.2018

DetSimen пишет:

Нальешь, тебе все напишуть. 

Извиняюсь за офтоп.

Сделал по одному рецепту подделку водки под коньяк, нормуль получилось. Берем 1,5 литра водки, добавляем две чайные ложки дубовой коры, одну чайную ложку черного натурально чая, 2-3 лаврового листа, 5-6 горошин чёрного перца. Ещё рекомендуют сушёной гвоздики, немного сахара и ванилина совсем чуть чуть, но я не добавлял. И настаивать 2-3 недели.

StasOn
Offline
Зарегистрирован: 10.05.2017

Извиняюсь за офтоп.

Конечно черти мои, и мне с ними жить! Предпочитаю знать что пью, и стараюсь пить то, что знаю.. Если на языке кака, то пить это нельзя, или можно, но немножко. Бывало принесут кампот кампотом, так после 05 встать не можешь )))

 

AndreyD
AndreyD аватар
Offline
Зарегистрирован: 07.10.2018

DetSimen пишет:

Нальешь, тебе все напишуть. 

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