Подсветка лестницы по датчику движения

Gortes
Gortes аватар
Offline
Зарегистрирован: 19.06.2022

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

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

#include <EEPROM.h>
#define pirA 7                      // PIR датчик
#define pirB 6
#define mosfet 9                    // мосфет
#define foto A0                     // фоторезистор
#define sw 3                        // кнопка
#define nightVAL 100                // порог включения
#define smooth 35                   // скорость затухания
byte pwm = 150;                     // максимальное значение яркости
int brightness = 0;                // текущее значение яркости
const long period = 10000;          // время свечения при срабатывании датчика
unsigned long last_press;
boolean butt_flag = 0;
boolean butt;
boolean led_flag = 0;
bool lightOff=true;
bool lightOn=false;
volatile int mode = 1;
int i;

void changeMode() {
  mode = mode + 1;
    if(mode > 2) {
      mode = 1;
   }
 }
 
void setup() {
Serial.begin(9600);
pinMode (pirB, INPUT);
pinMode (pirA, INPUT);
pinMode (mosfet, OUTPUT);
pinMode (sw, INPUT_PULLUP);

brightness = EEPROM.read( 0 );

TCCR1A = TCCR1A & 0xe0 | 1;
TCCR1B = TCCR1B & 0xe0 | 0x09;
}

void loop(){
  int foto = analogRead (0);
    if(Serial.available() > 0){
    int in_data = Serial.parseInt();
    Serial.println(foto);
      }
      if (mode == 1) {
        effect1();
      }
      if (mode == 2) {
        effect2();
      }  
} 

void effect1() {
  delay(100);
  if ((digitalRead(pirA) || (digitalRead(pirB)) == HIGH) && (analogRead(foto) < (nightVAL))){ 
    if (lightOff){
      for (i = 0; i <= brightness; i++){
        analogWrite(mosfet, i);
        delay(smooth);
      }
      lightOff = false;
      lightOn = true;
      delay(period);
    }
  }
  if (!lightOff){
         analogWrite(mosfet, brightness);
      for (i = brightness; i >= 0; i--){
        analogWrite(mosfet, i);
        delay(smooth);
      }
      lightOff = true;
      lightOn = false;
    }
  else {
    if (lightOn) {
      for (i = brightness; i >= 0; i--){
        analogWrite(mosfet, i);
        delay(smooth);
      }
      lightOff = true;
      lightOn = false;
    }
  }
}

void effect2() {
  delay(smooth);
    if (lightOff){
      for (i = 0; i < brightness; i++){
         analogWrite(mosfet, i);
         delay(smooth*1.5);
      }
    }
    lightOff = false;
    lightOn = true;
}

void yield() {
  butt = !digitalRead(sw);
    if (butt == 1 && butt_flag == 0 && millis() - last_press > 100) {
      butt_flag = 1;
      led_flag = !led_flag;
      last_press = millis();
    }
    if (butt == 0 && butt_flag == 1) {
      butt_flag = 0;
      mode = mode +1;
      if(mode > 2) {
        mode = 1;
      } 
    }
    if (butt == 1 && millis() - last_press > 700) {
      brightness = brightness + 10;
      analogWrite(mosfet, brightness);
      if (brightness > pwm) {
        brightness = 20; 
        EEPROM.write(0, brightness);   
      }
      last_press = millis();
    }
}
kalapanga
Offline
Зарегистрирован: 23.10.2016

1) Ваш код работает или нет? Если не работает или работает не так, нужно сделать, чтобы работал как надо. Это не оптимизация, это исправление ошибок.

2) Если код работает, то какую цель Вы преследуете, пытаясь его оптимизировать. Пример оптимизации: Вы хотите добавить в скетч ещё функционал, а он в память не влезает. А у Вас какая цель?

3) Что Вы понимаете под "Проблема в крайней кривизне кода и что самое главное делеи тормозящие." ?

4) Вы понимаете, что делают строки 37-38? Знаете, где и когда вызывается Ваша функция yield()? 

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

 

Gortes
Gortes аватар
Offline
Зарегистрирован: 19.06.2022

1)Код работает. Но в редких случаях не реагирует на кнопку, вот и грешу на делеи, но хотел победить эту проблему с помощью yield.

2)Хотел что бы бородатые дядьки тыкнули носом в ошибки, а в идеале показали как правильно.

3)Код был собран из кусков чужих наработок и уже шлифован под мои задачи.

4)37-38 мы увеличиваем частоту шим сигнала. Где и когда вызывается наша функция yield() не знаю. Уведел у Гайвера костыль и преминил.

5)Ну всетаки етот код мой хоть и +- понимаю что он делает. По поводу "использования конечных автоматов" спасибо.

kalapanga
Offline
Зарегистрирован: 23.10.2016

yield() вызывается именно внутри функции delay. Попробуйте ещё явный вызов yield(); в цикл loop добавить. Самой первой или самой последней строкой. Может это как раз и перекроет редкие случаи несрабатывания.

Gortes
Gortes аватар
Offline
Зарегистрирован: 19.06.2022

