Управление сервами по rs485

klen7832

✩✩✩✩✩✩✩
17 Мар 2025
18
0
Есть антенный привод, с двумя сервами, одна врашает по азимуту, другая по углу цели.
Пульт управления - джойстик и lcd-дисплей, где отображаются углы поворота.
Но всё это не работает на большом расстоянии(больше 50м.), а нужно 100м. как минимум.
Знаю, что есть возможность передачи по rs485.
Не могли бы вы накидать схему и код, для соединения посредством 485?
Код и схему приложу. Там всё просто для программистов, но нет к сожалению рядом таких.
 

klen7832

✩✩✩✩✩✩✩
17 Мар 2025
18
0
Оформи код соответствующим тэгом, см. Правила
/* --------------------- Antenna v 1.2 -----------------------------
V.1.0 Реализовано:
  • Движение от стика + быстрый возврат на исходнуу позицию
  • Движение с защитой выхода за пределы мак. углов
  • Вывод на экран с кор. для удобства
V.1.1 Реализовано:
  • Выбор угла (центр позиции)
  • Запоминание угла при отключении питания
V.1.2 Реализовано:
  • Запоминание фактических углов
  • Плавный возврат к центру при включении и от кнопки.
  • Дисплей и центровка выведены в отдельные блоки
--------------- Разработанно группой ПаялоХаб ------------------------ */

// ------------------------------ Библиотеки ------------------------
#include <I2Cdev.h> // Подключаем библиотеку
#include <Servo.h> // Подключаем библиотеку
#include <LiquidCrystal_I2C.h> // Подключаем библиотеку
#include <EEPROM.h> //Подключаем библиотеку

// ------------------------------ Управление ------------------------
LiquidCrystal_I2C lcd(0x27,16,2); // обьявляем экран
Servo myservo1; // обьявляем серво
Servo myservo2; // обьявляем серво

// ------------------------------ Переменные ------------------------
int dirX = 90; //центр 90
int dirX_center = 0; //Указатель центра
float dirX_disp = 0; //Переменная для правельного отображения угла на дисплее
int dirY = 90; //пог 65, центр 155
int dirX_Go = 0; //шаг для серво (плавность)
int dirY_Go = 0; //шаг для серво (плавность)
int x; // ось х стик
int y; // ось y стик
int val = 1; // Кнопка стика
int work = 0; // режим работы

void setup() // основной код. выполняется разово
{
pinMode(5, INPUT); // ввкл. кнопки стика
digitalWrite(5, HIGH); // уст. напряжение
lcd.init(); // вкл. экрана
lcd.backlight(); // Включаем подсветку
lcd.clear(); // Очистка экрана
lcd.setCursor(0, 0); // Позиция курсора
lcd.print("X :"); // вывод инф.
lcd.setCursor(0, 1); // Позиция курсора
lcd.print("Y :"); // вывод инф.
dirX = (EEPROM.read(2)); // Читаем с памяти dirX
dirY = (EEPROM.read(3)); // Читаем с памяти dirY
if (dirX < 0) {dirX = 90;} // Защита
if (dirX > 180) {dirX = 90;} // Защита
if (dirY < 0) {dirY = 90;} // Защита
if (dirY > 90) {dirY = 90;} // Защита
if (dirX < 90) {CenterGo1();} //Центровка
if (dirX > 90) {CenterGo2();} //Центровка
while (dirY < 90) //Центровка
{
dirY = dirY + 2; ////Центровка
oled(); // Экран
myservo2.attach(9); //Вкл. серво
myservo2.write(dirY+65); //Направляем серво
delay(150); // задержка
myservo2.detach(); // откл. сервы
}
dirY = 90; // Контроль
myservo2.attach(9); //Вкл. серво
myservo2.write(dirY+65); //Направляем серво
delay(150); // задержка
myservo2.detach(); // откл. сервы
dirX_center = dirX_center + (EEPROM.read(1)); //Читаем с памяти 2 значение
dirX_center = dirX_center + (EEPROM.read(0)*100); //Читаем с памяти 1 значение
if (dirX_center <0 and dirX_center>360){dirX_center = 0;} //Защита от ошибок
delay (3000); // Задержка
lcd.clear(); // Очистка экрана
lcd.setCursor(0, 0); // Позиция курсора
lcd.print("Center:"); // вывод инф.
}

