Радиоуправляемая тележка или танк на arduino nano

Sonrec
Offline
Зарегистрирован: 29.09.2021

Очень интересное решение, однако, если танк идет полным ходом - то получается при повороте нужно подтормаживать эту сторону трака(шасси), в то же самое время, если ход меньше полного(думаю что надо отталкиваться от половины газа) то скорость надо добавлять на противоположном траке(шасси). Но и тут не все гладко, нужна зависимость скорости поворота и скорости движения танка. тоесть, если мы на полном ходу включаем максимальный поворот на право(лево) то танк по идее должен остановить ту сторону и выдать максимальную скорость на противоположную. вот как это реализовать?!

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

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

в моём коде не останавливается, расчет идёт от канала скорости

Sonrec
Offline
Зарегистрирован: 29.09.2021

Видимо не тот код проверял... вечером потестирую ))) спасибо

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

Sonrec пишет:

Видимо не тот код проверял... вечером потестирую ))) спасибо

в коде должны быть такие строки:
 

speedA = (long)fspeed +  (lr*lr*(lr>=0? 1: -1));

speedB = (long)fspeed -  (lr*lr*(lr>=0? 1: -1));


	  

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

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

Sonrec
Offline
Зарегистрирован: 29.09.2021

Ну по средней линии думаю делать не стоит, так как если представить двухрычаговую систему управления, как было в танках Т34 например, то там либо полный газ, либо тормоз по одной из сторон, вот только это может привести к переворачиваю модели или сносу при попытке повернуть на полном ходу, я это и хотел учесть, что-то вроде "курсовой" устойчивости, только без электроники. То есть если полный газ, то выкрутить руль поворота больше чем на середину хода нельзя.... не знаю как бы это себя показало в реальной эксплуатации, дрифте на повороте... ))

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

Sonrec пишет:

Ну по средней линии думаю делать не стоит, так как если представить двухрычаговую систему управления, как было в танках Т34 например, то там либо полный газ, либо тормоз по одной из сторон, вот только это может привести к переворачиваю модели или сносу при попытке повернуть на полном ходу, я это и хотел учесть, что-то вроде "курсовой" устойчивости, только без электроники. То есть если полный газ, то выкрутить руль поворота больше чем на середину хода нельзя.... не знаю как бы это себя показало в реальной эксплуатации, дрифте на повороте... ))

это можно реализовать, даже наверное не для полного газа а процентов с 80%
там пяток строк добавить, но тележку на колёсах я ни разу не перевернул )))
Код ниже, как-то так )))
 

// версия 2021 года снижение реакции руля при скорости более 80%

volatile unsigned int rc1_data = 1500;
volatile unsigned int rc2_data = 1500;
volatile unsigned int rc3_data = 1500;
volatile unsigned long start_timeRC1 = 0; //Канал 1 - руль
volatile unsigned long start_timeRC2 = 0; //Канал 2 - ход/педаль газа
volatile unsigned long prevTime;
volatile long speedA;
volatile long speedB;
volatile byte flag_RC1 = 0;
volatile byte flag_RC2 = 0;

/***L298N***/
//Двигатель левого борта (А)
const int in1 = 4;    // direction pin 1
const int in2 = 7;    // direction pin 2
const int ena = 9;    // PWM pin to change speed
//Двигатель правого борта (Б)
const int in3 = 8;    // direction pin 1
const int in4 = 5;    // direction pin 2
const int enb = 6;    // PWM pin to change speed

int fspeed;           // скорость (ШИМ сигнал)
unsigned int fspeed_l = 0;
unsigned int fspeed_r = 0;
int lr;               // положение руля
int diff_lr = 2;      // дифференциал руля (от 2 до 5)
int minPwm = 50;
uint16_t period = 10;


void setup() {
  Serial.begin(115200);
  pinMode(in1, OUTPUT);      // connection to L298n
  pinMode(in2, OUTPUT);      // connection to L298n
  pinMode(ena, OUTPUT);      // connection to L298n
  pinMode(in3, OUTPUT);      // connection to L298n
  pinMode(in4, OUTPUT);      // connection to L298n
  pinMode(enb, OUTPUT);      // connection to L298n
  analogWrite(ena, 0);
  analogWrite(enb, 0);

  // Привязываем к Pin2 прерывание по фронту и спаду сигнала
  attachInterrupt(0, Rc1, CHANGE);
  // Привязываем к Pin3 прерывание по фронту и спаду сигнала
  attachInterrupt(1, Rc2, CHANGE);
} // END SETUP


