millis() зависает
- Войдите на сайт для отправки комментариев
Пнд, 28/09/2015 - 10:06
Добрый день!
Подскажите, пожалуйста, в какую сторону копать!
Вот этот кусок кода отлично работает, если поставить 10, 20, 30 секунд в условии. Но если ставим 40 секунд или больше, то во первых условие не срабатывает, а во вторых программа как бы подвешивается. И чтобы она заработала снова, нужно пару раз похлопать в ладоши, активизируя соответствующее условие двойного хлопка.
if ((millis()-globaltimeout)>1000*40) //принудительное выключение через 1 час бездействия { ledstate=0; }
ниже привожу весь полный код моей программулины ) int ledpin=13; int micPin=A0; // датчик шума подключен через аналоговый вход int sensorPin=2; // датчик движения int relePin=3; // реле включает свет int analogData=0; //тут будут храниться данные с АЦП пин 0 int ledstate=0; //переменная состояния вкл-выкл при хлопках int ledstate2=0; // переменная состояния вкл-выкл для датчика движения int ledstate3=0; int maxvalue=250; // настройки моего конкретного микрофона, уровень громкости unsigned long timeoff; // отслеживается 15 секунд без движения и выключает свет unsigned long globaltimeout; // отслеживает 1 час работы и выключает свет void setup() { pinMode(ledpin, OUTPUT); pinMode(micPin, INPUT); pinMode(sensorPin, INPUT); pinMode(relePin, OUTPUT); digitalWrite(13,0); digitalWrite(relePin,1); //реле свет выключен с самого начала globaltimeout=millis(); } void loop() { if (digitalRead(sensorPin)==1) // если сработал датчик движения { timeoff=millis(); // засекаем время когда он сработал ledstate2=1; // свет включить } else { if (millis()-timeoff>1000) // если с момента включения по датчику движения прошло более 15 секунд { ledstate2=0; // свет выключить } } analogData=analogRead(micPin); //читаем значение if(analogData>maxvalue) { delay(200); //ожидаем 200милисекунд для повторного хлопка - можно не делать но в моём случае это добавило удобства for(int t=0;t<=500;t++) //цикл на проверку второго хлопка по идее на пол секунды но ардуино делает его дольше { delay(1); //для цикла analogData=analogRead(micPin);//считываем данные if(analogData>maxvalue) { //сверяем данные для второго хлопка, если он был то меняем состояние лампы с вкл на выкл и наоборот ledstate=!ledstate; //меняем состояние analogData=0;//обнуляем данные globaltimeout=millis(); // запоминаем время срабатывания по двойному хлопку break;//выходим из цикла после второго хлопка delay(200);//для удобства } } analogData=0; } if ((millis()-globaltimeout)>1000*40) //принудительное выключение через 1 час бездействия { ledstate=0; } ledstate3=(ledstate || ledstate2); digitalWrite(13,ledstate3); //включаем выключаем светодиод на 13м пине digitalWrite(relePin,!ledstate3); // включаем выключаем свет через реле }
Комментарии писались для введения противника в заблуждение? Похоже на то.
Тут у Вас много чего намешано и кроме того условия. Давайте с начала.
Итак. самое начало времён. датчик движения пока ещё не сработал. Проверка в строке 28 даёт ложь (кстати, почему 1, а не HIGH?) и управление получает else-участок - строки 35-38. Здесь протсходит сравнение
if (millis()-timeoff>1000)
Но ведь timeoff пока ещё не получила НИКАКОГО значения! Результат операции непредсказуем. Можно, конечно, предположить, что после перезагрузки МК там 0, но ... так не делается.
Теперь про Ваше "зависание".
Переменная globaltimeout получает значение в setup, а потом только при двойном хлопке. Это означает, что если условие в строке 59 единожды сработало, оно будет продолжать срабатывать в каждом такте пока не появится двойной хлопок, т.е. пока не поменяется переменная globaltimeout. Отсюда странное поведение. Думаю, что она должна как-то меняться внутри условия (при срабатывании), но это Вам виднее т.к. я не знаю чего Вы хотите достичь.
Евгений, спасибо за подсказки!
Теперь присваиваю значение переменной timeoff в сетапе.
1 и 0 поменял а HIGH и LOW (думал что это одно и то же)...
globaltimeout именно должна меняться вне условия :) Тут все верно. Переменная отслеживает, что если прошло больше часа с момента последнего хлопка, то свет выключаем.
Насчет глюка - похоже, дело оказалось в преобразовании типов переменных, про которые я прочитал у автора LESHAK. Спасибо ему! Он предлагает определять значения с явным указанием типа. По крайней мере на 60 секундах мне это помогло! Сейчас проверяю на 1 часе.
Вот ссылка на тему Leshaka:
http://arduino.ru/forum/programmirovanie/eshche-raz-migaem-svetodiodom-b...
чой-то я не пойму как это должно работать:
у меня два датчика: датчик шума и датчик движения.
если хотя бы один из них сработал, то свет включен. если оба не работают. то свет выключен.
|| - это логическое ИЛИ
Есть такая бяда у этой функции, но вряд ли вы именно на этот случай напоролись. :)
Дело в том, что она считает .. "скачками", а именно: каждые 42 миллисекунды добавляется не 1, а сразу 2 миллисекунды за счет "накопления". Поэтому у неё не бывает 42 .. 84 и т.д. миллисекунд (или около того, инкремент остатка по 24 микросекунды..), далее со сдвигом в 8 микросекунд каждое накопление. Ваще-то. 40-ю - она отсчитает вполне сносно. Ещё это приводит к "плаванию" миллисекунд +- 2мсек с периодом в 41 миллисекунду от реального времени. И только.
Проверяйте millis() на вариант >= ... , а не просто == или на % (остаток от деления).
Скетч не смотрел, но думаю что это, все-таки не ваш случай.. смотрите что ещё может быть не так.
попробуйте избавиться с delay(), так как delay(200) в пять раз в цикле уже отставания на 1сек.
Действительно если вы работаете с переменной которую сами изменяете
то она обязательно станет равна (==) чему-то.
Она меняется " монотонно" в нужную вам строну
А вот с millis() лучше использовать >= или <=
так как для вас в момент считывания ( в почти случайное время) она меняется "скачками"
и как правильно заметил Arhat109-2 ещё и "скачками" разной величины.
Всем спасибо за советы!
Проблема решилась. Дело было действительно в неверном преобразовании типов данных, как я и писал выше. См. пояснения от Leshak по ссылке в моем посте выше.
Вот конечный работающий код, если кому интересно:
knik777, да действительно, там 40 тысяч - т.е. переход через границу int, я этого не заметил. Впрочем, и хорошо , что не заметил. От того, что Вы нашли сами пользы много больше, чем если бы я сказал.
А где там граница int ?
Я тоже смотрел на эти 40*1000, но вроде это не переменная, поэтому тип и разрядность описывать негде.
Разве что 400000UL или 40*1000UL
Евгений, а я про 40 тыщ понятия не имел. просто взял один-в-один и применил, как советовал Лешак. Оно заработало. И это для меня главное. Принцип понял, что надо следить за соответствием типов в будущем. а дальше лень разбираться :)
32767 - int больше не бывает.
только сейчас дошло!!! :)
int это диапазон от -32768 до 32767.
поэтому ставлю 10000-30000 мс, все работает. А на 40000 мс реально сваливалось.
PS 40000 мс есть только в первом листинге. Это было тестовое значение. Сейчас оно заменено на 1 час и приведено к unsigned long (то есть 60*60*1000UL)
Действительно, какой изящный оказался глюк )
Евгений, а я про 40 тыщ понятия не имел. просто взял один-в-один и применил, как советовал Лешак. Оно заработало. И это для меня главное. Принцип понял, что надо следить за соответствием типов в будущем. а дальше лень разбираться :)
Если писать просто числа без специальных суффиксов, то считается, что у них тип int. Но числа этого типа не бывают больше, чем 32767. Поэтому 40*1000 получится вовсе не 40000 как можно было бы ожидать. Другое дело, если написать 40L*1000L (собственно достаточно L в одном месте).
А где там граница int ?
Я тоже смотрел на эти 40*1000, но вроде это не переменная, поэтому тип и разрядность описывать негде.
Разве что 400000UL или 40*1000UL
Вообще-то millis() имеет тип unsigned(!) long. Соответсвенно проверки интервалов, даже в int желательно делать в unsigned .. и диапазон побольше 0..65365 и сравнение проще и вернее, и от millis() кумпилятыр отожрет что ему надо без вопросов, пардон "накладных расходов" на преобразование. :)