void loop() // основной код. выполняется циклично
{
//Работа 0 - поворотный механизм выключен. Задаем центр
if (work == 0)
{
val = digitalRead(5); // состояние кнопки (0 нажата кнопка, 1 - нет)
dirX_disp = analogRead(A0); // считываем данные по x со стика
if (dirX_disp <= 123 ) {dirX_center = dirX_center - 2;} //шаг для указания центра
if (dirX_disp > 123 and dirX_disp <= 462) {dirX_center = dirX_center - 1;} //шаг для указания центра
if (dirX_disp >= 562 and dirX_disp < 900) {dirX_center = dirX_center + 1;} //шаг для указания центра
if (dirX_disp > 900) {dirX_center = dirX_center + 2;} //шаг для указания центра

if (val == 0) //Если нажата кнопка серво (возврат на начальную позицию)
{
work = 1; // сброс движения серво
lcd.clear(); // Очистка экрана
lcd.setCursor(0, 0); // Позиция курсора
lcd.print("X :"); // вывод инф.
lcd.setCursor(0, 1); // Позиция курсора
lcd.print("Y :"); // вывод инф.
if (dirX_center < 100){EEPROM.write(0, 0);} // Записываем в память 1 значение
if (dirX_center >= 300) {dirX_center = dirX_center - 300; EEPROM.write(0, 3);} //Записываем в память 1 значение
if (dirX_center >= 200) {dirX_center = dirX_center - 200; EEPROM.write(0, 2);} //Записываем в память 1 значение
if (dirX_center >= 100) {dirX_center = dirX_center - 100; EEPROM.write(0, 1);} //Записываем в память 1 значение
EEPROM.write(1, dirX_center); // Записываем в память 2 значение
dirX_center = dirX_center + (EEPROM.read(0)*100); // Читаем с памяти 1 значение
if (dirX_center <0 and dirX_center>360){dirX_center = 0;} //Защита от ошибок
}

// дисплей
if (dirX_center < 0) {dirX_center = dirX_center + 360;} // защита
if (dirX_center >= 360) {dirX_center = dirX_center - 360;} // защита
lcd.setCursor(7, 0); // Позиция курсора
if (dirX_center < 10) {lcd.print(" ");} // вывод инф.
if (dirX_center >= 10 and dirX_center < 100) {lcd.print(" ");} // вывод инф.
if (dirX_center >= 100) {lcd.print(" ");} // вывод инф.
lcd.print(dirX_center); // вывод инф.
delay(150); // задержка
}

//Работа 1 - поворотный механизм включен
if (work == 1)
{
// Данные со стика
val = digitalRead(5); // состояние кнопки (0 нажата кнопка, 1 - нет)
x = analogRead(A1); // считываем данные по x со стика
if (x <= 123 ) {dirX = dirX + 2; dirX_Go = 1;} //шаг для серво (плавность)
if (x > 123 and x <= 462) {dirX = dirX + 1; dirX_Go = 1;} //шаг для серво (плавность)
if (x >= 562 and x < 900) {dirX = dirX - 1; dirX_Go = 1;} //шаг для серво (плавность)
if (x > 900) {dirX = dirX - 2; dirX_Go = 1;} //шаг для серво (плавность)
y = analogRead(A0); // считываем данные по y со стика
if (y <= 23 ) {dirY = dirY + 2; dirY_Go = 1;} //шаг для серво (плавность)
if (y > 23 and y <= 462) {dirY = dirY + 1; dirY_Go = 1;} //шаг для серво (плавность)
if (y >= 562 and y < 1000) {dirY = dirY - 1; dirY_Go = 1;} //шаг для серво (плавность)
if (y > 1000) {dirY = dirY - 2; dirY_Go = 1;} //шаг для серво (плавность)

// защита пределов углов
if (dirX < 0) {dirX = 0; dirX_Go = 0;} // Предел угла для x
if (dirX > 180) {dirX = 180; dirX_Go = 0;} // Предел угла для x
if (dirY < 0) {dirY = 0; dirY_Go = 0;} // Предел угла для y
if (dirY > 90) {dirY = 90; dirY_Go = 0;} // Предел угла для y

EEPROM.write(2, dirX); // Записываем в память X значение
EEPROM.write(3, dirY); // Записываем в память Y значение

// Перемещение серво
if (val == 0) //Если нажата кнопка серво (возврат на начальную позицию)
{
dirX_Go = 0; // сброс движения серво
if (dirX < 90) {CenterGo1();}
if (dirX > 90) {CenterGo2();}
while (dirY < 90)
{
dirY = dirY + 2; //плавное движение
oled(); //экран
myservo2.attach(9); //Вкл. серво
myservo2.write(dirY+65); //движение серво
delay(150); // задержка
myservo2.detach(); // откл. сервы
}
dirY = 90; //контроль
myservo2.attach(9); //Вкл. серво
myservo2.write(dirY+65); //движение серво
delay(150); // задержка
myservo2.detach(); // откл. сервы
}
if (dirX_Go == 1) // Если движение серво по оси х доступно
{
myservo1.attach(8); // вкл. серво
myservo1.write(dirX); // поворот серво
dirX_Go = 0; // сброс движения серво
}
if (dirY_Go == 1) // Если движение серво по оси y доступно
{
myservo2.attach(9); // вкл. серво
myservo2.write(dirY+65); // поворот серво
dirY_Go = 0; // сброс движения серво
}
oled(); //экран
delay(150); // задержка
myservo1.detach(); // откл. сервы
myservo2.detach(); // откл. сервы
}
}