void loop() {
  //  Serial.println(rc1_data);
  lr = map(rc1_data, 1000, 2000, -12, 12);
  //  Serial.println(rc2_data);

  if (rc2_data >= 1520)
  {
    // Если движение вперёд
    fspeed = map(rc2_data, 1520, 2000, minPwm, 200);
  }
  else if ( rc2_data <= 1480 )
  {
    // Если включили реверс .. д.б. ваще-то "иначе если .."
    fspeed = map(rc2_data, 1480, 1000, -minPwm, -200);
  }
  else
  {
    // , иначе стоп
    fspeed = 0;
  }

  // а теперь управляем моторами и нормируем скорости
  // только если прошел цикл (Fpwm=490гц или 2мсек!)
  // управлять моторами чаще чем раз в 10мсек - бессмысленно..

  if ( millis() - prevTime >= period )
  {
    if (rc2_data >= 1481 && rc2_data <= 1519) {
      speedA = 0;
      speedB = 0;
    }
    if (rc2_data <= 1480 || rc2_data >= 1520) {
      if (rc2_data <= 1150 || rc2_data >= 1850) { // скорость при которой реакцию руля уменьшаем в два раза
        speedA = (long)fspeed +  ((lr * lr) / diff_lr * (lr >= 0 ? 1 : -1)); 
        speedB = (long)fspeed -  ((lr * lr) / diff_lr * (lr >= 0 ? 1 : -1));
      } else {
        speedA = (long)fspeed +  (lr * lr * (lr >= 0 ? 1 : -1));
        speedB = (long)fspeed -  (lr * lr * (lr >= 0 ? 1 : -1));

      }
    } // ход только при нажатии педали газа
    if ( speedA > 255    ) {
      speedA =  255;
    }
    if ( speedB > 255    ) {
      speedB =  255;
    }
    if ( speedA < -255   ) {
      speedA = -255;
    }
    if ( speedB < -255   ) {
      speedB = -255;
    }
    if ( abs(speedA) < minPwm ) {
      speedA = 0;  // мотор не тянет, нет смысла жечь батарейку..
    }
    if ( abs(speedB) < minPwm ) {
      speedB = 0;
    }

    if ( speedA > 0     ) {
      aForward();
      analogWrite(ena,  (int)speedA);
    }
    else if ( speedA < 0 ) {
      aBackward();
      analogWrite(ena, -(int)speedA);
    }
    else                {
      aStop();
    }

    if ( speedB > 0     ) {
      bForward();
      analogWrite(enb,  (int)speedB);
    }
    else if ( speedB < 0 ) {
      bBackward();
      analogWrite(enb, -(int)speedB);
    }
    else                {
      bStop();
    }

    prevTime = millis();

    //  Serial.println(speedA, DEC);
    //  Serial.println(speedB, DEC);
  }
}

/******Обработчик прерывания по возрастанию и спаду сигнала с приёмника RC******/
void Rc1() {
  if (digitalRead(2) == HIGH && flag_RC1 == 0) {
    //сохраняем значение времени начала импульса
    start_timeRC1 = micros();
    flag_RC1 = 1;
  }
  if (digitalRead(2) == LOW && flag_RC1 == 1) {
    //сохраняем значение длительности импульса канала 1
    rc1_data = micros() - start_timeRC1;
    flag_RC1 = 0;
  }
}//END RC1

void Rc2() {
  if (digitalRead(3) == HIGH && flag_RC2 == 0) {
    //сохраняем значение времени начала импульса канала 2
    start_timeRC2 = micros();
    flag_RC2 = 1;
  }
  if (digitalRead(3) == LOW && flag_RC2 == 1) {
    //сохраняем значение длительности импульса
    rc2_data = micros() - start_timeRC2;
    flag_RC2 = 0;
  }
}//END RC2

//************** Функции работы с моторами привода *************//
void aForward() {
  digitalWrite(in1, LOW);
  delayMicroseconds(4); // блокируем сквозняки на всякий случай
  digitalWrite(in2, HIGH);
}

void bForward() {
  digitalWrite(in4, LOW);
  delayMicroseconds(4); // блокируем сквозняки на всякий случай
  digitalWrite(in3, HIGH);
}

void aBackward() {
  digitalWrite(in2, LOW);
  delayMicroseconds(4); // блокируем сквозняки на всякий случай
  digitalWrite(in1, HIGH);
}

