Автоповороты Servo и Ручные повороты Servo

Otto
Offline
Зарегистрирован: 26.06.2016

Нужна помощь, переделываю напольный вентилятор, всё ОК, кроме одного момента с Servo.
Есть 2 функции:

  -  первая отвечает за плавные ручные повороты Servo влево-вправо на заданные углы при зажатии кнопки на ИК пульте;
  -  вторая отвечает за автоповороты влево-вправо на те же заданные углы при одинарном нажатии кнопки и выключении при нажатии другой кнопки.

Обе функции работают и плавно поворачивают Servo, но не могу побороть проблему:
Если включаем автоповороты, Servo ходит туда-сюда, останавливает, серва запоминает положение, всё классно. Но когда зажимаю кнопку ручных поворотов влево или вправо (естественно при выключенных автоповоротах), то Servo резко возвращается на угол при котором остановилась Servo в автоповоротах.

Как подозреваю, это происходит из-за несоответствия углов после преобразования функции map().
Нужно что бы вручную повернул на определённый угол и включил автоповороты, Servo пошла крутится от последнего положения. Выключил автоповороты, зажал кнопку влево или вправо, Servo так же медленно начала продолжать вращение в нужную сторону.

Оставил в коде только относящееся к поворотам для общего понимания.
Нужные 2 функции вывел под отдельный спойлер:

//Функция для Автоповоротов Servo
void auto_Rotation() {
   if (millis() - tmr_auto >= AUTO_DELAY_TIME_SERVO) {    // Если время больше или равно "AUTO_DELAY_TIME_SERVO", тогда...
    tmr_auto = millis();       // записываем время в "tmr_auto"
    auto_val += dir;           // Прибавляем "auto_val" значение градуса "dir"
    if (auto_val >= RIGHT_CORNER || auto_val <= LEFT_CORNER) dir = -dir;    // Разворачиваем в обратную сторону

    rotation.write(auto_val);  // Записываем угол в Servo
  }
}


//Функция для ручных поворотов Servo (влево - вправо при удержании кнопки)
void manual_Turns() {
  if (millis() - tmr_turn > TURNS_DELAY_TIME_SERVO) (Right_Turns = false), (Left_Turns = false);    // Если вермя больше "TURNS_DELAY_TIME_SERVO", тогда опускам флаги

  if (Right_Turns ==  true && transformation <= MAX_VALUE) transformation += counter;    // Если правый флаг поднят и значение "transformation" меньше "MAX_VALUE", то производим инкремент счётчика
  if (Left_Turns  ==  true && transformation >= MIN_VALUE) transformation -= counter;    // Если левый флаг поднят и значение "transformation" больше "MIN_VALUE", то производим декремент счётчика

  auto_val = constrain(auto_val, LEFT_CORNER, RIGHT_CORNER);    // Ограничим диапазон для безопасности от "LEFT_CORNER" до "RIGHT_CORNER"
  auto_val = map(transformation, MIN_VALUE, MAX_VALUE, LEFT_CORNER, RIGHT_CORNER);   // Задаём диапазон скорости и поворота Servo (углы поворота от "LEFT_CORNER" до "RIGHT_CORNER" градусов)

  rotation.write(auto_val);    // Записываем положение в Servo
}

Почти полный код (для понимания), вырезал всё лишнее:

//Servo (pin)
#define SERVOMOTOR 9  // pin для подключения Servo

//Прерывания (pin)
#define INTERRUPT_PIN_IR 2    // Пин с прерыванием к которому подключён ИК приёмник

//Настройки для ручных и автоповоротов Servo
#define RIGHT_CORNER 130            // Задаём максимальный поворот на ПРАВЫЙ угол
#define LEFT_CORNER 12              // Задаём максимальный поворот на ЛЕВЫЙ угол

#define MAX_VALUE 20000              // Максимальное значение для задания скорости движения Servo при ручных поворотах
#define MIN_VALUE 0                 // Минимальное значение для задания скорости движения Servo при ручных поворотах

#define AUTO_DELAY_TIME_SERVO 50    // Время повторений итераций(в миллисекундах) для автоповоротов. Чем больше время, тем медленее будут автоповороты
#define TURNS_DELAY_TIME_SERVO 100  // Время (в миллисекундах) после истечения которого опускаем флаги (если не поступил новый сигнал) при ручных поворотах Servo

#include <SoftServo.h>    // Библиотека для программного управления Servo (на базе millis/micros)
#include <NecDecoder.h>   // Лёгкая библиотека для декодирования ИК протокола NEC

//Коды кнопок пульта в HEX
#define IR_UP         0x1     // Кнопка вверх: ↑ Авто - Вращения Вкл.
#define IR_DOWN       0x81    // Кнопка вниз:  ↓ Авто - Вращения Выкл.
#define IR_LEFT       0x8A    // Кнопка <: « Влево
#define IR_RIGHT      0xB2    // Кнопка >: Вправо »


SoftServo rotation;       // Создаём Объект для Sertvo
NecDecoder ir;            // Создаём Объект для ИК приёмника

//Автоповороты Servo
int auto_val = 0;
int dir = 1;       // Градус поворота для каждой итерации функции
bool auto_RotatON  = false;     // Если "true", то включаем автоповороты Servo
uint32_t tmr_auto;                // Последнее обновление времени (для автоповоротов Servo)


//Ручные повороты Servo
int transformation = 0;     // Переменная для преобразования ручных поворотов через map()
int counter = 1;               // Увеличиваем перемещение на каждом шаге для ручных поворотов
bool RotatON  = false;      // Если "true", то включаем повороты Servo(влево - вправо)
bool Right_Turns = false;   // Если "true", то поворачиваем вручную Servo вправо (при зажатии кнопки вправо)
bool Left_Turns  = false;   // Если "true", то поворачиваем вручную Servo влево (при зажатии кнопки влево)
uint32_t tmr_turn = 0;      // Последнее обновление времени (для ручных поворотов Servo)


void setup() {
  //Получение ИК сигнала через прерывания
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN_IR), irIsr, FALLING);

  if (auto_val > RIGHT_CORNER || auto_val < LEFT_CORNER) auto_val = (RIGHT_CORNER - LEFT_CORNER) / 2;   // Если считанный из EEPROM угол выходит за пределы заданных, то переводим серво в центральное положение от заданных углов
  rotation.attach(SERVOMOTOR);  // Вкл. Servo
  rotation.delayMode();         // переключить в режим задержки (по умолчанию)
  rotation.write(auto_val);     // Записываем положение Servo из FRAM после включения питания
}


void loop() {
  rotation.tick();   //Вызываем тикер для работы "SoftServo.h"

  if (auto_RotatON == true) auto_Rotation();   // Запускаем функцию вкл. автоповоротов Servo
  if (RotatON == true) manual_Turns();         // Иначе работает функция ручных поворотов Servo
  ir_Decoder();   // Проверяем функцию "ir_Decoder()" на наличие приёма команд с ИК пульта
}


void irIsr() {
  ir.tick();    // Вызывать при ОТРИЦАТЕЛЬНОМ (FALLING) фронте на пине ИК приемника в прерывании

  //При удержании любой кнопки срабатывает прерывание и перехватываем пустой ИК сигнал, считаем его за код кнопки. При отпускании кнопки считаем, что код перестал приниматься
  tmr_turn = millis();    // Записываем текущее время (для ручных поворотов Servo)
}