void CenterGo1 ()
{
while (dirX < 90)
{
dirX = dirX + 2; //плавное движение
oled(); //экран
myservo1.attach(8); //Вкл. серво
myservo1.write(dirX); //Направляем серво
delay(150); // задержка
myservo1.detach(); // откл. сервы
}
dirX = 90; //Контроль
myservo1.attach(8); //Вкл. серво
myservo1.write(dirX); //Направляем серво
delay(150); // задержка
myservo1.detach(); // откл. сервы
oled(); //экран
}

void CenterGo2 ()
{
while (dirX > 90)
{
dirX = dirX - 2; //плавное движение
oled(); //экран
myservo1.attach(8); //Вкл. серво
myservo1.write(dirX); //Направляем серво
delay(150); // задержка
myservo1.detach(); // откл. сервы
}
dirX = 90; //Контроль
myservo1.attach(8); //Вкл. серво
myservo1.write(dirX); //Направляем серво
delay(150); // задержка
myservo1.detach(); // откл. сервы
oled(); //экран
}

void oled ()
{
// дисплей (с кор. показаний для удобства восприятия)
dirX_disp = -1.66*(dirX-90); //Находим изменение угла
dirX_disp = dirX_center + dirX_disp; //Находим изменение угла от центра
if (dirX_disp < 0) {dirX_disp = dirX_disp + 360;} // защита
if (dirX_disp >= 360) {dirX_disp = dirX_disp - 360;} // защита
lcd.setCursor(3, 1); // Позиция курсора
if (dirX_disp < 10) {lcd.print(" ");} // вывод инф.
if (dirX_disp >= 10 and dirX_disp < 100) {lcd.print(" ");} // вывод инф.
if (dirX_disp >= 100) {lcd.print(" ");} // вывод инф.
lcd.print(dirX_disp); // вывод инф.
lcd.setCursor(3, 0); // Позиция курсора
if (((dirY-90)*-1) < 10) {lcd.print(" ");} // вывод инф.
if (((dirY-90)-1) >= 10 and ((dirY-90)-1) < 100 ) {lcd.print(" ");} // вывод инф.
lcd.print((dirY-90)*-1); // вывод инф.
lcd.print(".00 "); // вывод инф.
}
 