Доброго времени суток. Явный вызов yield(); в цикле loop помог. Немного почистил скетч, появилась новая задача. Как сделать так что бы в первом режиме работы(ждем сработки датчиков) если датчики сработали повторно до стадии выключения то пауза начала щитаться заново но без морганий.

#include <EEPROM.h>
#define pirA 7                      // PIR датчик
#define pirB 6
#define mosfet 9                    // мосфет
#define foto A0                     // фоторезистор
#define sw 3                        // кнопка
#define nightVAL 100                // порог включения
#define smooth 35                   // скорость затухания
byte pwm = 200;                     // максимальное значение яркости
int brightness;                 // текущее значение яркости
const long period = 5000;          // время свечения при срабатывании датчика
unsigned long last_press;
boolean butt_flag = 0;
boolean butt;
bool lightOff = true;
bool lightOn = false;
volatile int mode = 1;
int i;


void setup() {
  Serial.begin(9600);
  pinMode (pirB, INPUT);
  pinMode (pirA, INPUT);
  pinMode (mosfet, OUTPUT);
  pinMode (sw, INPUT_PULLUP);

  brightness = EEPROM.read( 0 );
  Serial.println(brightness);

  TCCR1A = TCCR1A & 0xe0 | 1;
  TCCR1B = TCCR1B & 0xe0 | 0x09;
}

void loop() {

  int foto = analogRead (0);
  if (Serial.available() > 0) {
    int in_data = Serial.parseInt();
    Serial.println();
  }
  if (mode == 1) {
    effect1();
  }
  if (mode == 2) {
    effect2();
    yield();
  }
}

void effect1() {
  delay(100);
  if ((digitalRead(pirA) || (digitalRead(pirB)) == HIGH) && (analogRead(foto) < (nightVAL))) {
    if (lightOff) {
      for (i = 0; i <= brightness; i++) {
        analogWrite(mosfet, i);
        delay(smooth);
      }
      delay(period);
      lightOff = false;
      lightOn = true;
    }
  }
  if (!lightOff) {
    analogWrite(mosfet, brightness);
    for (i = brightness; i >= 0; i--) {
      analogWrite(mosfet, i);
      delay(smooth);
    }
    lightOff = true;
    lightOn = false;
  }
}

void effect2() {
  if (lightOff) {
    for (i = 0; i < brightness; i++) {
      analogWrite(mosfet, i);
      delay(smooth * 1.5);
    }
    lightOff = false;
    lightOn = true;
  }
  else if (!lightOff) {
    analogWrite(mosfet, brightness);
    lightOff = false;
    lightOn = true;
  }
}


void yield() {
  butt = !digitalRead(sw);
  if (butt == 1 && butt_flag == 0 && millis() - last_press > 100) {
    butt_flag = 1;
    last_press = millis();
  }
  if (butt == 0 && butt_flag == 1) {
    butt_flag = 0;
    mode = mode + 1;
    if (mode > 2) {
      mode = 1;
    }
  }
  if (butt == 1 && millis() - last_press > 700) {
    brightness = brightness + 10;
    analogWrite(mosfet, brightness);
    if (brightness > pwm) {
      brightness = 20;
    }
    EEPROM.write(0, brightness);
    last_press = millis();
  }
}

 

b707
Offline
Зарегистрирован: 26.05.2017

ну если уж в цикл по mode = 2 вставили yield(), то и по mode=1 тоже ставьте, для симметрии...

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

Gortes
Gortes аватар
Offline
Зарегистрирован: 19.06.2022

Если не сложно покажите как правильно, а то что бы и плавно засвечивалось и обновлялся таймер свечения без делеев и yield() как то слишком для меня. Уже голова кипит...

 

sadman41
Offline
Зарегистрирован: 19.10.2016

На каждом лупе добавлять по 1% к яркости, например.

Gortes
Gortes аватар
Offline
Зарегистрирован: 19.06.2022

Не реализована фича одна котороя мне важна. Испульзую PIR AM312. Нужно что бы при срабатывании датчика плавно загорался свет и светил скажем 10сек, НО при повторном срабатывании датчика отсчет начинался сначала. По прошествию 10 сек свет плавно угасал до 0. НО при сработке датчика начал загораться стого места где закончил угасать(что бы не мигал).

sadman41
Offline
Зарегистрирован: 19.10.2016

А в чем затруднение?

Имейте переменную "яркость". Сработал датчик, начинайте каждые N ms плюсовать к ней переменную с положительным значением M. До 100 досчитали, Вместо М записали в переменную нуль. Оно плюсуется, но яркость не меняется. Ждете 10сек. В переменную кладете -M. Счёт пошёл вниз. До 0 дошли, в переменную положили 0. Сработал датчик - в переменную кладёте +M. Так оно и будет мотаться туда-сюда.

Gortes
Gortes аватар
Offline
Зарегистрирован: 19.06.2022

Затрудниени в том что не знаю как это правильно написать и внедрить. Не могу найти похожие реализации а с нуля все сделать знаний и времени не хватает. Благодарен тем кто ответил в теме, но не пойму почему никто не покажет как нужно сделать, может проект слишком простой для местной аудитории ...