void ir_Decoder() {
  //Получаем ИК коды
  if (ir.available()) {             // Если пакет успешно принят, то...
    switch (ir.readCommand()) {   // Читаем команду
      case IR_UP:
        //Кнопка вверх: ↑ Авто - Вращения Вкл.
        RotatON = false;          // Опускаем флаг для ручных поворотов
        auto_RotatON = true;      // Поднимаем флаг для Вкл. Автоповоротов
        break;

      case IR_DOWN:
        //Кнопка вниз:  ↓ Авто - Вращения Выкл.
        RotatON = false;          // Опускаем флаг для ручных поворотов
        auto_RotatON = false;     // Опускаем флаг для Автоповоротов
        break;

      case IR_LEFT:
        //Кнопка <: « Влево
        auto_RotatON = false;     // Опускаем флаг для Автоповоротов
        RotatON = true;           // Поднимаем флаг для ручных поворотов
        Right_Turns = true;       // Поднимаем флаг при нажатии и удержании кнопки "« Влево"
        break;

      case IR_RIGHT:
        //Кнопка >: Вправо »
        auto_RotatON = false;    // Опускаем флаг для Автоповоротов
        RotatON = true;          // Поднимаем флаг для ручных поворотов
        Left_Turns  = true;      // Поднимаем флаг при нажатии и удержании кнопки "Вправо »"
        break;
    }
  }
}


//Функция для Автоповоротов Servo
void auto_Rotation() {
  if (millis() - tmr_auto >= AUTO_DELAY_TIME_SERVO) {    // Если время больше или равно "AUTO_DELAY_TIME_SERVO", тогда...
    tmr_auto = millis();                                    // записываем время в "tmr_auto"
    auto_val += dir;                                         // Прибавляем "auto_val" значение градуса "dir"
    if (auto_val >= RIGHT_CORNER || auto_val <= LEFT_CORNER) dir = -dir;    // Разворачиваем в обратную сторону

    rotation.write(auto_val);  // Записываем угол в Servo
  }
}


//Функция для ручных поворотов Servo (влево - вправо при удержании кнопки)
void manual_Turns() {
  if (millis() - tmr_turn > TURNS_DELAY_TIME_SERVO) (Right_Turns = false), (Left_Turns = false);    // Если вермя больше "TURNS_DELAY_TIME_SERVO", тогда опускам флаги

  if (Right_Turns ==  true && transformation <= MAX_VALUE) transformation += counter;    // Если правый флаг поднят и значение "transformation" меньше "MAX_VALUE", то производим инкремент счётчика
  if (Left_Turns  ==  true && transformation >= MIN_VALUE) transformation -= counter;    // Если левый флаг поднят и значение "transformation" больше "MIN_VALUE", то производим декремент счётчика

  auto_val = constrain(auto_val, LEFT_CORNER, RIGHT_CORNER);                             // Ограничим диапазон для безопасности от "LEFT_CORNER" до "RIGHT_CORNER"
  auto_val = map(transformation, MIN_VALUE, MAX_VALUE, LEFT_CORNER, RIGHT_CORNER);       // Задаём диапазон скорости и поворота Servo (углы поворота от "LEFT_CORNER" до "RIGHT_CORNER" градусов)

  rotation.write(auto_val);    // Записываем положение в Servo
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

Otto
Offline
Зарегистрирован: 26.06.2016

ЕвгенийП пишет:

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

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

Схема:

 

 

 

 

 

 

Плата в Arduino IDE:"MiniCore"
Код по вкладкам (по порядку):

1)

/*** НАСТРОЙКИ ***/
//Пины Реле (pin)
#define RELAY1 5    // Режим 1 - ПОНИЖЕННАЯ скорость первого режима через конденсатор
#define RELAY2 6    // Режим 1 - Низкая скорость
#define RELAY3 7    // Режим 2 - Средняя скорость
#define RELAY4 8    // Режим 3 - Высокая скорость

//Настройки Реле (ВКЛЮЧЕНИЕ по плюсу или минусу на управляющие контакты)
#define RELAY_TYPE LOW      // Выбор типа реле LOW или HIGH. (LOW - ВКЛЮЧЕНИЕ реле по МИНУСУ, HIGH - ВКЛЮЧЕНИЕ реле по ПЛЮСУ)
#define DELAY_REL_OFF 50    // Ожидание "delay()" (в миллисекундах), пока контакты реле разъединятся после отключения реле, что бы исключить одновременную работу двух и более реле при переключении

// - - - - - - - - - - - - - - //

//Led индикация (pin)
#define LED_GREAN 10   // Зелёный светодиод для индикации режима 1
#define LED_BLUE  11   // Синий светодиод для индикации режима 2
#define LED_RED   12   // Красный светодиод для индикации режима 3

//Настройки LED
#define LED_BLINK_LOW 600   // Частота мигания индикатора "LED_GREAN" (в миллисекундах) для режима ПОНИЖЕННОЙ скорости

// - - - - - - - - - - - - - - //

//Прерывания (pin)
#define INTERRUPT_PIN_IR 2    // Пин с прерыванием к которому подключён ИК приёмник

//Servo (pin)
#define SERVOMOTOR 9  // pin для подключения Servo

// - - - - - - - - - - - - - - //

//FRAM
#define ADDRR_FRAM 0b11   // Задаём адрес i2c для FRAM памяти в бинарном формате (0b11 в BIN равен 0x56 в HEX)
#define PAGE_FRAM   0     // Страница на FRAM памяти, куда будем писать данные (0 - первая страница, 1 - вторая страница)
#define SECTOR_FRAM 71    // Сектор в FRAM памяти, куда будем писать данные

// - - - - - - - - - - - - - - //

//Настройки таймера отключения вентилятора
#define TIMER_MODE1 3000    // Задаём время для таймера на отключение вентилятора (в миллисекундах) для режима 1
#define TIMER_MODE2 5000    // Задаём время для таймера на отключение вентилятора (в миллисекундах) для режима 1
#define TIMER_MODE3 7000    // Задаём время для таймера на отключение вентилятора (в миллисекундах) для режима 1
#define TIMER_MODE4 9000    // Задаём время для таймера на отключение вентилятора (в миллисекундах) для режима 1
#define TIMER_MODE5 11000   // Задаём время для таймера на отключение вентилятора (в миллисекундах) для режима 1
#define TIMER_MODE6 13000   // Задаём время для таймера на отключение вентилятора (в миллисекундах) для режима 1

// - - - - - - - - - - - - - - //

//Настройки для ручных и автоповоротов Servo
#define RIGHT_CORNER 130            // Задаём максимальный поворот на ПРАВЫЙ угол
#define LEFT_CORNER 12              // Задаём максимальный поворот на ЛЕВЫЙ угол

#define MAX_VALUE 5000              // Максимальное значение для задания скорости движения Servo при ручных поворотах
#define MIN_VALUE 0                 // Минимальное значение для задания скорости движения Servo при ручных поворотах

#define AUTO_DELAY_TIME_SERVO 50    // Время повторений итераций(в миллисекундах) для автоповоротов. Чем больше время, тем медленее будут автоповороты
#define TURNS_DELAY_TIME_SERVO 100  // Время (в миллисекундах) после истечения которого опускаем флаги (если не поступил новый сигнал) при ручных поворотах Servo

// - - - - - - - - - - - - - - //