void bBackward() {
  digitalWrite(in3, LOW);
  delayMicroseconds(4); // блокируем сквозняки на всякий случай
  digitalWrite(in4, HIGH);
}

void aStop() {
  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
}

void bStop() {
  digitalWrite(in3, LOW);
  digitalWrite(in4, LOW);
}

PS точки переключения взяты не абстрактно, а с рельных устройств имеющих раздрай (кораблики)

Sonrec
Offline
Зарегистрирован: 29.09.2021

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

 

Sonrec
Offline
Зарегистрирован: 29.09.2021

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

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

Sonrec
Offline
Зарегистрирован: 29.09.2021

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

это я скопировал из мониторинга порта, в коде ничего не менял....

 
Ch1: 1492  Ch2: 1516  speedA: 52  speedB: 54
Ch1: 1492  Ch2: 1516  speedA: 52  speedB: 54
Ch1: 1492  Ch2: 1512  speedA: 51  speedB: 53
Ch1: 1492  Ch2: 1516  speedA: 52  speedB: 54
Ch1: 1492  Ch2: 1516  speedA: 52  speedB: 54
Ch1: 1492  Ch2: 1512  speedA: 51  speedB: 53
спустя примерно 30 секунд - писк меняется и данные по порту текут другие
 
 
 
Ch1: 1492  Ch2: 1508  speedA: 0  speedB: 51
Ch1: 1492  Ch2: 1508  speedA: 0  speedB: 51
Ch1: 1492  Ch2: 1508  speedA: 0  speedB: 51
Ch1: 1492  Ch2: 1508  speedA: 0  speedB: 51
Ch1: 1492  Ch2: 1508  speedA: 0  speedB: 51
Ch1: 1492  Ch2: 1508  speedA: 0  speedB: 51
Ch1: 1492  Ch2: 1508  speedA: 0  speedB: 51
Ch1: 1492  Ch2: 1504  speedA: 0  speedB: 0
Ch1: 1492  Ch2: 1504  speedA: 0  speedB: 0
Ch1: 1492  Ch2: 1504  speedA: 0  speedB: 0
Ch1: 1492  Ch2: 1504  speedA: 0  speedB: 0
Ch1: 1492  Ch2: 1504  speedA: 0  speedB: 0
 
Sonrec
Offline
Зарегистрирован: 29.09.2021

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

Ch1: 1492  Ch2: 1508  speedA: 0  speedB: 0

Ch1: 1492  Ch2: 1508  speedA: 0  speedB: 0
Ch1: 1496  Ch2: 1508  speedA: 0  speedB: 0
Ch1: 1496  Ch2: 1532  speedA: 0  speedB: 0
Ch1: 1508  Ch2: 1532  speedA: 53  speedB: 53
Ch1: 1492  Ch2: 1504  speedA: 0  speedB: 
 
да, нужно думаю пробовать смещать изменение управления на процентов 30, так как 80 это совсем неудобно....
и если руль держать в одном положении а газ давать вперед-назад, то танк разворачивается, хотя, если логически подумать - то не должен был бы )))
надо еще погонять
спасибо за подсказку
ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

у меня аппаратура отстроенная, строго 1000 - 1500 -2000, калибруется через меню, моросит в пределах 1 единицы, можно защитный интервал от 1500 в ту и другую сторону увеличить, но вообще-то аппаратуру надо калибровать

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

PPS я использую по паре двигателей на борт, этой проблемы нет, при нейтральном руле идёт строго по прямой

lilik
Offline
Зарегистрирован: 19.10.2017

ua6em пишет:

Пропищать можно на двигателях переменного тока, как это делается на постоянных - не знаю

Сейчас ради хохмы подключил вместо пьезо моторредуктор жёлтый через 220 Ом. Еле слышно, но также как от пьезопищалки. Потом поставил ключ транзисторный - в тишине с 30-50 см слышна музыка чисто.

// НОВОГОДНЯЯ МЕЛОДИЯ
int tonePin = 7; 
int minus = 6;

