Arduino+IR+Servo
- Войдите на сайт для отправки комментариев
Написал такой скетч:
#include "IRremote.h"
IRrecv irrecv(11);
decode_results results;
#include <Servo.h>
Servo LXservo;
const char SERVO_MIDDLE_ANGLE = 90; // угол сервы при котором колеса выставлены прямо
int servoAngle = SERVO_MIDDLE_ANGLE;
int lastButton = 0;
void setup()
{
LXservo.attach(8);
Serial.begin(9600); // Выставляем скорость COM порта
irrecv.enableIRIn(); // Запускаем прием
}
void loop()
{
if (digitalRead(11) == LOW) servoAngle = SERVO_MIDDLE_ANGLE;
LXservo.write(servoAngle);
if (irrecv.decode(&results))
{
int res = results.value;
int lastButton ;
Serial.println(res, HEX);
if(res==0x48B7) || res==0xFFFFC837)// Если нажата кнопка "LEFT" или "RIGHT"
{
lastButton=res;
}
else if(res==0xFFFFFFFF)// Если кнопку удерживают
{
if(lastButton==0x48B7) servoAngle = 115;// Удерживают "LEFT"
if(lastButton==0xFFFFC837)servoAngle = 75; // Удерживают "RIGHT"
}
irrecv.resume();
delay (100);
}
}
Задумка такая, что при отсутствии управляющего сигнала серва возвращается в положение 90 град. После включения схемы серва становится в 90 градусов и все. На пульт не реагирует. Светодиод на плате мигает во время нажатия на кнопки, но серва стоит. Если изменить функцию loop следующим образом образом:
vvoid loop()
{
//if (digitalRead(11) == LOW) servoAngle = SERVO_MIDDLE_ANGLE;
LXservo.write(servoAngle);
if (irrecv.decode(&results))
{
int res = results.value;
int lastButton ;
Serial.println(res, HEX);
if(res==0x48B7)
{
servoAngle = 115;
}
else if(res==0xFFFFC837)
{
servoAngle = 75;
}
else if(res==0x708F)
{
servoAngle = 90;
}
irrecv.resume();
delay (100);
}
}
то так все работает, но мне так не надо)). Где грабли в первом коде?
Впредь обязательно уважайте правила форума - http://arduino.ru/forum/obshchii/vstavka-programmnogo-koda-v-temukommentarii
Обе библиотеки (Servo и IRremote) используют один и тот же таймер-счётчик №1. Возникает конфликт. Отсюда и проблема.
Прошу прощения за обращение не по форме. Почему же тогда во втором случае все работает? Как решить проблему со счетчиком?
Ну, откуда мне знать, почему работает - конфликт оборудования вещь непредсказуемая.
Проблему решить можно по-разному. Зависит от Вашего желания и квалификации.
1. точно не помню, но кажется (не уверен), что библиотеке IRremote можно сказать каким счётчиком ей пользоваться. Если можно, то это простейшее решение.
Если же нельзя, то
2. Можно искать библиотеки, которые используют другие счётчики.
3. Можно взять существующую библиотеку и переделать её на другой счётчик. С Servo это сделать сложно, а с IRremote попроще.
4. Можно вообще отказаться от бибилотек (или от одной из них) и написать свой код.
Выбирайте.
Прикольно. А попроще никак?:-)
Прикольно. А попроще никак?:-)
Можно, конечно. Например, пойти в соответсвующий раздел форума и там Вам это сделают за некоторое скромное вознаграждение.
За вознаграждение я готовую игрушку купил бы. Дело в принципе: сделать все самостоятельно.
Ну, так, вперёд. Четыре пункта выше.
Начните с первого, если с ним повезёт, то всё просто. Откройте тексты библиотеки IRremote и постарайтесь понять можно ли там указать каким таймером пользоваться. Если можно, то это задаётся константами. По умолчанию используется первый таймер, Вам нужно перейти на второй.
Если же нет, смотрите что именно делает таймер и напишите тоже самое для второго. Я это недавно делал для библиотеки VirtualWire (она использует первый таймер, а у меня он ещё и в другом месте использовался). Там оказалось всё просто. Таймер просто отбивал тактирование, а написал такое же тактирование ( с той же частотй) для второго таймера и проблема снялась.
Дело не в таймерах, вот часть кода из библиотеки ServoTimers.h (см. последний "абзац" - мой случай):
Авот часть кода из библиотеки IRremoteInt.h (см. также последний "абзац"):
Из чего понятно что библиотека Servo.h использует таймер №1, а библиотека IRremoteInt.h таймер №2. Грабли где-то в другом месте.
Ага, т.е. как я и предполагал, в IRremote можно настраивать таймер и у Вас настроено на второй (у меня-то на первый, потому я так и писал).
Отлично! Эту причину исключили. Надо смотреть дальше.
Посмотрел. Кажется, понятно. Вы ставите серву по "нулю на пине" ресивера. Но ноль на пине - не есть отсутствие сигнала. Давайте всё-таки сделаем точно как Вы написали словами - есть сигнал выставляем углы, нет сигнала - выставляем на фиксированный угол.
Код получается примерно такой. Попробуйте.
Вы бы всё-таки публиковали код по правилам. Дело тут не в "не по форме", как Вы изволили выразиться, а в том, что я вот хочу сказать что-то по коду. Если бы он был нормально опубликован, там бы были номера строк, а так их нет. Ну, и как мне Вам что-то говорить?
В общем, сегодня я добрый, но вообще, согласитесь, создавать неудобства тому, кто хочет Вам помочь - не лучшая идея.
Исправляюсь:
Как вы написали я так уже пробовал. Для верности перепопробовал еще раз))...Не помогло. Все тоже самое. Хех... Видно придется вращать серву "вручную" как тут предлагают http://coolcode.ru/arduino-upravlenie-servoprivodom-bez-biblioteki-servo-h/
Да, нет, оно должно работать, надо просто спокойно разбираться в чём дело.
Например, попробуйте в моём коде, после строки 20 поставить задержку, ну для начала в полсекунды (я понимаю, что она неприемлема в конечно коде), но мы пока проблему ищем. Изменилось что-то?
Я извиняюсь, вот так как в вашем примере заработало. Я просто не досмотрел что этот код на основе второго варианта моего кода. Но второй вариант и был рабочим. А мне надо чтобы было именно по удержанию кнопки. Мне кажется что-то не так именно с удержанием кнопки т.е. с приемом 0хFFFFFFFF. Ведь серва гарантировано стоит в 90 градусах как будто нет приема...
Так, стоп. То, что заработал - хорошо. Но, тогда я не очень понимаю, Чего Вам надо.
Ну, серва-то гарантированно стоит просто потому, что Вы её туда ставите. LOW на пине появляется постоянно и при наличие сигнала тоже. А Вы как увидите LOW, так и ставите - это стопудово неправильно.
Вас интересует нажатие и удержание кнопки на ПДУ, так? Правильно? Ответьте. Причём ответьте толком - типа "пока удерживаю, делается то-то ...". Если так, то это надо по другому обрабатывать.
Я попробовал вот так:
Т.е. вставил строки 44 45 из вашего примера. Т.е. не по LOW тоже не работает.
Что мне надо:
1. При нажатии и дальнейшем удержании кнопки LEFT происходит поворот влево (115 градусов).
2. При нажатии и дальнейшем удержании кнопки RIGHT происходит поворот вправо (75 градусов).
3. При отпускании кнопок серва становится в нейтральное положение (SERVO_MIDDLE_ANGLE).
Удержание надо чтобы в дальнейшем реализовать плавный поворот в зависимости от длительности удержания (чем дольше держишь кнопку тем на больший угол происходит поворот), но это другая история. Покачто пусть оно заработает в простом варианте.
Хорошо, для начала, давайте посмотрим, что выдаёт Ваш приёмник. Запустите скетч:
Нажмите кнопку и немного подержите, затем отпустите.
Посмотрите на результат. Заодно скопируйте результат сюда (кусок от "нажатия - пару строк", до "отпускания + пару строк"), я тоже посмотрю.
Нажатие и удержание Лево, право с irrecv.resume()
Нажатие и удержание Лево, право без irrecv.resume()
Скорость уменьшил до 300, ато на 115200 голова заболела)). Еще во втором случае (без irrecv.resume();) после одного нажатия сигнал не прекращался после отпускания кнопки.
Так, ну сами-то посмотрели? Видите, с resume у Вас в то время, когда кнопка нажата сигнал постоянно то появляется. то пропадает (а кнопку-то никто не отпускал!) Этого Ваша программа не обрабатывает.
Без resume сигнал чистый. Пока держите - идёт постоянно.
Можно попробовать запустить основной код без resume и посмотреть что будет (хотите, попробуйте). Но я бы сначала попробовал более чистый эксперимент. Дело в том, что вывод в сериал очень длительная и сложная процедура. Давайте поставим такой эсепримент, а потом уж будем думать что делать с кодом.
По идее, в этом тесте при нажатии кнопки должно однократно появлятья сообщение pressed, а при отпускании - released. Если же в процессе, пока кнопка нажата, будет появляться то released, то pressed, мы должны понимать, что сигнал самопроизвольно пропадает и учитывать это в программе.
Сделайте и скажите как получилось с resume и как без resume.
сигнал не прекращался после отпускания кнопки.
А! Это важно. Запомним. Сделайте пока второй тест, который я только что выложил.
Ну, что там, как оно? Попробовали? У меня просто нет под рукой ничего (я еду в машине (не за рулём) с ноутбуком на коленях), а то я бы давно сам провёл все свои эксперименты.
С irrecv.resume() нажатие и удержание. То есть сигнал сам пропадает???
Без irrecv.resume() после одного нажатия программа перестает реагировать на манипуляции с пультом.
Окей. Промежуточные выводы
1. Забываем про вариант без resume. resume нужен и точка.
2. Я проавильно понял, что цепочка pressed-released появлялась в процессе постоянного удержания кнопки? Так? Значит сигнал периодически пропадает.
В Вашей программе факт пропадания сигнала не обрабатывался никак. А обрабатывать надо.
Значит, делаем так. Сейчас мы выясним на какое время прпадает сигнал, а потом будем думать можно ли с этим бороться.
Время которое мы печатали в первом тест не подойдёт, т.к. там была ОЧЕНЬ тормозная операция вывода в Serial. Нам нужно измерить время более чисто. Сейчас я модивифицирую тест.
Минутку.
Запустите. Нажмите кнопку и держите пока не вылезет "отчёт". После этого можете отпускать, больше там ничего происходить не будет.
В отчёте после каждого "pressed" (кроме первого) будет написан временной интервал с предыдущего "released". Так мы узнаем на какое время пропадает сигнал.
Кстати, мне попробовать не на чем, так что если где лопухнулся - опишите внятно ошибку, я поправлю.
Теперь понятно почему все работает при однократном нажатии и не работает при удержании. Получается передатчик шлет прерывистый сигнал и серва просто не успевает повернуться как сигнал опять пропадает.
Ага, ну, вот, теперь понятно.
Т.е. при постоянно нажатой кнопке сигнал на самом деле появляется на очень короткий интервал (время между pressed и последующим released везде меньше миллисекунды, а затем пропадает примерно на 110 мс. Затем снова появляется на такой же короткий интервал, затем снова пропадает на 110 мс и т.д.
Теперь понятно, почему у Вас ничерта не работало? Вы просто считали, что там постоянно "высокий уровень", а он идёт "короткими пиками" - это я о сигнале.
Это общее правило, прежде, чем программировать, исследуйте свою аппаратуру, чтобы точно знать как она работает.
Ну, теперь можно и к программированию приступить. Общая идея программы, которая отслеживает длительное нажатие может юыть такова.
1. Когда зафиксировали первое нажатие, запомнили время.
2. Ждём того, что случится раньше: появится следующее нажатие или истекут 120мс.
3) если раньше появилось следующее нажатие, запоминаем время и переходим к п.2
4) если раньше истёк интервал в 120мс, фиксируем факт отпускания кнопки.
Вот как-то так должно нормально работать. Вы согласны с такой логикой?
Попробуйте её реализовать без меня. Постарайтесь - всё у Вас получится. Если уж совсем зашьётесь ....
Но про предварительное исследование аппаратуры (как раз то, чем мы полдня занимались) - это общее правило! Всегда с этого начинайте - сначала найти все грабли, а потом уж ходить по грядке.
ОК. Спасибо. Логика понятна. Буду пробовать.
Вот както так, уже с плавным ходом сервы по длительности удержания. Более менее стабильно работает только с такими значениями "millis() - Time" и "дэлэем" в конце loop-а. Более менее стабильно потому, что при быстром попеременном и многократном нажатии LEFT и RIGHT происходит зависание МК, а шестилетний пользователь данного устройства гарантировано так делать будет и часто)). Помогает кнопка РЕЗЕТ. В двух предыдущих проектах была защита от зависаний по "watch dog timer". Но здесь он почему-то не работает плюс добавляет глюков в работу схемы. Вероятно виноваты таймеры... Буду ковыряться дальше...
Переделал часть кода на операторе switch (стр. 50 -61). Перестало работать. В чем ошибка?
Разобрался. Забыл про оператор break после каждого case.