//Коды кнопок пульта в HEX (8 бит)
#define IR_OK         0x48    // Кнопка OK: Off (Выключить ВСЁ)
#define IR_UP         0x1     // Кнопка вверх: ↑ Авто - Вращения Вкл.
#define IR_DOWN       0x81    // Кнопка вниз:  ↓ Авто - Вращения Выкл.
#define IR_LEFT       0x8A    // Кнопка <: « Влево
#define IR_RIGHT      0xB2    // Кнопка >: Вправо »
#define IR_1          0x80    // Кнопка 1: 1 режим (On)
#define IR_2          0x40    // Кнопка 2: 2 режим (On)
#define IR_3          0xC0    // Кнопка 3: 3 режим (On)
#define IR_4          0x20    // Кнопка 4: 1 режим (Timer1)
#define IR_5          0xA0    // Кнопка 5: 1 режим (Timer1)
#define IR_6          0x60    // Кнопк  6: 1 режим (Timer1)
#define IR_7          0xE0    // Кнопка 7: 2 режим (Timer2)
#define IR_8          0x10    // Кнопка 8: 2 режим (Timer2)
#define IR_9          0x90    // Кнопка 9: 2 режим (Timer2)
#define IR_10         0xA4    // Кнопка +10: 3 режим (Timer3)
#define IR_0          0x50    // Кнопка 0: 3 режим (Timer3)
#define IR_T_SEARCH   0x64    // Кнопка T-SEARCH: 3 режим (Timer3)
#define IR_STEP       0x0     // Кнопка STEP: 4 режим (Timer4)
#define IR_PLAY_PAUSE 0xA8    // Кнопка PLAY/PAUSE: 4 режим (Timer4)
#define IR_STOP       0x28    // Кнопка STOP: 4 режим (Timer4)
#define IR_SUBTITLE   0x14    // Кнопка SUBTITLE: LOW (On)
#define IR_ANGLE      0x94    // Кнопка ANGLE: LOW 1 режим (Timer1)
#define IR_AUDIO      0xE4    // Кнопка AUDIO: LOW 2 режим (Timer2)
#define IR_ZOOM       0x2     // Кнопка ZOOM: LOW 3 режим (Timer3)
#define IR_RANDOM     0x74    // Кнопка RANDOM: LOW 4 режим (Timer4)
#define IR_PROG       0x54    // Кнопка PROG: LOW 5 режим (Timer5)
#define IR_KARAOKE    0xA2    // Кнопка KARAOKE: LOW 6 режим (Timer6)

2)

/*** БИБЛИОТЕКИ ***/
#include <TimerMs.h>      // Многофункциональный программный таймер на системном таймере millis() для Arduino
#include <SoftServo.h>    // Библиотека для программного управления Servo (на базе millis/micros)
#include <NecDecoder.h>   // Лёгкая библиотека для декодирования ИК протокола NEC
#include <FRAM.h>         // Библиотека для работы с FRAM памятью (записываем положение Servo)
#include <microWire.h>    // Подключаем лёгкую библиотека со стандартным набором инструментов для работы с аппаратным I2C


/*** ОБЪЕКТЫ ***/
SoftServo rotation;       // Создаём Объект для Sertvo
NecDecoder ir;            // Создаём Объект для ИК приёмника
FRAM fram(ADDRR_FRAM);    // Задаём адрес i2c для FRAM памяти

// (период, мс), (0 не запущен / 1 запущен), (режим: 0 период / 1 таймер)
TimerMs mode1 (TIMER_MODE1, 0, 1);         // Режим 1 на все 4 скорости (Timer1)
TimerMs mode2 (TIMER_MODE2, 0, 1);         // Режим 2 на все 4 скорости (Timer2)
TimerMs mode3 (TIMER_MODE3, 0, 1);         // Режим 3 на все 4 скорости (Timer3)
TimerMs mode4 (TIMER_MODE4, 0, 1);         // Режим 4 на все 4 скорости (Timer4)
TimerMs mode5 (TIMER_MODE5, 0, 1);         // Режим 5 только на пониженную скорость (Timer5)
TimerMs mode6 (TIMER_MODE6, 0, 1);         // Режим 6 только на пониженную скорость (Timer6)
TimerMs blink_LOW (LED_BLINK_LOW, 0, 1);   // Таймер для мигания "LED_GREAN" в LOW режимах


/*** ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ***/
//Управление пинами МК
const byte Relay_Array[4] = {RELAY1, RELAY2, RELAY3, RELAY4};   // Массив реле
const byte Led_Array[3] = {LED_GREAN, LED_BLUE, LED_RED};       // Массив LED


//Автоповороты Servo
int servo_val = fram.ReadInt(PAGE_FRAM, SECTOR_FRAM);    // Считываем в переменную "servo_val" значения с сектора "SECTOR_FRAM", страницы "PAGE_FRAM"
int dir = 1;                  // Градус поворота для каждой итерации функции
bool auto_RotatON  = false;   // Если "true", то включаем автоповороты Servo
uint32_t tmr_auto = 0;        // Последнее обновление времени (для автоповоротов Servo)

//Ручные повороты Servo
int transformation = 0;     // Переменная для преобразования ручных поворотов через map()
int counter = 1;            // Увеличиваем перемещение на каждом шаге для ручных поворотов
bool RotatON  = false;      // Если "true", то включаем повороты Servo(влево - вправо)
bool Right_Turns = false;   // Если "true", то поворачиваем вручную Servo вправо (при зажатии кнопки вправо)
bool Left_Turns  = false;   // Если "true", то поворачиваем вручную Servo влево (при зажатии кнопки влево)
uint32_t tmr_turn = 0;      // Последнее обновление времени (для ручных поворотов Servo)


//Для режима LOW (мигание "LED_GREAN")
bool blink_ON = false;      // Если "true", то включаем мигание LED в режиме LOW


//Массив флагов для защиты от быстрого откл./вкл. (перещёлкивания) реле при повторном приёме ИК сигнала и в пределах одного режима
//Первый элемент массива "нулевой" используется для флага задержки "DELAY_REL_OFF" между переключениями реле, остальные флаги для переключения смены режимов
bool repeat[] = {0, 0, 0, 0, 0};    // Объявляем массив без явного задания размера

3)

void setup() {
  //Назначаем все РЕЛЕ и LED как ВЫХОД
  for (byte r = 0; r < 4; r++) pinMode(Relay_Array[r], OUTPUT);
  for (byte l = 0; l < 3; l++) pinMode(Led_Array[l], OUTPUT);

  //Выключаем все РЕЛЕ
  for (byte r = 0; r < 4; r++) digitalWrite(Relay_Array[r], !RELAY_TYPE);   // Инвертируем состояние заданное в "RELAY_TYPE"

  //Выключаем все LED
  for (byte l = 0; l < 3; l++) digitalWrite(Led_Array[l], LOW);

  //Получение ИК сигнала через прерывания
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN_IR), irIsr, FALLING);

  //Уставливаем в режим таймера (остановится после срабатывания)
  mode1.setTimerMode();   // 6 режимов таймеров для всех 4-х скоростей
  mode2.setTimerMode();
  mode3.setTimerMode();
  mode4.setTimerMode();
  mode5.setTimerMode();
  mode6.setTimerMode();

  blink_LOW.setPeriodMode();    // Установить в режим периода (будет постоянно перезапускаться после срабатывания)

  //Настройки Servo
  //Защита от сильного дребезжания и зависания Servo с большим потреблением тока
  if (servo_val > RIGHT_CORNER || servo_val < LEFT_CORNER) servo_val = (RIGHT_CORNER - LEFT_CORNER) / 2;   // Если считанный из FRAM угол выходит за пределы заданных, то переводим серво в центральное положение от заданных углов
  rotation.attach(SERVOMOTOR);  // Вкл. Servo
  rotation.delayMode();         // переключить в режим задержки (по умолчанию)

  rotation.write(servo_val);     // Записываем положение Servo из FRAM после включения питания
  fram.WriteInt(PAGE_FRAM, SECTOR_FRAM, servo_val);   //Записываем в сектор "SECTOR_FRAM", "PAGE_FRAM" страницы значение "servo_val"
}

