таймер для ардуины
- Войдите на сайт для отправки комментариев
Пнд, 18/03/2013 - 10:22
Требуется каждые 15 секунд включать реле и на 240 секунд отключать. Что сделать с millis() чтоб такое реализовать? К примеру, вот такой код:
int relay = 10;
void setup() {
Serial.begin(9600);
pinMode(relay, OUTPUT);
}
void loop() {
int time = 0;
if((millis() - time) == 12000) {
digitalWrite(relay, HIGH);
time = millis();
}
}
Он конечно не работает)) Что исправить и чем дополнить?
#define relay 10 void setup() { Serial.begin(9600); pinMode(relay, OUTPUT); } void loop() { static bool state; static unsigned long time; if((millis() - time) > (state ? 1000 : 3000)) { state = !state; digitalWrite(relay, state); time = millis(); } }1 секунду вкл. 3 секунды выкл.
отлично! всё путём.
Вот спасибки. Просто и красиво . Это почти ТО что я хотел .
Я добавил в ваш код кнопку и получил искомое.
#define relay 6 void setup() { Serial.begin(9600); pinMode(relay, OUTPUT); } void loop() { static bool state; if(digitalRead(2)==LOW) { digitalWrite(relay, LOW); } else(digitalRead(2)==HIGH); { static unsigned long time; if((millis() - time) > (state ? 1000 : 3000)) { state = !state; digitalWrite(relay, state); time = millis(); } } }Так код работает . Но почему он не работает вот так:
#define relay 6 void setup() { Serial.begin(9600); pinMode(relay, OUTPUT); } void loop() { static bool state; if(digitalRead(2)==HIGH) { static unsigned long time; if((millis() - time) > (state ? 100 : 800)) { state = !state; digitalWrite(relay, state); time = millis(); } } else(digitalRead(2)==LOW); { digitalWrite(relay, LOW); } }else не может быть с условием, либо if, либо else if
СПАСИБО!!!Точно!Теперь я знаю почему не работали и некоторые другие скетчи.Пошел исправлять их.
сходная проблема... есть mega2560, millis() и вывод на жк индикатор.. когда ардуина насчитывает минуту, проходит примерно 45 секунд реального времени.... кварц врет или по жизни такая точность?
примерный код...
long int intervalTimer = 500; void loop() { unsigned long currentMillis = millis(); if(currentMillis - previousMillis > interval) { previousMillis = currentMillis; CheckStatus(); //опрос кнопок каждый interval } if(currentMillis - PrevTimeMillis > intervalTimer) { PrevTimeMillis = currentMillis; Timer(); } if(TimerUpdate == HIGH) // TimeUpdate(); } void Timer() { TimerUpdate = HIGH; halfsecond ++; if(halfsecond == 2){ second ++; if(second == 60) { minute ++; if(minute == 60) { hour ++; if(hour == 24)hour = 0; minute = 0; } second = 0; } halfsecond = 0; } } void TimeUpdate(void) { if(DyspColonState==LOW) { DyspColonState=HIGH; DyspDecimalControl(DISPLAY_ADDRESS1,0b00000000); Timer(); } else { DyspColonState=LOW; DyspDecimalControl(DISPLAY_ADDRESS1,0b00010000); } // TimeDisp[0] = hour / 10; // TimeDisp[1] = hour % 10; // TimeDisp[2] = minute / 10; // TimeDisp[3] = minute % 10; TimeDisp[0] = minute / 10; TimeDisp[1] = minute % 10; TimeDisp[2] = second / 10; TimeDisp[3] = second % 10; DyspSendString(DISPLAY_ADDRESS1,TimeDisp); TimerUpdate = LOW; }сорри нашел косяк... строка 44, лишний вызов, остался от переноса кода....
Думаю что и не кварце дело, и не в яйцах, и не в бабине ) Так попробуйте:
int second, minute, hour; bool TimerUpdate; void setup() { Serial.begin(9600); } void loop() { Timer(); if(TimerUpdate == HIGH) { if(hour < 10) Serial.print("0"); Serial.print(hour); Serial.print(":"); if(minute < 10) Serial.print("0"); Serial.print(minute); Serial.print(":"); if(second < 10) Serial.print("0"); Serial.println(second); TimerUpdate = 0; } } void Timer() { static unsigned long millis_prev; if(millis()-millis_prev > 500) { TimerUpdate = HIGH; second = (millis()/1000)%60; minute = (millis()/60000UL)%60; hour = (millis()/3600000UL)%24; millis_prev = millis(); } }спасиб ) кое что у себя поправил...
Ребята помогите пожалуйста. Вопрос в следующем:
Есть Ожидание приема с порта
while(Serial.available()== 0);
далее работа с даными приема, данные поступают около 2 раз в секунду.
Как сделать таймер, что бы при отсутствии входящих данных, через 20 секунд происходило {какое то действие}
а при поступлении таймер сбрасывался.
Извините за неподобающее оформление, ну и надеюсь не будете сильно критиковать.
Перекопала интернет, но так и не поняла.
П.с. Я не программистка, просто пришлось...
unsigned long millis_prev = millis(); while(!Serial.available() && millis()-millis_prev < 20000); if(millis()-millis_prev >= 20000) { // прошло 20 секунд } else { // что-то прилетело в буфер порта }unsigned long millis_prev = millis(); while(!Serial.available() && millis()-millis_prev < 20000); if(millis()-millis_prev >= 20000) { // прошло 20 секунд } else { // что-то прилетело в буфер порта }спасибо Максим Вам огромное, а обнуление таймера тут ненужно?
Не знаю что вы имеете ввиду под словами "обнуление таймера тут ненужно?" ... Если вы про отсчет 20 секунд, то нет не нужно, а что бы знать точно это вам нужно или нет покажите весь код и напишите что хотите.
void setup() { Serial.begin(9600); maxServo1.attach(servoPin1); maxServo2.attach(servoPin2); } void loop () { while(Serial.available()== 0); int data = Serial.read() - '0'; Serial.println(Serial.read()); if (data == 20 && i==0) { i=i+1; Serial.flush(); } else if (data >=0 && i==1) { int pos1 = map(data, 0, 9, 20, 160); pos1 = constrain(pos1, 20, 160); maxServo1.write(pos1); i=i+1; Serial.flush(); } else if (data >=0 && i==2) { int pos2 = map(data, 0, 9, 80, 140); pos2 = constrain(pos2, 80, 140); maxServo2.write(pos2); Serial.flush(); i=i-2; } else { Serial.flush(); i=0; } }На порт приходит символ, за ним 2 значения от 0 до 9. Оба значения переводятся в положение сервоприводов. Если задержка более 20 секунд, нужно вернуть их в первоначальное положение. Как то так.
Задержка, как правило, отсчитывается от определенного момента. Что таким моментом является у вас? Рискну предположить, что момент последнего поступления символа по UART.
В общем, чуть конкретизируйте этот пункт.
Ну так опишите что делает этот код или точнее что вы от него хотите. Потому как символ никак не учавствует или запутывает работу кода, а Serial.flush() уже давно (с версии IDE1.0) не очищает буффер.
именно считала, что таймер должен срабатывать если с момента последнего прихода данных прошло более 20 секунд, точнее действия по таймеру.
Символ этот D, угадал? Надо ж было так зашифровать... Често говоря из кода не ясно что вы хотите от него, опишите что хотите... какой формат команды, которую вы отправляете? D12 ?
Вся проблема в том, что я подстраиваюсь под передатчик, к которому доступа не имею. Передатчик выдает следующее символ D, вы все верно поняли, далее два значения от 0 до 9. Тоесть типа D19. Приемник должен принять не перепутав очередности, в принципе это работает хорошо. Далее первое значение от 0 до 9 перевести в угол поворота сервопривода в диапазоне от 20 до 160 градусов
аналогично со вторым.
Это все работает. Но вслучае отсутствия сигнала, с момента прихода последнего, во времени более 20 секунд - сервопривода должны занять положение в 90 градусов
Не, все эти бесконечные while ждущие чего-то - это зло. Так же как и delay().
У вас же потом будет постоянная проблема с одновременностями и проч.
void loop(){ static unsigned long lastReceived=1; // если один - таймер запустится сразу, если 0 - при первом получении данных if(Serial.available())lastReceived=millis(); if( lastReceived && (millis()-lastReceived>=20000)){ // тут что-то делаем по поводу "20 сек небыло данных" lastReceived=0; // и вырубаем таймер что-бы небыло повторных сработок } // а тут пишите вашу остальную логику обработки данных из Serial }Если "все остальная логика работает" (не вникал в нее), то теоретически вместе это будет так выглядить (просто скоировал ее, убраз вот только "стоять ждать")
void loop () { static unsigned long lastReceived=0; // если один - таймер запустится сразу, если 0 - при первом получении данных if(Serial.available())lastReceived=millis(); if( lastReceived && (millis()-lastReceived>=20000)){ // тут что-то делаем по поводу "20 сек небыло данных" maxServo1.write(90); maxServo2.write(90); //lastReceived=0; // и вырубаем таймер что-бы небыло повторных сработок } // пошла ваша логика if(Serial.available()== 0){ int data = Serial.read() - '0'; Serial.println(Serial.read()); if (data == 20 && i==0) { i=i+1; Serial.flush(); } else if (data >=0 && i==1) { int pos1 = map(data, 0, 9, 20, 160); pos1 = constrain(pos1, 20, 160); maxServo1.write(pos1); i=i+1; Serial.flush(); } else if (data >=0 && i==2) { int pos2 = map(data, 0, 9, 80, 140); pos2 = constrain(pos2, 80, 140); maxServo2.write(pos2); Serial.flush(); i=i-2; } else { Serial.flush(); i=0; } } }Тогда уж наверное так должно быть:
// пошла ваша логика if(Serial.available()){ int data = Serial.read() - '0'; .......иначе будем читать буффер, когда в нем ничего нет и не читать когда что то есть.
И странно как у вас все работает... если например приходит D23, то D попадает в переменную data, 2 просто обратно отправляется, 3 опять в data. у следующей команды D отправляется обратно и т.д.
.... int data = Serial.read() - '0'; Serial.println(Serial.read()); .....Я бы сделал так
#include <Servo.h> #define servoPin1 3 #define servoPin2 4 Servo maxServo1; Servo maxServo2; void setup() { Serial.begin(9600); maxServo1.attach(servoPin1); maxServo2.attach(servoPin2); } void loop () { static unsigned long millis_prev; if(Serial.available() >= 3) { if(Serial.read() == 'D') { int data = Serial.read() - '0'; int pos1 = map(data, 0, 9, 20, 160); pos1 = constrain(pos1, 20, 160); maxServo1.write(pos1); data = Serial.read() - '0'; int pos2 = map(data, 0, 9, 80, 140); pos2 = constrain(pos2, 80, 140); maxServo2.write(pos2); } millis_prev = millis(); } else if(millis_prev && (millis()-millis_prev > 20000)) { maxServo1.write(90); maxServo2.write(90); millis_prev = 0; } }Интересный приемчик использовать переменную lastReceived и как переменную для хранения миллисекунд и как флаг.
Тогда уж наверное так должно быть:
Да конечно. if(Serial.available()) . Просто пока вставлял, пару раз форматирование вставало раком, "собирал " знаново и на третьей попытке - забыл ==0 убрать.
Я бы сделал так
Конечно, на вкус и цвет - фломастеры разные, но...
1. В моем варианте "логика работы" и "логика таймера" - полностью разделены. Легче поддерживать. И переписывать логику чтения/парсинга не отвлекаясь про "не забыть про таймер".
2. available() >= 3 - , а если первое чтение произойдет во не с самого начала пакета? Синхронизация сбилась навсегда?
Интересный приемчик использовать переменную lastReceived и как переменную для хранения миллисекунд и как флаг.
Да. Я обычно его и использую. Для того что-бы событие отработало один раз. Вначале я так и сделал, но когда стало ясно что у по событию нам нужно "ставить сервы в 90-то" - я закоментил строку "останавливаем таймер". Так как ничего страшного в многократном выставлянии серв в 90-то - нет. Следовательно можно обойтись без флага (без выключения таймера после сработки).
IMHO на такие вещи хорошо ложатся на паттерн "машина состояний"
#define START_MARKER 'D' // символ означающий начало пакет // перечень возможных режимов сотояний enum mode_t{ WAIT_START, WAIT_DATA1, WAIT_DATA2 }; mode_t mode=WAIT_START; // тут храним что мы сейчас ждем из Serial // возвращает true если ch - код символа какой-то цифры bool IsDigit(char ch){ return ch>='0' && ch<='9'; } void loop () { checkTimeOut(); // унесли таймерную логику в отдельную функцию // пошла ваша логика if(Serial.available()){ char ch=Serial.read(); // читаем из Serial if(mode==WAIT_START && ch==START_MARKER){ // мы ждали 'D' и он пришел mode=WAIT_DATA1; // теперь будем ждать данные для первой сервы } else if (IsDigit(ch)){ // что-то делаем только если это у нас пришла цифра byte data=ch-'0'; // конвертим код в цифру switch(mode){// решаем какую серву нужно выставить case WAIT_DATA1: maxServo1.write(map(data,0,9,20,160)); // ставим серву mode=WAIT_DATA2; // и начинаем ждать данные для второй сервы break; case WAIT_DATA2: maxServo2.write(map(data,0,9,80,140)); // ставим серву mode=WAIT_START; // и опять ждем стартовый маркер break; default: Serial.println("ERROR!!! Unknown mode");// какая-то фигня, никогда не должны сюда попадать. Сообщаем об этом } // switch } // IsDigit } // Serial.available } void checkTimeOut(){ // проверяем прошло ли 20-сек и ставит сервы в 90 то если да static unsigned long lastReceived=millis(); // сразу запускаем таймер if(Serial.available())lastReceived=millis(); if( millis()-lastReceived>=20000){ // 20 секунд ничего не просиходло // ставим сервы maxServo1.write(90); maxServo2.write(90); // начинаем ожиать букву 'D' mode=WAIT_START; } }Да, на вкус и цвет. С синхронизацией при глюке будет проблема, но это решается очисткой буфера.
Коль завел речь про "машину состояний", то лично я полюбляю (да, фломастеры :), делать ее не через switch, а через указатели и таблицу обработчиков. Тогда каждый режим - в отдельной функции и ковыряешься с ним потихоньку, а не в одной общей простыне. И добавить новый режим легче, без ветвистых if-фов.
#define START_MARKER 'D' // символ означающий начало пакет // перечень возможных режимов сотояний enum mode_t{ WAIT_START=0, WAIT_DATA1, WAIT_DATA2 }; mode_t mode=WAIT_START; // тут храним что мы сейчас ждем из Serial // описываем тим обработчика режимов, возвращает режим в который нужно перейти typedef byte (*StateHandlerT)(byte code, byte digit); // и создаем таблицу обработчиков, ее индексы - совпадают с возможными значениями переменной mode StateHandlerT handlers[]={start_handler,data1_handler,data2_handler}; void loop () { checkTimeOut(); // унесли таймерную логику в отдельную функцию // пошла ваша логика if(Serial.available()){ char ch=Serial.read(); // читаем из Serial byte digit=ch-'0'; // конвертируем в цифру mode=(mode_t)handlers[mode](ch,digit); // вызываем нужный обработчик и устанавливаем новый режим (который вернул обработчик) } } void checkTimeOut(){ // проверяем прошло ли 20-сек и ставит сервы в 90 то если да static unsigned long lastReceived=millis(); // сразу запускаем таймер if(Serial.available())lastReceived=millis(); if( millis()-lastReceived>=20000){ // 20 секунд ничего не просиходло // ставим сервы maxServo1.write(90); maxServo2.write(90); // начинаем ожиать букву 'D' mode=WAIT_START; } } // возвращает true если ch - код символа какой-то цифры bool IsDigit(char ch){ return ch>='0' && ch<='9'; } // ============обработчики режимов =========== byte start_handler(byte code, byte digit){ // ожидает 'D' if(code==START_MARKER)return WAIT_DATA1; // / мы ждали 'D' и он пришел теперь будем ждать данные для первой сервы else return WAIT_START; // иначе - продолжаем ждать 'D' } byte data1_handler(byte code, byte digit){ if(!isDigit(code))return WAIT_START; // если это было не цифра - начинаем ждать опять 'D', но можно было и "ждать цифру до победы", тогде вернуть mode - оставить текущий режим maxServo1.write(map(digit,0,9,20,160)); // ставим серву return WAIT_DATA2; // и начинаем ждать данные для второй сервы } byte data2_handler(byte code, byte digit){ if(!isDigit(code))return WAIT_START; // если это было не цифра - начинаем ждать опять 'D' maxServo2.write(map(digit,0,9,80,140)); // ставим серву return WAIT_START; // и опять ждем стартовый маркер }а вот за машину состояний спасибо, раньше не сталкивался с этой системой, погуглил, начал переписывать свой код... в размере прилично уменьшился и стал более понятен, а то больно развесистые свитчи были...
Есть вопросы про атоматное програмирование:
пример, обратный таймер, кнопка старт,она же стоп, кнопка, плюс, кнопка минус.
у таймера по идее 2 состояния, ждет старта или останова. плюсом и минусом можно уменьшать или увеличивать счетчик как на ходу так и в остановленном состоянии, также долгим удержанием этих клавиш можно менять счетчик с увеличенным инкрементом.
вопрос в следующем куда запихивать эту обработку? в функции останова, старта или таймера,а может лучше отдельный автомат на изменение счетчика от кнопок?
А зачем его вообще пихать в состояния? состояния "ждет старта, таймер идет, таймер закончился" - это понятно. А "увеличивать/уменьшать" счетчик - нет таких состояний. Ну можно, конечно притянуть за уши, но зачем? Это же просто переменные которые нужно менять при нажатии кнопок, судя по вашему описанию в любых состояниях. Так что IMHO их просто вынесте в loop() скажем и обрабатывать эти кнопки паралельно с машиной состояний.
В качестве примера - смотрите как timeout() я обрабатываю. Его я не пихал в состояния (хотя и может он на них воздействовать).
в общем так и было, но в основном цикле, получается опять сильно развесистый свитч на обработку кнопок... там на этот плюс и минус хочу навесить еще изменение 6-7 других параметров,
Ну дык а вам никто и не обещал, что "машина состояний" приминима всегда и везде. "Серебрянной пули" для того что-бы сделать любой код красивым - не существует. switch/if - существовали до нее, так и после (кстати "машина состояний" - это скорее "идея", которая может реализованно быть по разному. через таблицу указателей, через switch, через if...)
Ну а вашими "+"/"-" - тут абстрактно не скажешь. Уже от задачи зависит. Может просто параметры в массив загнать, может сделать еще одну табличку обработчиков (только уже не "состояний", а кнопок)...
но все равно спасибо, кое что применил...
Ребят спасибо огромное всем кто откликнулся, заработало все. Дело в другом немного крылось оказалось, что передатчик посылает в эфир "D" потом отправляет от 0 - 9 состояние первого сервопривода, далее отправляет состояние второго. В целом все заработало. Удачи вам в проектах!
Попробуйте вот эту библиотеку http://blog.ilyaplot.ru/arduino_setinterval/. Мне кажется, отлично упростит задачу.