ZamPoTeh

✩✩✩✩✩✩✩
2 Янв 2024
9
1
Сервы точно подключены к D8, D10? Может быть D9, D10? И ещё вопрос, сервы какие?
 

klen7832

✩✩✩✩✩✩✩
17 Мар 2025
18
0
@ZamPoTeh, да, точно. Дело в том, что на коротком отрезке витой пары все работает адекватно, но при увеличении длинны кабеля, более 30м., сервы сходят с ума - постоянные дергания, зависания. Питание в норме, тем более, даем 24В. с понижением. Видимо все-таки управляющему сигналу слишком далеко. Сервы обычные (270градусов./35кг. и 180гр./25кг.). Если нужно, сделаю фото.
 

klen7832

✩✩✩✩✩✩✩
17 Мар 2025
18
0
@ZamPoTeh, можно более подробно, что это, для чего, и что и куда припаивать? В табличном формате можно.

@ZamPoTeh, нам нужна длинна кабеля порядка 100м. Это поможет?
 

ZamPoTeh

✩✩✩✩✩✩✩
2 Янв 2024
9
1
Это усилитель, увеличит нагрузочную способность ардуинки, только питание на неё придется подавать отдельно

Поможет не поможет, не знаю, пробовать надо. Но это лучше чем просто выход ардуино. Вот статья о её подключении: https://www.joyta.ru/4575-mikrosxema-uln2003-opisanie-i-sxemy-primeneniya/
 

klen7832

✩✩✩✩✩✩✩
17 Мар 2025
18
0
@ZamPoTeh, с самой обвязкой разберёмся. Куда её цеплять на ардуино?

И вопрос по 485... не проще он, не надёжнее ли?
 

ZamPoTeh

✩✩✩✩✩✩✩
2 Янв 2024
9
1
У Вас сервы управляются ШИМ сигналом, при чём тут RS485. Микросхемку к выходу ардуино. Она 7-ми канальная, я бы попробовал даже выходы запараллелить, если нагрузочной способности на 100м витуху не хватит.
 

klen7832

✩✩✩✩✩✩✩
17 Мар 2025
18
0
@ZamPoTeh, ок.
Будем пробовать.
Можно подробнее по подключению этой микросхемы к ардуино? Что там и куда, на какой контакт микросхемы, и на какой пин платы?
 

poty

★★★★★★✩
19 Фев 2020
3,445
981

@klen7832,
расстояние между чем и чем должно быть 100м? Упоминание RS485 предполагает вынос управления (джойстик или что там у Вас), но Вы согласились на тестирование драйвера, а это уже между МП и сервоприводами.
 

klen7832

✩✩✩✩✩✩✩
17 Мар 2025
18
0
@poty, да, сервы управляются джойстиком, угол выводится на дисплей(схема ☝).
Немного не понял о тестировании .
МП - это, что? Извините, я не специалист в этом.

@poty, задача стоит в управлении на 100м. минимум.
 

poty

★★★★★★✩
19 Фев 2020
3,445
981
@klen7832, МП - это микропроцессор. Т.е., на проводах вынесены сервоприводы, а остальная часть (ардуино, экран, джойстик) находится в руках оператора?
 

klen7832

✩✩✩✩✩✩✩
17 Мар 2025
18
0
@ZamPoTeh, спасибо. Дельно.
Поясните, пунктирные линии на входах и выходах микросхемы. Это как понимать?
 

klen7832

✩✩✩✩✩✩✩
17 Мар 2025
18
0
@ZamPoTeh,понял, понял. Это паралели.

@ZamPoTeh,Теперь суть понял. Впаиается в управляющие цепи. Понял. 🤝🤝
Попробуем и этот вариант.
485 не отбрасываю, т.к. лучше всегда иметь запасной вариант.
 