const struct sound{ 
  int freq, duration, dly; 
  } sounds[] PROGMEM = {
    {293, 225, 250}, {493, 225, 250}, {440, 225, 250}, {391, 225, 250}, {293, 450, 750}, 
    {293, 225, 250}, {293, 225, 250}, {493, 225, 250}, {440, 225, 250}, {391, 225, 250}, 
    {329, 450, 750}, {329, 225, 250}, {329, 225, 250}, {523, 225, 250}, {493, 225, 250}, 
    {440, 225, 250}, {587, 450, 750}, {587, 225, 250}, {659, 225, 250}, {587, 225, 250}, 
    {523, 225, 250}, {440, 225, 250}, {493, 675, 750}, {293, 225, 250}, {293, 225, 250}, 
    {493, 225, 250}, {440, 225, 250}, {391, 225, 250}, {293, 450, 750}, {293, 225, 250}, 
    {293, 225, 250}, {493, 225, 250}, {440, 225, 250}, {391, 225, 250}, {329, 450, 750}, 
    {329, 225, 250}, {329, 225, 250}, {523, 225, 250}, {493, 225, 250}, {440, 225, 250}, 
    {587, 225, 250}, {587, 225, 250}, {587, 225, 250}, {587, 225, 250}, {659, 225, 250}, 
    {587, 225, 250}, {523, 225, 250}, {440, 225, 250}, {391, 450, 500}, {587, 450, 500}, 
    {493, 225, 250}, {493, 225, 250}, {493, 450, 500}, {493, 225, 250}, {493, 225, 250}, 
    {493, 450, 500}, {493, 225, 250}, {587, 225, 250}, {391, 337, 375}, {440, 112, 125}, 
    {493, 450, 1000},{523, 225, 250}, {523, 225, 250}, {523, 337, 375}, {523, 112, 125}, 
    {523, 225, 250}, {493, 225, 250}, {493, 225, 250}, {493, 112, 125}, {493, 112, 125}, 
    {493, 225, 250}, {440, 225, 250}, {440, 225, 250}, {493, 225, 250}, {440, 450, 500}, 
    {587, 450, 500}, {493, 225, 250}, {493, 225, 250}, {493, 450, 500}, {493, 225, 250},
    {493, 225, 250}, {493, 450, 500}, {493, 225, 250}, {587, 225, 250}, {391, 337, 375}, 
    {440, 112, 125}, {493, 450, 1000},{523, 225, 250}, {523, 225, 250},{523, 337, 375}, 
    {523, 112, 125}, {523, 225, 250}, {493, 225, 250}, {493, 225, 250}, {493, 112, 125}, 
    {493, 112, 125}, {587, 225, 250}, {587, 225, 250}, {523, 225, 250}, {440, 225, 250}, 
    {391, 675, 750}
};
const uint8_t melodieLength = sizeof(sounds) / sizeof(sound);
sound SoundSRAM;
    
void setup()
{
pinMode(tonePin,OUTPUT);
pinMode(minus,OUTPUT);
digitalWrite(minus, LOW);
Serial.begin(9600);
}

void midi() {
  
  Serial.println(melodieLength);
  for (uint8_t i = 0; i < melodieLength; i++) {

    memcpy_P( &SoundSRAM, &sounds[i], sizeof(sound));
    
    Serial.print(i);Serial.print("=");
    Serial.print(SoundSRAM.freq);Serial.print(",");
    Serial.print(SoundSRAM.duration);Serial.print(",");
    Serial.print(SoundSRAM.dly);Serial.println();
    
    tone(tonePin, SoundSRAM.freq, SoundSRAM.duration);
    delay(SoundSRAM.dly);
  }
}

void loop()
{
  midi();delay(5000);
}

 

lilik
Offline
Зарегистрирован: 19.10.2017

Ни у кого случайно других красивых массивов-мелодий нет?

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

lilik пишет:

Ни у кого случайно других красивых массивов-мелодий нет?

а слабо написать скетч формирующий маcсив через enum ? Тогда открываем ноты и просто пишем с листа

Sonrec
Offline
Зарегистрирован: 29.09.2021

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

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

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

файлсэйв настрой при обрыве связи чтобы приёмник выдавал по каналам 1500

Sonrec
Offline
Зарегистрирован: 29.09.2021

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

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

Sonrec пишет:

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


Взять тут библиотеку, добавить через ZIP библиотеки, в примерах будет консольная программа посмотреть, что прилетает с аппаратуры. Не забываем сказать слова благодарности ЕвгенийП )))

Sonrec
Offline
Зарегистрирован: 29.09.2021

Так с аппаратуры то видно через консоль порта - вывод сигнала - я выше показывал, больше то вроде ничего не надо, не думаю что что-то еще прилетает...  так то сигнал в уровне +-10 единиц держится от 1500

 

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

Начал переделывать скетч с использованием шины IBUS (использовать с приёмниками FS-IA6B и FS-IA10B)
должно быть доступно до 14 каналов, а это уже полное управление ТАНКОМ, башня, пушка, ходовые огни и т.д.