4)

void loop() {
  rotation.tick();   //Вызываем тикер для работы "SoftServo.h"

  // Ждём нажатия кнопки Вкл. автоповоротов или ручных поворотов с ИК пульта, если получили сигнал, то проверяем какой:
  if (auto_RotatON == true) auto_Rotation();    // Запускаем функцию вкл. автоповоротов Servo
  if (RotatON == true) manual_Turns();          // Иначе работает функция ручных поворотов Servo

  //Если флаги "blink_ON" и "blink_LOW.tick()" подняты, то переключаем состояние "LED_GREAN" на противоположное с периодичностью таймера "blink_LOW"
  if (blink_ON == true && blink_LOW.tick() == true) digitalWrite(LED_GREAN, !digitalRead(LED_GREAN));

  ir_Decoder();   // Проверяем функцию "ir_Decoder()" на наличие приёма команд с ИК пульта
  mode_Timer();   // Проверяем функцию "mode_Timer()" на наличие работающих таймеров
}

5)

// Функция для обработки прерывания
void irIsr() {
  ir.tick();    // Вызывать при ОТРИЦАТЕЛЬНОМ (FALLING) фронте на пине ИК приемника в прерывании

  //При удержании любой кнопки срабатывает прерывание и перехватываем пустой ИК сигнал, считаем его за код кнопки. При отпускании кнопки считаем, что код перестал приниматься
  tmr_turn = millis();    // Записываем текущее время (для ручных поворотов Servo)
}


//Если вызвать функцию "WriteRelLed(pin_Rel, pin_Led)"с аргументами, то переключимся на выбранное РЕЛЕ и LED
//Функция упрощения Вкл./Выкл. РЕЛЕ и LED и уменьшения кода при многократных обращениях к пинам
void WriteRelLed(byte pin_Rel, byte pin_Led) {    // номер пина РЕЛЕ - "pin_Rel", номер пина LED - "pin_Led", которые будут Вкл.
  //Выключаем все РЕЛЕ
  digitalWrite(RELAY1, !RELAY_TYPE);    // Инвертируем состояние заданное в "RELAY_TYPE"
  digitalWrite(RELAY2, !RELAY_TYPE);
  digitalWrite(RELAY3, !RELAY_TYPE);
  digitalWrite(RELAY4, !RELAY_TYPE);

  //Останавливаем все таймеры
  mode1.stop();
  mode2.stop();
  mode3.stop();
  mode4.stop();
  mode5.stop();
  mode6.stop();
  blink_LOW.stop();   // Останавливаем таймер для мигающего LED в режиме LOW
  blink_ON = false;   // Опускаем флаг для мигающего LED в режиме LOW

  if (repeat[0] == 1) {     // Если флаг "repeat[0]" поднят, то включаем задержку
    delay(DELAY_REL_OFF);   // Ждём, пока контакты реле разъединятся после отключения
    repeat[0] = 0;          // Опускаем флаг
  }

  //Вкл. нужное РЕЛЕ из массива (в массиве счёт начинается с нуля, поэтому дописываем -1 от pin_Rel)
  digitalWrite(Relay_Array[pin_Rel - 1], RELAY_TYPE); // Вкл. нужное реле при состоянии "RELAY_TYPE"

  //Выключаем все LED
  digitalWrite(LED_GREAN, LOW);
  digitalWrite(LED_BLUE, LOW);
  digitalWrite(LED_RED, LOW);
  //Вкл. нужный LED из массива (в массиве счёт начинается с нуля, поэтому дописываем -1 от pin_Led)
  digitalWrite(Led_Array[pin_Led - 1], HIGH);   // Вкл. нужный LED
}



//Если вызвать функцию "WriteRelLed()" без аргументов, то выключатся все РЕЛЕ и LED
void WriteRelLed() {    // Выключаем ВСЁ
  //Выключаем все РЕЛЕ
  digitalWrite(RELAY1, !RELAY_TYPE);    // Инвертируем состояние заданное в "RELAY_TYPE"
  digitalWrite(RELAY2, !RELAY_TYPE);
  digitalWrite(RELAY3, !RELAY_TYPE);
  digitalWrite(RELAY4, !RELAY_TYPE);

  //Выключаем все LED
  digitalWrite(LED_GREAN, LOW);
  digitalWrite(LED_BLUE, LOW);
  digitalWrite(LED_RED, LOW);

  //Останавливаем все таймеры
  mode1.stop();
  mode2.stop();
  mode3.stop();
  mode4.stop();
  mode5.stop();
  mode6.stop();
  blink_LOW.stop();   // Останавливаем таймер для мигающего LED в режиме LOW
  blink_ON = false;   // Опускаем флаг для мигающего LED в режиме LOW

  //Блок защиты от быстрого откл./вкл. (перещёлкивания) реле при повторном приёме ИК сигнала и в пределах одного режима
  for (byte i = 0; i < sizeof(repeat); i++) {   // Вычисляем размер массива
    repeat[i] = 0;    // Присваиваем всему массиву ноль (false), тем самым делаем доступный любой режим для включения
  }
}

//Функция с поднятием одного заданного флага и опусканием всех остальных флагов (для защиты и одновременного включения двух релюшек)
void flag_Array(byte flag_Read) {
  for (byte f = 0; f < sizeof(repeat); f++) {   // Вычисляем размер массива и повторяем столько же раз, какой и размер массива
    for (byte i = 0; i < flag_Read; i++) {      // Подставляем число полученное в функцию
      repeat[f] = 0;                            // Опускаем все флаги
      repeat[flag_Read] = 1;                    // И поднимаем нужный (заданный) флаг
    }
  }
}

6)