poty

★★★★★★✩
19 Фев 2020
3,445
981
ULN2003 - микросхема с открытым коллектором и инвертирует сигнал. Для первого: на приёмной стороне нужно поставить сопротивление к положительной шине питания управления, сопротивление выбрать минимальное из допустимых для увеличения помехоустойчивости. Для второго - использовать оставшиеся транзисторы в микросхеме для обратной инверсии, либо учесть в коде.
Также нужны конденсаторы байпаса по питанию сервоприводов на приёмной стороне, если питание также передаётся по проводам.
P.S. RS485, как защита от помех, хороший вариант, но сильно усложнит жизнь.
 
Изменено:
  • Лойс +1
Реакции: klen7832

klen7832

✩✩✩✩✩✩✩
17 Мар 2025
18
0
@poty,
т.е. схема с микросемой сложнее? Не могли бы вы тоже отобразить графически всё?

@poty,
485 усложнит жизнь в каком плане? Сложности сборки, или непостоянства адекватной работы?
 

poty

★★★★★★✩
19 Фев 2020
3,445
981
@klen7832, RS 485 требует как передающего, так и приёмного модуля. На приёмной стороне, помимо, собственно, модуля RS 485, должно стоять что-то, что понимает протокол передачи (например, ещё одна Ардуино). Всё это вносит задержки в сигнал.
 
  • Лойс +1
Реакции: ZamPoTeh

klen7832

✩✩✩✩✩✩✩
17 Мар 2025
18
0

@poty,
Да, на сколько я понимаю, в этой схеме две ардуинки(одна в пульте, другая на приводе), а так же два модуля rs485. У меня есть понятие, что 485 встраиваются в саму схему, но конкретно это сделать не могу. Да и в коде нужно вносить изменения.


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

@ZamPoTeh, думаю, что меня так сразу смутило в вашем вопросе...

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


@ZamPoTeh,
 

Вложения

ZamPoTeh

✩✩✩✩✩✩✩
2 Янв 2024
9
1
В вашем коде используется библиотека servo.h. При помощи которых Вы управляете приводами ШИМ сигналом. Насторожило то, что пин D8 не поддерживает ШИМ.

Вы объявили две переменные типа servo: myservo1 и myservo2. Далее в скетче должно быть указано к каким выводам ардуино подключены данные сервы. В Вашем случае: myservo2.attach(9) т.е., 9-й пин. и myservo1.attach(8) т.е., 8-й пин. Вы провода правильно припаяли?

Попробуйте серву 1 с вывода D8 подключить к выводу D10. И в скетче везде где у Вас происходит к ней обращение (myservo1.attach(8) ) заменить 8 на 10.
 

klen7832

✩✩✩✩✩✩✩
17 Мар 2025
18
0

@ZamPoTeh,
Не вопрос, попробую. В начале так и было, но в поисках причины мы узрели, что на 10 пине есть какое то сопротивление. Не короткое, а именно сопротивление. Мы перепаяли на другой пин.

Но с другой стороны, сейчас, в таком виде, на коротком о резке витой пары всё работает как нужно... Думаю, что не в этом дело.
Или прямо суть нужно припаять на 10 пин?

Мы поргешили на ардуинку, но в тот момент другой не было, перепаяли просто на 8 пин.
А потом поставил от греха подальше другую плату, и код не меняли. Оставили на 8 и 9.


@ZamPoTeh,
Да, сделаю. 🤝
Согласен, но удивляет, что на коротком отрезке всё работает


@ZamPoTeh,
Перепаяли. Такая же картина. На 50м. вроде одна серва(вериикаль) начинает работать, но при подключении второй(горизонт), в них вселяется злой дух, хоть батюшку зови.
На метровом отрезке всё норм работает.
 
Изменено:

ZamPoTeh

✩✩✩✩✩✩✩
2 Янв 2024
9
1
Я не большой в этом специалист, но в описании выводов ардуино нано указано, что на D8 ШИМ отсутствует.