Проверка на реальном железе пока не проводилась:
 

// На базе версии 2021 года - снижение реакции руля при скорости более 80% (базовая)
//
// 13.11.2021 - исправлен на использование необходимого числа каналов
// 11.11.2021 - перевод на управление по шине iBUS
// аппаратура управления FlySky - перешитая на 14 каналов
//   приёмник FlySky IA-6B  (IA-10B - аналогично)
//    Распиновка приёмника
//      IBUS-TX   IBUS-RX (INPUT)
//      G V+  S   G V+  S - SBUS ( OUTPUT)
//      o  o  o   o  o  o
//      -----------------
//      *  *  *  *  *  *  * S         ─┐
//      *  *  *  *  *  *  * V+          │ BIND
//      *  *  *  *  *  *  * G(ND)     ─┘
//  CH  1  2  3  4  5  6    (PWM)
// PPM  *
// при подключении по IBUS выходы PWM задействованы
// Задействованные пины адруино nano
// D4,D5,D6,D7,D8,D9,D13,A0,A1

// Используемые библиотеки
// iBUS.h          - https://github.com/UA6EM/iBUS
// AltSoftSerial.h - https://github.com/UA6EM/AltSoftSerial

#define DEBUG

#include <iBUS.h>
#include <AltSoftSerial.h>
AltSoftSerial RC_IBUS = AltSoftSerial(A0, A1);
iBus ibus(RC_IBUS);

int channels_per_packet = 14;
uint32_t last_read = 0;
const int PERIOD = 50;
const int channels = 14;

/* Заготовка для обратного канала (сигналы датчиков передаваемые на пуль управления)
  iBUSTelemetry telemetry(11); // I use only PCINT0 interrupt vector, so you can use D8 to D13 pins.

    D8 .. D13 - генерируют запрос прерывания PCINT0
    A0 .. A5  - генерируют запрос прерывания PCINT1
    D0 .. D7  - генерируют запрос прерывания PCINT2
*/

volatile unsigned int rc_data[14]
{ 1500, 1500, 1500, 1500, 1500,
  1500, 1500, 1500, 1500, 1500,
  1500, 1500, 1500, 1500
};

volatile unsigned long start_timeRC1 = 0; //Канал 1 - руль
volatile unsigned long start_timeRC2 = 0; //Канал 2 - ход/педаль газа
volatile unsigned long prevTime;
volatile long speedA;
volatile long speedB;
volatile byte flag_RC1 = 0;
volatile byte flag_RC2 = 0;

/***L298N***/
//Двигатель левого борта (А)
const int in1 = 4;    // direction pin 1
const int in2 = 7;    // direction pin 2
const int ena = 9;    // PWM pin to change speed
//Двигатель правого борта (Б)
const int in3 = 8;    // direction pin 1
const int in4 = 5;    // direction pin 2
const int enb = 6;    // PWM pin to change speed

int fspeed;           // скорость (ШИМ сигнал)
unsigned int fspeed_l = 0;
unsigned int fspeed_r = 0;
int lr;               // положение руля
int diff_lr = 2;      // дифференциал руля (от 2 до 5)
int minPwm = 50;
uint16_t period = 25;


void setup() {
#ifdef DEBUG
  Serial.begin(115200);
#endif

  pinMode(in1, OUTPUT);         // connection to L298n
  pinMode(in2, OUTPUT);         // connection to L298n
  pinMode(ena, OUTPUT);         // connection to L298n
  pinMode(in3, OUTPUT);         // connection to L298n
  pinMode(in4, OUTPUT);         // connection to L298n
  pinMode(enb, OUTPUT);         // connection to L298n
  pinMode(LED_BUILTIN, OUTPUT); // индикация соединения по шине IBUS
  analogWrite(ena, 0);
  analogWrite(enb, 0);

  // Set timeout between received packets to 20 ms before considering it a lost connection
  ibus.set_alive_timeout(20);
  // Set minimum time between transmitting packets, to 5ms
  ibus.set_tx_period(5);

  if (ibus.is_alive()) {
    digitalWrite(LED_BUILTIN, HIGH);
#ifdef DEBUG
    Serial.println("IBUS is CONNECT");
#endif
  } else {
    digitalWrite(LED_BUILTIN, LOW);
#ifdef DEBUG
    Serial.println("IBUS BUSY");
#endif
  }
} // END SETUP