//Функция приёма данных с ИК пульта
void ir_Decoder() {
  if (ir.available()) {   // Если пакет успешно принят, то...
    switch (ir.readCommand()) {   // Читаем команду
      case IR_OK:
        //Кнопка OK: Off (Выключить ВСЁ)
        WriteRelLed();   // Выключаем всё
        RotatON = false;        // Опускаем флаг для ручных поворотов
        auto_RotatON = false;   // Опускаем флаг для Автоповоротов
        break;

      case IR_UP:
        //Кнопка вверх: ↑ Авто - Вращения Вкл.
        RotatON = false;        // Опускаем флаг для ручных поворотов
        auto_RotatON = true;    // Поднимаем флаг для Вкл. Автоповоротов
        break;

      case IR_DOWN:
        //Кнопка вниз:  ↓ Авто - Вращения Выкл.
        RotatON = false;        // Опускаем флаг для ручных поворотов
        auto_RotatON = false;   // Опускаем флаг для Автоповоротов
        break;

      case IR_LEFT:
        //Кнопка <: « Влево
        auto_RotatON = false;   // Опускаем флаг для Автоповоротов
        RotatON = true;         // Поднимаем флаг для ручных поворотов
        Right_Turns = true;     // Поднимаем флаг при нажатии и удержании кнопки "« Влево"
        break;

      case IR_RIGHT:
        //Кнопка >: Вправо »
        auto_RotatON = false;  // Опускаем флаг для Автоповоротов
        RotatON = true;        // Поднимаем флаг для ручных поворотов
        Left_Turns  = true;    // Поднимаем флаг при нажатии и удержании кнопки "Вправо »"
        break;

      case IR_1:
        //Кнопка 1: 1 режим (On) (без таймера)
        if (repeat[1] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[1]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(1, 1);    // Вкл. "RELAY1" и "LED_GREAN" без таймера
        flag_Array(1);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_2:
        //Кнопка 2: 2 режим (On) (без таймера)
        if (repeat[2] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[2]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(2, 2);    // Вкл. "RELAY2" и "LED_BLUE" без таймера
        flag_Array(2);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_3:
        //Кнопка 3: 3 режим (On) (без таймера)
        if (repeat[3] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[3]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(3, 3);    // Вкл. "RELAY3" и "LED_RED" без таймера
        flag_Array(3);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_4:
        //Кнопка 4: 1 режим (Timer1)
        if (repeat[1] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[1]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(1, 1);    // Вкл. "RELAY1" и "LED_GREAN"
        mode1.start();        // Запускаем таймер для режима 1 (для всех 4-х скоростей)
        flag_Array(1);        // Поднимаем заданный флаг в скобках, а все остальные опускаем

        break;

      case IR_5:
        //Кнопка 5: 2 режим (Timer1)
        if (repeat[2] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[2]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(2, 2);    // Вкл. "RELAY2" и "LED_BLUE"
        mode1.start();        // Запускаем таймер для режима 1 (для всех 4-х скоростей)
        flag_Array(2);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_6:
        //Кнопк  6: 3 режим (Timer1)
        if (repeat[3] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[3]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(3, 3);    // Вкл. "RELAY3" и "LED_RED"
        mode1.start();        // Запускаем таймер для режима 1 (для всех 4-х скоростей)
        flag_Array(3);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_7:
        //Кнопка 7: 1 режим (Timer2)
        if (repeat[1] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[1]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(1, 1);    // Вкл. "RELAY1" и "LED_GREAN"
        mode2.start();        // Запускаем таймер для режима 2 (для всех 4-х скоростей)
        flag_Array(1);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_8:
        //Кнопка 8: 2 режим (Timer2)
        if (repeat[2] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[2]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(2, 2);    // Вкл. "RELAY2" и "LED_BLUE"
        mode2.start();        // Запускаем таймер для режима 2 (для всех 4-х скоростей)
        flag_Array(2);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_9:
        //Кнопка 9: 3 режим (Timer2)
        if (repeat[3] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[3]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(3, 3);    // Вкл. "RELAY3" и "LED_RED"
        mode2.start();        // Запускаем таймер для режима 2 (для всех 4-х скоростей)
        flag_Array(3);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_10:
        //Кнопка +10: 1 режим (Timer3)
        if (repeat[1] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[1]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(1, 1);    // Вкл. "RELAY1" и "LED_GREAN"
        mode3.start();        // Запускаем таймер для режима 3 (для всех 4-х скоростей)
        flag_Array(1);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_0:
        //Кнопка 0: 2 режим (Timer3)
        if (repeat[2] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[2]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(2, 2);    // Вкл. "RELAY2" и "LED_BLUE"
        mode3.start();        // Запускаем таймер для режима 3 (для всех 4-х скоростей)
        flag_Array(2);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_T_SEARCH:
        //Кнопка T-SEARCH: 3 режим (Timer3)
        if (repeat[3] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[3]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(3, 3);    // Вкл. "RELAY3" и "LED_RED"
        mode3.start();        // Запускаем таймер для режима 3 (для всех 4-х скоростей)
        flag_Array(3);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_STEP:
        if (repeat[1] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[1]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(1, 1);    // Вкл. "RELAY1" и "LED_GREAN"
        mode4.start();        // Запускаем таймер для режима 4 (для всех 4-х скоростей)
        flag_Array(1);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_PLAY_PAUSE:
        //Кнопка PLAY/PAUSE: 2 режим (Timer4)
        if (repeat[2] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[2]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(2, 2);    // Вкл. "RELAY2" и "LED_BLUE"
        mode4.start();        // Запускаем таймер для режима 4 (для всех 4-х скоростей)
        flag_Array(2);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_STOP:
        //Кнопка STOP: 3 режим (Timer4)
        if (repeat[3] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[3]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(3, 3);    // Вкл. "RELAY3" и "LED_RED"
        mode4.start();        // Запускаем таймер для режима 4 (для всех 4-х скоростей)
        flag_Array(3);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_SUBTITLE:
        //Кнопка SUBTITLE: LOW (On)
        if (repeat[4] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[4]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(4, 1);    // Вкл. "RELAY4" и "LED_GREAN"
        blink_LOW.start();    // Запускаем таймер для мигающего LED в режиме LOW
        blink_ON = true;      // Поднимаем флаг для мигающего LED в режиме LOW
        flag_Array(4);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_ANGLE:
        //Кнопка ANGLE: LOW (Timer1)
        if (repeat[4] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[4]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(4, 1);    // Вкл. "RELAY4" и "LED_GREAN"
        mode1.start();        // Запускаем таймер для режима 1 (для LOW скорости)
        blink_LOW.start();    // Запускаем таймер для мигающего LED в режиме LOW
        blink_ON = true;      // Поднимаем флаг для мигающего LED в режиме LOW
        flag_Array(4);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_AUDIO:
        //Кнопка AUDIO: LOW (Timer2)
        if (repeat[4] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[4]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(4, 1);    // Вкл. "RELAY4" и "LED_GREAN"
        mode2.start();        // Запускаем таймер для режима 2 (для LOW скорости)
        blink_LOW.start();    // Запускаем таймер для мигающего LED в режиме LOW
        blink_ON = true;      // Поднимаем флаг для мигающего LED в режиме LOW
        flag_Array(4);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_ZOOM:
        //Кнопка ZOOM: LOW (Timer3)
        if (repeat[4] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[4]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(4, 1);    // Вкл. "RELAY4" и "LED_GREAN"
        mode3.start();        // Запускаем таймер для режима 3 (для LOW скорости)
        blink_LOW.start();    // Запускаем таймер для мигающего LED в режиме LOW
        blink_ON = true;      // Поднимаем флаг для мигающего LED в режиме LOW
        flag_Array(4);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_RANDOM:
        //Кнопка RANDOM: LOW (Timer4)
        if (repeat[4] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[4]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(4, 1);    // Вкл. "RELAY4" и "LED_GREAN"
        mode4.start();        // Запускаем таймер для режима 4 (для LOW скорости)
        blink_LOW.start();    // Запускаем таймер для мигающего LED в режиме LOW
        blink_ON = true;      // Поднимаем флаг для мигающего LED в режиме LOW
        flag_Array(4);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_PROG:
        //Кнопка PROG: LOW (Timer5)
        if (repeat[4] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[4]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(4, 1);    // Вкл. "RELAY4" и "LED_GREAN"
        mode5.start();        // Запускаем таймер для режима 5 (для LOW скорости)
        blink_LOW.start();    // Запускаем таймер для мигающего LED в режиме LOW
        blink_ON = true;      // Поднимаем флаг для мигающего LED в режиме LOW
        flag_Array(4);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;

      case IR_KARAOKE:
        //Кнопка KARAOKE: LOW (Timer6)
        if (repeat[4] == 0) repeat[0] = 1;    // Если у элемента массива "repeat[4]" флаг опущен, то поднимаем флаг первому элементу "repeat[0]" для задержки "DELAY_REL_OFF"
        WriteRelLed(4, 1);    // Вкл. "RELAY4" и "LED_GREAN"
        mode6.start();        // Запускаем таймер для режима 6 (для LOW скорости)
        blink_LOW.start();    // Запускаем таймер для мигающего LED в режиме LOW
        blink_ON = true;      // Поднимаем флаг для мигающего LED в режиме LOW
        flag_Array(4);        // Поднимаем заданный флаг в скобках, а все остальные опускаем
        break;
    }
  }
}

7)

//Функция для Автоповоротов Servo
void auto_Rotation() {
  if (millis() - tmr_auto >= AUTO_DELAY_TIME_SERVO) {    // Если время больше или равно "AUTO_DELAY_TIME_SERVO", тогда...
    tmr_auto = millis();       // записываем время в "tmr_auto"
    servo_val += dir;           // Прибавляем "servo_val" значение градуса "dir"
    if (servo_val >= RIGHT_CORNER || servo_val <= LEFT_CORNER) dir = -dir;    // Разворачиваем в обратную сторону

    rotation.write(servo_val);  // Записываем угол в Servo
    fram.WriteInt(PAGE_FRAM, SECTOR_FRAM, servo_val);   //Записываем в сектор "SECTOR_FRAM", "PAGE_FRAM" страницы значение "servo_val"
  }
}


//Функция для ручных поворотов Servo (влево - вправо при удержании кнопки)
void manual_Turns() {
  if (millis() - tmr_turn > TURNS_DELAY_TIME_SERVO) (Right_Turns = false), (Left_Turns = false);    // Если вермя больше "TURNS_DELAY_TIME_SERVO", тогда опускам флаги

  if (Right_Turns ==  true && transformation <= MAX_VALUE) transformation += counter;    // Если правый флаг поднят и значение "transformation" меньше "MAX_VALUE", то производим инкремент счётчика
  if (Left_Turns  ==  true && transformation >= MIN_VALUE) transformation -= counter;    // Если левый флаг поднят и значение "transformation" больше "MIN_VALUE", то производим декремент счётчика

  servo_val = constrain(servo_val, LEFT_CORNER, RIGHT_CORNER);    // Ограничим диапазон для безопасности от "LEFT_CORNER" до "RIGHT_CORNER"
  servo_val = map(transformation, MIN_VALUE, MAX_VALUE, LEFT_CORNER, RIGHT_CORNER);   // Задаём диапазон скорости и поворота Servo (углы поворота от "LEFT_CORNER" до "RIGHT_CORNER" градусов)

  rotation.write(servo_val);    // Записываем положение в Servo
  fram.WriteInt(PAGE_FRAM, SECTOR_FRAM, servo_val);   //Записываем в сектор "SECTOR_FRAM", "PAGE_FRAM" страницы значение "servo_val"
}

8)

//Функция отключения всех РЕЛЕ, LED и Автоповоротов по истечению работы таймера
void mode_Timer() {
  if (mode1.tick() == true) {         // Если таймер режима 1 запущен/перезапущен (пришла команда с ИК пульта), то...
    if (mode1.active() == false) {    // Проверяем работает ли таймер (start/resume), если НЕ работает, то...
      WriteRelLed();          // Выключаем всё
      mode1.stop();           // Останавливаем таймер
      auto_RotatON = false;   // Опускаем флаг для Автоповоротов
    }
  }

  if (mode2.tick() == true) {         // Если таймер режима 2 запущен/перезапущен (пришла команда с ИК пульта), то...
    if (mode2.active() == false) {    // Проверяем работает ли таймер (start/resume), если НЕ работает, то...
      WriteRelLed();          // Выключаем всё
      mode2.stop();           // Останавливаем таймер
      auto_RotatON = false;   // Опускаем флаг для Автоповоротов
    }
  }

  if (mode3.tick() == true) {         // Если таймер режима 3 запущен/перезапущен (пришла команда с ИК пульта), то...
    if (mode3.active() == false) {    // Проверяем работает ли таймер (start/resume), если НЕ работает, то...
      WriteRelLed();          // Выключаем всё
      mode3.stop();           // Останавливаем таймер
      auto_RotatON = false;   // Опускаем флаг для Автоповоротов
    }
  }

  if (mode4.tick() == true) {         // Если таймер режима 4 запущен/перезапущен (пришла команда с ИК пульта), то...
    if (mode4.active() == false) {    // Проверяем работает ли таймер (start/resume), если НЕ работает, то...
      WriteRelLed();          // Выключаем всё
      mode4.stop();           // Останавливаем таймер
      auto_RotatON = false;   // Опускаем флаг для Автоповоротов
    }
  }


  if (mode5.tick() == true) {         // Если таймер режима 5 запущен/перезапущен (пришла команда с ИК пульта), то...
    if (mode5.active() == false) {    // Проверяем работает ли таймер (start/resume), если НЕ работает, то...
      WriteRelLed();          // Выключаем всё
      mode5.stop();           // Останавливаем таймер
      auto_RotatON = false;   // Опускаем флаг для Автоповоротов
    }
  }


  if (mode6.tick() == true) {         // Если таймер режима 6 запущен/перезапущен (пришла команда с ИК пульта), то...
    if (mode6.active() == false) {    // Проверяем работает ли таймер (start/resume), если НЕ работает, то...
      WriteRelLed();          // Выключаем всё
      mode6.stop();           // Останавливаем таймер
      auto_RotatON = false;   // Опускаем флаг для Автоповоротов
    }
  }
}

 

Otto
Offline
Зарегистрирован: 26.06.2016

Есть у кого идея как решить проблему?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

так и не понял откуда она возвращается если она стоит после остановок автоповоротов, а главное как она туда попала )))

Otto
Offline
Зарегистрирован: 26.06.2016

ua6em пишет:

так и не понял откуда она возвращается если она стоит после остановок автоповоротов, а главное как она туда попала )))

Включаю автоповороты, они крутятся себе, потом отключаю их, Servo остановился к примеру на угле в 50 градусов. Нужно что бы при нажатии кнопки ручных поворотов влево или вправо Servo продолжило движение в ту сторону, куда нажата кнопка с угла на котором остановилось Servo после автоповоротов.

И так же на ручных поворотах остановилась Servo на угле к примеру в 75 градусов и при включении Автоповоротов движение продолжилось с остановленного угла.

А то в моём случае после отключения автоповоротов Servo останавливается, нажимаю к примеру влево, и Servo резко возвращается в крайний угол и оттуда начинает поворачивать.
 

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

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

 

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

Otto
Offline
Зарегистрирован: 26.06.2016

b707 пишет:

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

 

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

Пробовал, но как то не так что-то делаю  и не получилось добиться результата.
Касаемо скетча, его с самого начала выложил, без всего лишнего, что бы не копаться.

Продублирую под спойлер:
 

//Servo (pin)
#define SERVOMOTOR 9  // pin для подключения Servo

//Прерывания (pin)
#define INTERRUPT_PIN_IR 2    // Пин с прерыванием к которому подключён ИК приёмник

//Настройки для ручных и автоповоротов Servo
#define RIGHT_CORNER 130            // Задаём максимальный поворот на ПРАВЫЙ угол
#define LEFT_CORNER 12              // Задаём максимальный поворот на ЛЕВЫЙ угол

#define MAX_VALUE 20000              // Максимальное значение для задания скорости движения Servo при ручных поворотах
#define MIN_VALUE 0                 // Минимальное значение для задания скорости движения Servo при ручных поворотах

#define AUTO_DELAY_TIME_SERVO 50    // Время повторений итераций(в миллисекундах) для автоповоротов. Чем больше время, тем медленее будут автоповороты
#define TURNS_DELAY_TIME_SERVO 100  // Время (в миллисекундах) после истечения которого опускаем флаги (если не поступил новый сигнал) при ручных поворотах Servo

#include <SoftServo.h>    // Библиотека для программного управления Servo (на базе millis/micros)
#include <NecDecoder.h>   // Лёгкая библиотека для декодирования ИК протокола NEC

//Коды кнопок пульта в HEX
#define IR_UP         0x1     // Кнопка вверх: ↑ Авто - Вращения Вкл.
#define IR_DOWN       0x81    // Кнопка вниз:  ↓ Авто - Вращения Выкл.
#define IR_LEFT       0x8A    // Кнопка <: « Влево
#define IR_RIGHT      0xB2    // Кнопка >: Вправо »


SoftServo rotation;       // Создаём Объект для Sertvo
NecDecoder ir;            // Создаём Объект для ИК приёмника

//Автоповороты Servo
int auto_val = 0;
int dir = 1;       // Градус поворота для каждой итерации функции
bool auto_RotatON  = false;     // Если "true", то включаем автоповороты Servo
uint32_t tmr_auto;                // Последнее обновление времени (для автоповоротов Servo)


//Ручные повороты Servo
int transformation = 0;     // Переменная для преобразования ручных поворотов через map()
int counter = 1;               // Увеличиваем перемещение на каждом шаге для ручных поворотов
bool RotatON  = false;      // Если "true", то включаем повороты Servo(влево - вправо)
bool Right_Turns = false;   // Если "true", то поворачиваем вручную Servo вправо (при зажатии кнопки вправо)
bool Left_Turns  = false;   // Если "true", то поворачиваем вручную Servo влево (при зажатии кнопки влево)
uint32_t tmr_turn = 0;      // Последнее обновление времени (для ручных поворотов Servo)


void setup() {
  //Получение ИК сигнала через прерывания
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN_IR), irIsr, FALLING);

  if (auto_val > RIGHT_CORNER || auto_val < LEFT_CORNER) auto_val = (RIGHT_CORNER - LEFT_CORNER) / 2;   // Если считанный из EEPROM угол выходит за пределы заданных, то переводим серво в центральное положение от заданных углов
  rotation.attach(SERVOMOTOR);  // Вкл. Servo
  rotation.delayMode();         // переключить в режим задержки (по умолчанию)
  rotation.write(auto_val);     // Записываем положение Servo из FRAM после включения питания
}


void loop() {
  rotation.tick();   //Вызываем тикер для работы "SoftServo.h"

  if (auto_RotatON == true) auto_Rotation();   // Запускаем функцию вкл. автоповоротов Servo
  if (RotatON == true) manual_Turns();         // Иначе работает функция ручных поворотов Servo
  ir_Decoder();   // Проверяем функцию "ir_Decoder()" на наличие приёма команд с ИК пульта
}


void irIsr() {
  ir.tick();    // Вызывать при ОТРИЦАТЕЛЬНОМ (FALLING) фронте на пине ИК приемника в прерывании

  //При удержании любой кнопки срабатывает прерывание и перехватываем пустой ИК сигнал, считаем его за код кнопки. При отпускании кнопки считаем, что код перестал приниматься
  tmr_turn = millis();    // Записываем текущее время (для ручных поворотов Servo)
}


void ir_Decoder() {
  //Получаем ИК коды
  if (ir.available()) {             // Если пакет успешно принят, то...
    switch (ir.readCommand()) {   // Читаем команду
      case IR_UP:
        //Кнопка вверх: ↑ Авто - Вращения Вкл.
        RotatON = false;          // Опускаем флаг для ручных поворотов
        auto_RotatON = true;      // Поднимаем флаг для Вкл. Автоповоротов
        break;

      case IR_DOWN:
        //Кнопка вниз:  ↓ Авто - Вращения Выкл.
        RotatON = false;          // Опускаем флаг для ручных поворотов
        auto_RotatON = false;     // Опускаем флаг для Автоповоротов
        break;

      case IR_LEFT:
        //Кнопка <: « Влево
        auto_RotatON = false;     // Опускаем флаг для Автоповоротов
        RotatON = true;           // Поднимаем флаг для ручных поворотов
        Right_Turns = true;       // Поднимаем флаг при нажатии и удержании кнопки "« Влево"
        break;

      case IR_RIGHT:
        //Кнопка >: Вправо »
        auto_RotatON = false;    // Опускаем флаг для Автоповоротов
        RotatON = true;          // Поднимаем флаг для ручных поворотов
        Left_Turns  = true;      // Поднимаем флаг при нажатии и удержании кнопки "Вправо »"
        break;
    }
  }
}


//Функция для Автоповоротов Servo
void auto_Rotation() {
  if (millis() - tmr_auto >= AUTO_DELAY_TIME_SERVO) {    // Если время больше или равно "AUTO_DELAY_TIME_SERVO", тогда...
    tmr_auto = millis();                                    // записываем время в "tmr_auto"
    auto_val += dir;                                         // Прибавляем "auto_val" значение градуса "dir"
    if (auto_val >= RIGHT_CORNER || auto_val <= LEFT_CORNER) dir = -dir;    // Разворачиваем в обратную сторону

    rotation.write(auto_val);  // Записываем угол в Servo
  }
}


//Функция для ручных поворотов Servo (влево - вправо при удержании кнопки)
void manual_Turns() {
  if (millis() - tmr_turn > TURNS_DELAY_TIME_SERVO) (Right_Turns = false), (Left_Turns = false);    // Если вермя больше "TURNS_DELAY_TIME_SERVO", тогда опускам флаги

  if (Right_Turns ==  true && transformation <= MAX_VALUE) transformation += counter;    // Если правый флаг поднят и значение "transformation" меньше "MAX_VALUE", то производим инкремент счётчика
  if (Left_Turns  ==  true && transformation >= MIN_VALUE) transformation -= counter;    // Если левый флаг поднят и значение "transformation" больше "MIN_VALUE", то производим декремент счётчика

  auto_val = constrain(auto_val, LEFT_CORNER, RIGHT_CORNER);                             // Ограничим диапазон для безопасности от "LEFT_CORNER" до "RIGHT_CORNER"
  auto_val = map(transformation, MIN_VALUE, MAX_VALUE, LEFT_CORNER, RIGHT_CORNER);       // Задаём диапазон скорости и поворота Servo (углы поворота от "LEFT_CORNER" до "RIGHT_CORNER" градусов)

  rotation.write(auto_val);    // Записываем положение в Servo
}

 

Otto
Offline
Зарегистрирован: 26.06.2016

Тут в самом низу 2 функции

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

Otto пишет:

Пробовал, но как то не так что-то делаю  и не получилось добиться результата.

покажите, как пробовали

Otto
Offline
Зарегистрирован: 26.06.2016

b707 пишет:

Otto пишет:

Пробовал, но как то не так что-то делаю  и не получилось добиться результата.

покажите, как пробовали

В глобальной переменной int transformation = tmr_auto;

А дальше затык с функцией map(), там сильно разнятся значения присвоенные "transformation" и "MAX_VALUE / MIN_VALUE".

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

Otto пишет:

сильно разнятся значения присвоенные "transformation" и "MAX_VALUE / MIN_VALUE".

а как так может быть. что пределы МАП в авто и в ручном режиме - разные? серва то одна и та же, пределы поворотов тоже одни и те же....

 

 

Добавка - я думаю именно в этом корень ваших проблем. Зачем вы задаете повороты в ручном и авто режиме в разных системах координат?

tmr_auto - в градусах, а  transformation - в каких-то странных единицах от 0 до 2000

Что мешает в ручном режиме тоже использовать градусы. например?

Из-за того, что вы повороты задаете по разному, при передаче угла из одного режима в другой получается ерунда:

Otto пишет:

В глобальной переменной int transformation = tmr_auto;

так нельзя,  у вас же две эти переменные в разных системах

 

Kakmyc
Offline
Зарегистрирован: 15.01.2018

А для чего вообще значение скорости переносить пропорционально в значение угла ?

Otto
Offline
Зарегистрирован: 26.06.2016

b707 пишет:

Otto пишет:

сильно разнятся значения присвоенные "transformation" и "MAX_VALUE / MIN_VALUE".

а как так может быть. что пределы МАП в авто и в ручном режиме - разные? серва то одна и та же, пределы поворотов тоже одни и те же....

 

 

Добавка - я думаю именно в этом корень ваших проблем. Зачем вы задаете повороты в ручном и авто режиме в разных системах координат?

tmr_auto - в градусах, а  transformation - в каких-то странных единицах от 0 до 2000

Что мешает в ручном режиме тоже использовать градусы. например?

Из-за того, что вы повороты задаете по разному, при передаче угла из одного режима в другой получается ерунда:

Otto пишет:

В глобальной переменной int transformation = tmr_auto;

так нельзя,  у вас же две эти переменные в разных системах

 

 

Об этом упомянул вначале, что проблема скорее всего с преобразованием в map().

<< Что мешает в ручном режиме тоже использовать градусы. например?

Только ЗА, но не догоню как, точнее чем и на что заменить в преобразовании map(), подкиньте идею пожалуйста.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Какое нафиг преобразование ?
Зачем ?
Угол поворота , он и есть угол поворота.
Зачем его во что-то переводить ?

Otto
Offline
Зарегистрирован: 26.06.2016

Kakmyc пишет:
Какое нафиг преобразование ? Зачем ? Угол поворота , он и есть угол поворота. Зачем его во что-то переводить ?

Что бы задать скорость поворотов. Если есть идея как уменьшить скорость поворотов, то напишите

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Otto пишет:

Kakmyc пишет:
Какое нафиг преобразование ? Зачем ? Угол поворота , он и есть угол поворота. Зачем его во что-то переводить ?

Что бы задать скорость поворотов. Если есть идея как уменьшить скорость поворотов, то напишите

Так скорость поворотов , это время между инкрементом/декрементом угла.
Нафига ВРЕМЯ ПАУЗЫ между переключениями переводить в угол ???

Otto
Offline
Зарегистрирован: 26.06.2016

Kakmyc пишет:
Otto пишет:

Kakmyc пишет:
Какое нафиг преобразование ? Зачем ? Угол поворота , он и есть угол поворота. Зачем его во что-то переводить ?

Что бы задать скорость поворотов. Если есть идея как уменьшить скорость поворотов, то напишите

Так скорость поворотов , это время между инкрементом/декрементом угла. Нафига ВРЕМЯ ПАУЗЫ между переключениями переводить в угол ???

 

Не писал бы на форум если бы всё было чётко и работало как часики.

Поэтому и попросил помощи, что бы исправить косяки

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Ещё раз попытаюсь объяснить.
Есть такой параметр как угол поворота.
Его мы передаём в функцию servo.write();
Этот угол мы меняем по определенному времени либо увеличиваем, либо уменьшаем.
Пауза между сменой угла будет задавать скорость поворота. Чем меньше пауза, тем больше скорость.
Так за каким хреном нужно переводить пропорционально значение ПАУЗЫ между сменой угла, к ЗНАЧЕНИЮ этого угла ?

Kakmyc
Offline
Зарегистрирован: 15.01.2018

За скорость поворотов отвечают строки 14 и 15. Они в коде вообще меняться не могут.

Вот примерно так функция ручного управления должна выглядеть:




void manual_Turns() {

if (millis() - tmr_turn > TURNS_DELAY_TIME_SERVO){ 
        tmr_turn=millis();       
     if (Right_Turns ==  true) auto_val+=counter; 
 if (Left_Turns  ==  true ) auto_val -= counter;    
Right_Turns=0;
Left_Turnd=0;
    }
auto_val = constrain(auto_val, LEFT_CORNER, RIGHT_CORNER);                        
 rotation.write(auto_val); 
}

 

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

Otto пишет:

Не писал бы на форум если бы всё было чётко и работало как часики.

Поэтому и попросил помощи, что бы исправить косяки

вы вообще не догоняете. что ли?

Вот у вас есть процедура void auto_Rotation() в который вы поворачиваете серву, прибавляя или убавляя угол поворота В ГРАДКСАХ.

так что мешает точно так же поворачивать серву вручную, тоже меняя ГРАДУСЫ, а не какой-то левый параметр ?

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

Otto. в дополнение к тому что выше - я бы еще в код для ручных поворотов добавил таймер, чтобы он вызывался не каждый проход loop(), как сейчас, а через определенный интервал.

По-хорошему ручные повороты вполне можно делать через ту же функцию auto_Rotation() - просто вызываете ее по кнопке на определенный период времени, да и все. И тогда преобразование МАП вообще станет ненужным...

 

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

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

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

Otto
Offline
Зарегистрирован: 26.06.2016

Kakmyc пишет:

За скорость поворотов отвечают строки 14 и 15. Они в коде вообще меняться не могут.

Вот примерно так функция ручного управления должна выглядеть:

void manual_Turns() {

if (millis() - tmr_turn > TURNS_DELAY_TIME_SERVO){ 
        tmr_turn=millis();       
     if (Right_Turns ==  true) auto_val+=counter; 
 if (Left_Turns  ==  true ) auto_val -= counter;    
Right_Turns=0;
Left_Turnd=0;
    }
auto_val = constrain(auto_val, LEFT_CORNER, RIGHT_CORNER);                        
 rotation.write(auto_val); 
}

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

Вот только есть минус, что без функции map Servo стал более грубо поворачиваться, заметны рывки, особенно при декременте, подбором времени и значения счётчика пока не удалось решить эту проблему.
 

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

Otto пишет:

Вот только есть минус, что без функции map Servo стал более грубо поворачиваться, заметны рывки, особенно при декременте, подбором времени и значения счётчика пока не удалось решить эту проблему.
 

то что вы пишете - глупость. В функции МАП нет никакой магии, которую нельзя было бы получить просто подбором шага. Из чего можно сделать вывод. что вы опять ошиблись в логике кода.

Если хотите советов - показывайте новый код