Народ, подскажите, что может быть не так в коде

Тема в разделе "Arduino & Shields", создана пользователем Delphin911, 25 фев 2020.

  1. Delphin911

    Delphin911 Нерд

    Не понимаю в чем причина, вроде ничего сложного в коде. Собираю освещение на дачу на базе фонаря, купленного на Алиэкспресс (собственно оттуда только корпус с солнечной батареей и и PIR датчик, остальное все сдохло через месяц-два), схемы содраной с датчика движения от Ардуино и Attiny13.
    Собственно сам датчик работает. Затыка в програмке на Attiny. Хочу сделать, чтобы фонарь не вспыхвал и гас мгновенно, а плавно зажигался и гас. Собственно зажигается он без проблем, а вот когда гаснет, то соазу зажигается и потом в цикле.
    Вот код
    Код (C++):
    #include <util/delay.h>
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #define ledPin PB0 //Выход на светодиоды PB0, 13
    #define pirPin PB3 //Вход с датчика движения PB3, 2
    #define WM PB1 //Индикатор режима работы. Не горит - выключен. Горит - режим1. Мигает - режим2.
    #define BTN PB4 //Кнопка управления
    unsigned long last_press; //Для устранения дребезга нопки
    byte mode; //Режим работы
    boolean work;
    long unsigned int wait;

    void setup() {
      // put your setup code here, to run once:
      DDRB &= ~(1 << pirPin); //Задаем вход PB2
      DDRB |= (1 << ledPin); //Задаем выход PB0
      DDRB |= (1 << WM); //Задаем выход PB1
      DDRB &= ~(1 << BTN); //Задаем вход PB4
      digitalWrite(pirPin, 0);   //дадим датчику время на калибровку   4
      for(int i = 0; i < 20; i++)  
      {    
        digitalWrite(WM,1);
        delay(500);  
        digitalWrite(WM,0);
        delay(500);  
      }  
      delay(50);
      digitalWrite(WM,0);
      mode = 0;
    }

    void loop() {
      // put your main code here, to run repeatedly:
    //Отработка нажатия кнопки, задание режима--------------------------------------------------------------
    if (digitalRead(BTN) && millis() - last_press > 300){
      if (mode == 1) mode = 0;
      else mode = 1;
      last_press = millis();
    }
    //Режим выключено---------------------------------------------------------------------------------------
      if (mode == 0)
      {
        PORTB &= ~_BV(ledPin);
        PORTB &= ~_BV(WM);
      }
    //Режим включено--------------------------------------------------------------------------------------
    else {
    PORTB |= _BV(WM);
    if(digitalRead(pirPin) == 1)
    {
      if (!work)
      {
        work = true;
        plavno(0,255);
        //digitalWrite(ledPin,1);
      }
    }
    if(digitalRead(pirPin) == 0)
    {
    //  wait = millis();
    //  if (millis() - wait > 3000)
    //  {
        //digitalWrite(ledPin,0);
      if (work)
      {  
        //plavno(255,0);
        digitalWrite(ledPin,0);
        //delay(1000);
      }
      work = false;
    }
    }
    }

    void plavno(int a, int b){
    unsigned long zaderzka; //Задержка для плавного включения-выключения
    zaderzka = millis();
    if (a<b){
      for (int i=a;i<=b;i++)
      {
        analogWriteOUT(i);
        while((millis() - zaderzka) < 30) {}
        zaderzka = millis();
      }
    }
    if (a>b){
      for (int i=a;i>=b;i--)
      {
        analogWriteOUT(i);
        while((millis() - zaderzka) < 30) {}
        zaderzka = millis();
      }
    }
    }

    void analogWriteOUT(int val){
    sbi(TCCR0A, COM0A1);
    OCR0A = val; // устанавливаем уровень ШИМ
    }
     
    Собственно код был частично содран и частично написан самостоятельно. Вот этот момент глючит:
    Код (C++):
      if (work)
      {  
        //plavno(255,0);
        digitalWrite(ledPin,0);
        //delay(1000);
      }
    Если поставить просто выключение как стоит в коде, то все работает. Если же включаю плавное выключение, то начинается бесконечное включение-выключение. Я уже почти смирился и готов оставить выключение мгновенное, но просто интересно в чем же дело?
     
  2. Delphin911

    Delphin911 Нерд

    Сам понимаю, коряво получилось, но по другому что-то не сообразил. Но на включении работает без проблем.
    А почему равно надо убрать? Ведь 255 и 0 это нормальное значение. Или я чего-то недопонимаю?
     
  3. akl

    akl Гуру

    для процесса выключения нужно что-то, что закончит этот процесс (то есть не даст ему повторяться бесконечно). во включении это есть
    if (!work)
    {
    work = true;

    нужно что-то похожее и для выключения.
    ну и да, миллис так не используют - в данном случае это ничем не лучше обычного делея
     
  4. Delphin911

    Delphin911 Нерд

    Ну вы объясните? Я же не просто блажу и отказываюсь слушать вас, а хочу понять, чтобы не допускать впредь таких ошибок.
    Я понимаю, но что-то не соображу как сделать. Собственно это из Delay и было переделано. Можете показать как это надо делать? Ссылочку на код или краткий кусочек кода.
     
  5. akl

    akl Гуру

    пример правильного использования миллис есть в этом же скетче - "Отработка нажатия кнопки"
    но надо ли оно в плавном включении-выключении? проще там делей оставить, иначе слишком много придется переделывать.
     
  6. Delphin911

    Delphin911 Нерд

    Понятно. Спасибо. Но в принципе ведь должно работать как у меня написано? Ну точнее даже на включении оно работает. А вот почему при выключении оно снова включается. При чем не просто гаснет, потом снова загорается, потом гаснет. А именно отрабатывает как будто обнаружено движение. Т.е. загорается, горить сколько установлено времени на датчике движения и потом плавно гаснет и тут же снова включается и по новой. Я сначала грешил на какие-то ошибки в схеме. Но когда светодиод гасится мгновенно, т.е. digitalWrite(ledPin,0); то все работает как положено.
     
  7. akl

    akl Гуру

    возможно датчик нестабильно работает, digitalRead(pirPin) срабатывает когда не должен и work включается?
     
  8. Delphin911

    Delphin911 Нерд

    Да уже думал так. Только при плавном выключении всегда неправильно работает (всегда повторно по кругу срабатывает), а при мгновенном выключении всегда все работает правильно, без проблем.
     
  9. akl

    akl Гуру

    это потому что мгновенное выключение просто прописывает в пин ноль, а плавное выключение сначала включает, а потом плавно выключает. поэтому ложные заходы в мгновенное выключение просто незаметны.

    можно попробовать на пине датчика подтяжечный резистор подключить, вдруг там наводки или че. Тестером проверить какие там реальные напряжения в разных условиях от датчика приходят
     
  10. Delphin911

    Delphin911 Нерд

    Так у меня там есть. Там work = false после if идет. Т.е. work присваивается false в любом случае, какое бы значение work перед этим не было.
     
  11. Delphin911

    Delphin911 Нерд

    Ага, спасибо. Попробую.
    К стати вспомнил один момент. Вся схема у меня на макетке собрана. Я начинал разрабытвать ее на с Attiny13, а с Arduino. И код был тот же (ну в общем). И все работало. И тогда я взял уже этот код прописал в Arduino и подцепил ее вместо Attiny13. И все работало.
     
  12. akl

    akl Гуру

    вот например как я бы сделал асинхронную (без задержек) функцию плевного изменения:


    Код (C++):

    /*
    функция принимает ссылку на переменную, которую будет менять,
    значение до которого надо изменить переменную,
    интервал времени, через которое будет меняться,
    текущее время.
    возвращает разницу между текущим и заданным значениями,
    если вернуло 0 - значит изменение завершено
    */

    int plavno(unsigned char &curlight, unsigned char newlight, unsigned int interval, unsigned long curtime){
        static unsigned long oldtime=0;
        int d=curlight-newlight;
        if(curtime-oldtime>interval && d!=0){
            oldtime=curtime;
            if(d>0){
                curlight--;
            }else if(d<0){
                curlight++;
            }
        }
        return d;
    }
     
    примерный пример использования

    Код (C++):
       
    if(!flag){ //какой-нибудь флаг
         if(plavno(light,31,50,millis())==0){ //мимоходом в цикле меняем переменную light раз в 50 мс пока она не станет равна 31
              flag=1; //взводим флаг что изменение завершено
         }
    }
    if(light_old!=light){ //проверяем изменилось ли значение
        light_old=light;
        analogWriteOUT(light); // если изменилось - отправляем его на выход
    }
     
    конечно это не единственный и не лучший способ - просто пример для размышления

    а еще - в обычном СИ (не ++) передача по ссылке не работает в таком виде - там надо использовать указатель. Но проще тупо глобальную переменную сразу менять непосредственно
     
    Последнее редактирование: 25 фев 2020
  13. Delphin911

    Delphin911 Нерд

    Спасибо! Сегодня вечером будем курить бамбук! :)
     
  14. akl

    akl Гуру

    курить лучше вот эту книжку https://www.r-5.org/files/books/com...nis_Ritchie-The_C_Programming_Language-RU.pdf
    даже не всю, а до указателей (лучше включительно). там не так много, но проясняет гораздо лучше любых ардуино-уроков.
    конечно придется установить какое-нибудь иде с си, но это тоже очень полезно - многие вещи гораздо проще прикидывать и тестировать безардуиновыми способами
     
    Un_ka и arkadyf нравится это.
  15. Delphin911

    Delphin911 Нерд

    Спасибо! И эту книжку покурим. :)
     
  16. Delphin911

    Delphin911 Нерд

    В общем ничего не получилось. Подтянул резистором на 10кОм. Переписал код в месте функции плавного включения выключения. Не помогло. Все равно после выключения снова срабатывает включение и так до бесконечности если оставить плавное выключение. Если сделать мгновенное выключение, то все остальное работает.
    Вот код какой получился:
    Код (C++):
    #include <util/delay.h>
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #define ledPin PB0 //Выход на светодиоды PB0, 13
    #define pirPin PB3 //Вход с датчика движения PB3, 2
    #define WM PB1 //Индикатор режима работы. Не горит - выключен. Горит - режим1. Мигает - режим2.
    #define BTN PB4 //Кнопка управления
    unsigned long last_press; //Для устранения дребезга нопки
    byte mode; //Режим работы
    boolean work;
    long unsigned int wait;
    unsigned char light, light_old;

    void setup() {
      // put your setup code here, to run once:
      DDRB &= ~(1 << pirPin); //Задаем вход PB2
      DDRB |= (1 << ledPin); //Задаем выход PB0
      DDRB |= (1 << WM); //Задаем выход PB1
      DDRB &= ~(1 << BTN); //Задаем вход PB4
      light = 0;
      light_old = 0;
      digitalWrite(pirPin, 0);   //дадим датчику время на калибровку   4
      for(int i = 0; i < 20; i++)  
      {    
        digitalWrite(WM,1);
        delay(500);  
        digitalWrite(WM,0);
        delay(500);  
      }  
      delay(50);
      digitalWrite(WM,0);
      mode = 0;
    }

    void loop() {
      // put your main code here, to run repeatedly:
    //Отработка нажатия кнопки, задание режима--------------------------------------------------------------
    if (digitalRead(BTN) && millis() - last_press > 300){
      if (mode == 1) mode = 0;
      else mode = 1;
      last_press = millis();
    }
    //Режим выключено---------------------------------------------------------------------------------------
      if (mode == 0)
      {
        PORTB &= ~_BV(ledPin);
        PORTB &= ~_BV(WM);
      }
    //Режим включено--------------------------------------------------------------------------------------
    if (mode == 1){
    PORTB |= _BV(WM);
    if(digitalRead(pirPin) == 1)
    {
      if (!work)
      {
        //digitalWrite(ledPin,1);
        if(plavno(light,255,10,millis())==0)
        { //мимоходом в цикле меняем переменную light раз в 50 мс пока она не станет равна 31
          work=true; //взводим флаг что изменение завершено
        }
        if(light_old!=light)
        { //проверяем изменилось ли значение
          light_old=light;
          analogWriteOUT(light); // если изменилось - отправляем его на выход
        }

      }
    }
    if(digitalRead(pirPin) == 0)
    {
      if (work)
      {  
        //digitalWrite(ledPin,0);
        if(plavno(light,0,10,millis())==0)
        { //мимоходом в цикле меняем переменную light раз в 50 мс пока она не станет равна 31
          work=false; //взводим флаг что изменение завершено
        }
        if(light_old!=light)
        { //проверяем изменилось ли значение
          light_old=light;
          analogWriteOUT(light); // если изменилось - отправляем его на выход
        }
      }
      //work = false;
    }
    }
    }


    void analogWriteOUT(int val){
    sbi(TCCR0A, COM0A1);
    OCR0A = val; // устанавливаем уровень ШИМ
    }

    int plavno(unsigned char &curlight, unsigned char newlight, unsigned int interval, unsigned long curtime)
    {
        static unsigned long oldtime=0;
        int d=curlight-newlight;
        if(curtime-oldtime>interval && d!=0){
            oldtime=curtime;
            if(d>0){
                curlight--;
            }else if(d<0){
                curlight++;
            }
        }
        return d;
    }
     
     
  17. akl

    akl Гуру

    а если вместо датчика подключить кнопку? или тупо провод, которым вручную тыкать то в + то в -.

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

    если это датчик движения, то по идее он должен зафиксировав движение поднимать ногу на какое-то время. если это время слишком короткое, то надо в программе установить отдельную засечку времени, чтобы после однократного срабатывания датчика свет горел какое-то достаточно продолжительное время
     
    Последнее редактирование: 26 фев 2020
  18. Delphin911

    Delphin911 Нерд

    Да уже недели 2 разбираюсь. Сначала на датчик и думал, схему пересобрал. Но ведь при мгновенном отключении работает как надо. Ну в итоге оставлю мгновенное отключение.
     
  19. akl

    akl Гуру

    там делов то тестером ткнуть в пин.

    можно вот что попробовать
    добавить переменные
    char detect;
    unsigned long detect_timer;
    в цикл там где mode==1 вставить вот это
    Код (C++):

    if(digitalRead(pirPin)==1){
         detect=1;
        detect_timer=millis();
    }
    if(detect && millis()-detect_timer>60000){
          detect=0;
    }
     
    а дальше соответственно digitalRead(pirPin) == 1 заменить на detect==1
    и тогда после того как датчик один раз сработал свет будет гореть минуту. то есть свет не выключится если хотя бы раз в минуту там кто-то шевелится.

    но если проблема в ложных срабатываниях датчика, то это не поможет. но надо в любом случае посмотреть что на ноге датчика происходит.
     
    Последнее редактирование: 26 фев 2020
  20. Delphin911

    Delphin911 Нерд

    Спасибо akl! Вечером буду пробовать и датчик кнопкой заменить и кусок кода вставить.
    Я к стати подумал, а что если после выключения сделать digitalWrite(pirPin, 0). Вдруг поможет!
    Тестером нет надобности на пин лазить. У меня там светодиод на выходе схемы датчика движения стоит. Он загорается как будто датчик сработал. Но фигня вся опять таки в том, что при мгновенном выключении он не срабатывает, а при плавном, срабатывает.