void loop() {
  read_channels();
  //  Serial.println(rc_data[0]);
#ifdef DEBUG
  // Serial.println(rc_data[0]);
#endif

  lr = map(rc_data[0], 1000, 2000, -12, 12);

  //  Serial.println(rc_data[1]);
#ifdef DEBUG
  // Serial.println(rc_data[1]);
#endif

  if (rc_data[1] >= 1520)
  {
    // Если движение вперёд
    fspeed = map(rc_data[1], 1520, 2000, minPwm, 200);
  }
  else if ( rc_data[1] <= 1480 )
  {
    // Если включили реверс .. д.б. ваще-то "иначе если .."
    fspeed = map(rc_data[1], 1480, 1000, -minPwm, -200);
  }
  else
  {
    // , иначе стоп
    fspeed = 0;
  }

  // а теперь управляем моторами и нормируем скорости
  // только если прошел цикл (Fpwm=490гц или 2мсек!)
  // управлять моторами чаще чем раз в 10мсек - бессмысленно..

  if ( millis() - prevTime >= period )
  {
    if (rc_data[1] >= 1481 && rc_data[1] <= 1519) {
      speedA = 0;
      speedB = 0;
    }
    if (rc_data[1] <= 1480 || rc_data[1] >= 1520) {
      if (rc_data[1] <= 1150 || rc_data[1] >= 1850) { // скорость при которой реакцию руля уменьшаем в два раза
        speedA = (long)fspeed +  ((lr * lr) / diff_lr * (lr >= 0 ? 1 : -1));
        speedB = (long)fspeed -  ((lr * lr) / diff_lr * (lr >= 0 ? 1 : -1));
      } else {
        speedA = (long)fspeed +  (lr * lr * (lr >= 0 ? 1 : -1));
        speedB = (long)fspeed -  (lr * lr * (lr >= 0 ? 1 : -1));

      }
    } // ход только при нажатии педали газа
    if ( speedA > 255    ) {
      speedA =  255;
    }
    if ( speedB > 255    ) {
      speedB =  255;
    }
    if ( speedA < -255   ) {
      speedA = -255;
    }
    if ( speedB < -255   ) {
      speedB = -255;
    }
    if ( abs(speedA) < minPwm ) {
      speedA = 0;  // мотор не тянет, нет смысла жечь батарейку..
    }
    if ( abs(speedB) < minPwm ) {
      speedB = 0;
    }

    if ( speedA > 0     ) {
      aForward();
      analogWrite(ena,  (int)speedA);
    }
    else if ( speedA < 0 ) {
      aBackward();
      analogWrite(ena, -(int)speedA);
    }
    else                {
      aStop();
    }

    if ( speedB > 0     ) {
      bForward();
      analogWrite(enb,  (int)speedB);
    }
    else if ( speedB < 0 ) {
      bBackward();
      analogWrite(enb, -(int)speedB);
    }
    else                {
      bStop();
    }

    prevTime = millis();

    //  Serial.println(speedA, DEC);
    //  Serial.println(speedB, DEC);
  }
}


// ******** Обработчик приёмника IBUS ******** //
void read_channels()
{
  // Only print every PERIOD ms
  if (millis() - last_read > PERIOD)
  {
    last_read = millis();
    if (ibus.is_alive())
    {
      digitalWrite(LED_BUILTIN, HIGH);
      for (int i = 0; i < channels; i++) {
        rc_data[i] = ibus.get_channel(i);
#ifdef DEBUG
        Serial.print("READ FROM IBUS RC");
        Serial.println(rc_data[i]);
#endif
      }
    } else {
#ifdef DEBUG
      Serial.println("BUSY IBUS ");
#endif
      digitalWrite(LED_BUILTIN, LOW);
    }
  }
}

//************** Функции работы с моторами привода *************//
void aForward() {
  digitalWrite(in1, LOW);
  delayMicroseconds(4); // блокируем сквозняки на всякий случай
  digitalWrite(in2, HIGH);
}

void bForward() {
  digitalWrite(in4, LOW);
  delayMicroseconds(4); // блокируем сквозняки на всякий случай
  digitalWrite(in3, HIGH);
}

void aBackward() {
  digitalWrite(in2, LOW);
  delayMicroseconds(4); // блокируем сквозняки на всякий случай
  digitalWrite(in1, HIGH);
}

void bBackward() {
  digitalWrite(in3, LOW);
  delayMicroseconds(4); // блокируем сквозняки на всякий случай
  digitalWrite(in4, HIGH);
}

void aStop() {
  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
}

void bStop() {
  digitalWrite(in3, LOW);
  digitalWrite(in4, LOW);
}