sadman41
Offline
Зарегистрирован: 19.10.2016

br += step

If (br <= 0) { step = 0 ; }

If (br >= 100) { step = -2 ; }
...

И тд. Какие ещё примеры тут нужны?

А время ваше никто экономить не будет. Кроме алиэкспресса. Но он строго за деньги работает.

Gortes
Gortes аватар
Offline
Зарегистрирован: 19.06.2022

Нашел схожую реализацию задачи от ЕвгенийП, да простит он меня за такую интерпретацию)

#include <EEPROM.h>
static const uint32_t LIGHT_PERIOD = 1000ul * 5; // время свечения при срабатывании датчика
enum STATES { ON, GOING_ON, GOING_OFF };
#define pirA 7                      // PIR датчик
#define pirB 6                      // PIR датчик
#define mosfet 9                    // мосфет
#define foto A0                     // фоторезистор
#define sw 3                        // кнопка
#define nightVAL 50                // порог включения (думаем что ночь)
#define smooth 20                   // скорость затухания
byte mpwm = 200;                    // диапазон значения яркости
int brightness;                     // текущее значение максимальной яркости
int mode = 1;                       // начальный режим работы
unsigned long last_press;
unsigned long last_time;
boolean butt_flag = 0;
boolean butt;



void setup() {
  Serial.begin(9600);
  pinMode (pirB, INPUT);
  pinMode (pirA, INPUT);
  pinMode (mosfet, OUTPUT);
  pinMode (sw, INPUT_PULLUP);

  brightness = EEPROM.read( 0 );
  Serial.println(brightness);

  TCCR1A = TCCR1A & 0xe0 | 1;
  TCCR1B = TCCR1B & 0xe0 | 0x09;
}

bool signalDetected(const uint32_t curMillis) {
  static uint32_t lastTime = 0;
  const uint32_t debouncePeriod = 16;
  if (curMillis - lastTime < debouncePeriod) return false;
  lastTime = curMillis;
  static bool currentlyPressed = false;
  if ((digitalRead(pirA) || (digitalRead(pirB)) == HIGH) && (analogRead(foto) < (nightVAL))) {
    currentlyPressed = false;
    return true;
  }
  return false;
}

bool changePWM(const uint32_t curMillis, int8_t step) {
  static uint8_t pwm = 0;
  static uint32_t lastTime = 0;
  const uint32_t changePeriod = smooth;
  if (curMillis - lastTime < changePeriod) return false;
  lastTime = curMillis;
  bool result = false;
  if (step > 0) {
    if (pwm < brightness) pwm ++;
    result = pwm == brightness;
  } else {
    if (pwm > 0) pwm --;
    result = pwm == 0;
  }
  analogWrite(mosfet, pwm);
  return result;
}

void loop() {
  butt = !digitalRead(sw);
  if (butt == 1 && butt_flag == 0 && millis() - last_press > 20) {
    butt_flag = 1;
    last_press = millis();
  }
  if (butt == 0 && butt_flag == 1) {
    butt_flag = 0;
    mode = mode + 1;
    if (mode > 2) {
      mode = 1;
    }
  }
  if (butt == 1 && millis() - last_press > 700) {
    mode = 2;
    brightness = brightness + 10;
    Serial.println(brightness);
    analogWrite(mosfet, brightness);
    if (brightness > mpwm) {
      brightness = 20;
    }
    EEPROM.write(0, brightness);
    last_press = millis();
  }
  int foto = analogRead (0);
  if (Serial.available() > 0) {
    int in_data = Serial.parseInt();
    Serial.println(foto);
  }
  if (mode == 1) {
    effect1();
  }
  if (mode == 2) {
    effect2();
  }
}

void effect1() {
  static STATES currentState = GOING_OFF;
  static uint32_t theTimer = 0;
  static uint32_t oldMillis = 0;
  const uint32_t currentMillis = millis();
  if (oldMillis == currentMillis) return;
  oldMillis = currentMillis;
  if (signalDetected(currentMillis)) {
    if (currentState == GOING_OFF) {
      currentState = GOING_ON;
    }
    theTimer = LIGHT_PERIOD;
  } else {
    if (currentState == GOING_ON) {
      theTimer --;
      if (theTimer == 0) {
        currentState = GOING_OFF;
      }
    }
  }
  if (currentState == GOING_ON) {
    const bool gotMaximum = changePWM(currentMillis, 1);
    if (gotMaximum) {
      currentState = GOING_ON;
    }
  } else if (currentState == GOING_OFF) {
    const bool gotMinimum = changePWM(currentMillis, -1);
    if (gotMinimum) {
      currentState = GOING_OFF;
    }
  }
}

void effect2() {
  analogWrite(mosfet, brightness);
}

 

b707
Offline
Зарегистрирован: 26.05.2017

Gortes пишет:

Нашел схожую реализацию задачи от ЕвгенийП

мне кажется, вряд ли он обрадуется, что этот код приписывают ему :)