Имитация нажатия кнопки с помощью последовательного монитора для запуска сервопривода
Я новичок в Arduino и C, и у меня возникли проблемы с этим скетчем. Я работаю над проектом по управлению 6 сервоприводами с помощью виртуальных кнопок. Когда я удерживаю нажатой кнопку, сервопривод плавно движется вперед до определенной фиксированной точки, а когда я отпускаю его, он возвращается в фиксированную точку. Сначала я протестировал это с помощью одного сервопривода и реальной кнопки на макете, используя следующий скетч.
#include<Servo.h>
int switch1;
float switch1Smoothed;
float switch1Prev;
Servo myservo1;
float a=1200.0; //нижний предел угла
void setup() {
Serial.begin(115200);
pinMode(A1,INPUT_PULLUP);
myservo1.attach(9);
myservo1.writeMicroseconds(a);
switch1Prev=a;
}
void loop() {
//Двигатель 1
switch1 = digitalRead(A1);// переключатель чтения | будет непрерывно возвращать 1 или 0
//Serial.println(переключатель 1);
switch1 = switch1 * 2400; // умножить на ограничение угла
// *** сглаживание ***
switch1Smoothed = (switch1 * 0.05) + (switch1Prev * 0.95);
if(switch1Smoothed>=a){
switch1Prev = switch1Smoothed;
}
else{
switch1Prev = a;
}
// *** окончание сглаживания ***
// Serial.print(switch1); // печать на последовательный терминал/плоттер
// Serial.print(" , ");
// Serial.println(переключатель 1 сглажен);
myservo1.writeMicroseconds(switch1Smoothed) ;
delay(10); // повторять цикл 100 раз в секунду
}
Это работает просто отлично. Сейчас я работаю с NodeRed, создателем потока JavaScript с низким уровнем кода, где находится моя виртуальная кнопка, и я настроил ее на отправку 0 или 1 в виде строк в зависимости от того, нажата кнопка или нет через последовательную связь. Это конфигурация последовательного узла на NodeRed:
И я написал короткую функцию на Arduino, чтобы проанализировать входящую строку из последовательного соединения, преобразовать ее обратно в целое число, а затем использовать ее в качестве моего ввода "кнопка". Однако, похоже, что либо моя логика ошибочна, либо мой код вызван тем, что мой сервопривод ведет себя не так, как с физической кнопкой.
Вот измененный скетч:
#include <Servo.h>
int switch1;
float switch1Smoothed;
float switch1Prev;
Servo myservo1;
float a=1200.0;
String readString, mot1, mot2;
void setup() {
Serial.begin(9600);
myservo1.attach(9);
myservo1.writeMicroseconds(a);
switch1Prev=a;
}
void captureChar(int& n1){ //использование ссылки для передачи значения
while (Serial.available()) {
delay(3); //задержка, позволяющая заполнить буфер
if (Serial.available() >0) {
char c = Serial.read(); //получает один байт из последовательного буфера
readString += c; //делает строку ReadString
}
}
if (readString.length() >0) {
mot1 = readString.substring(0,1);
mot2 = readString.substring(1,2);
n1 = mot1.toInt();
//n2 = mot2.toInt();
readString="";
}
}
void loop(){
captureChar(switch1);
switch1 = switch1 * 2400; // умножить на максимальный предел угла
// *** сглаживание ***
switch1Smoothed = (switch1 * 0.05) + (switch1Prev * 0.95);
if(switch1Smoothed>=a){
switch1Prev = switch1Smoothed;
}
else{
switch1Prev = a;
}
// *** окончание сглаживания ***
// Serial.print(switch1); // печать на последовательный терминал/плоттер
// Serial.print(" , ");
// Serial.println(переключатель 1 сглажен);
myservo1.writeMicroseconds(switch1Smoothed);
delay(15);
}
Когда я загружаю этот скетч и удерживаю виртуальную кнопку нажатой, то есть посылаю непрерывный поток строк "1", двигатель просто очень быстро перемещается вперед и назад, вместо того, чтобы медленно перемещаться в заданное максимальное положение и останавливаться там, пока кнопка удерживается.
@rayank97, 👍3
Обсуждение1 ответ
Может возникнуть проблема с вашей логикой сглаживания, которая довольно
запутана. Однако, если первый код работает нормально, вы можете сохранить его
почти как есть, и только замените digitalRead()
на версию, которая
считывает виртуальную кнопку. Похоже, это то, что captureChar()
попытки достичь. Однако у этой функции есть несколько проблем:
- он имеет цикл и задержку, что приводит к нарушению синхронизации исходного кода
- его интерфейс сильно отличается от интерфейса
digitalRead()
и , следовательно, не может быть использован в качестве замены.
Вот что я бы предложил в качестве замены digitalRead()
. Он имеет
ту же подпись, что и оригинальный digitalRead()
, и может быть использован в качестве дополнительной
замены:
int virtual_switch_reading = LOW;
int virtualDigitalRead(uint8_t /* pin number ignored */) {
int c = Serial.read();
if (c == '0')
virtual_switch_reading = LOW;
else if (c == '1')
virtual_switch_reading = HIGH;
return virtual_switch_reading;
}
Обратите внимание, что вызов Serial.available()
бесполезен: если данные
недоступны, Serial.read()
возвращает значение -1
, которое не равно ни '0'
, ни '1'
.
Обновление: устранение требования о наличии шести виртуальных коммутаторов.
Как указано в комментарии, я предлагаю вам закодировать состояния всех переключателей в пределах NodeRed в виде одного байта с одним битом на переключатель. Затем вы должны отправлять этот байт на Arduino либо периодически, либо всякий раз, когда какой-либо переключатель меняет состояние.
На Arduino, чтобы имитировать digitalRead()
на виртуальном
коммутаторе, вам сначала нужно сопоставить номера контактов с виртуальными коммутаторами:
// Сопоставьте контакт с виртуальным коммутатором.
const uint8_t virtual_switch_masks[NUM_DIGITAL_PINS] = {
0, // pin 0: не виртуальный
0, // pin 1: не виртуальный
1<<0, // вывод 2: виртуальный коммутатор 0
1<<1, // вывод 3: виртуальный коммутатор 1
// и т.д...
};
Обратите внимание, что в этой таблице не хранятся номера переключателей. Вместо этого он хранит
битовые маски. Например, виртуальный коммутатор 1 сохраняется как 1<<1
=
0b00000010
, т.е. Все биты чисты, за исключением бита с номером 1 (крайний правый
равен нулю), который установлен.
Затем вы можете имитировать digitalRead()
, извлекая соответствующий бит
из байта, который вы в последний раз получили от NodeRed, что выполняется
побитово и с соответствующей маской:
// Состояния всех виртуальных коммутаторов, по одному биту на коммутатор.
uint8_t virtual_switch_states = 0;
int virtualDigitalRead(uint8_t pin) {
uint8_t mask = virtual_switch_masks[pin];
// Если это не виртуальный коммутатор, выполните обычное digitalRead().
if (!mask)
return digitalRead(pin);
// Для виртуального коммутатора извлеките соответствующий бит
// из virtual_switch_states.
if (virtual_switch_states & mask)
return HIGH;
else
return LOW;
}
Чтобы обновить состояния виртуальных коммутаторов на
основе полученных данных, вам просто нужно добавить это где-нибудь внутри loop()
:
if (Serial.available())
virtual_switch_states = Serial.read();
Это сработало бы, если бы я использовал только один двигатель, не так ли? Я планирую использовать 6 двигателей, и я рассматривал возможность отправки строки вида "000000" с каждым символом, представляющим состояние 1 из 6 двигателей (в основном у меня было бы 6 виртуальных переключателей с узлом), @rayank97
Кроме того, что касается сглаживания, я согласен, что оно не очень утонченное, но это самый простой фрагмент кода, который я мог придумать, и я почувствовал, что этого будет достаточно для целей моего проекта. Тем не менее, я определенно открыт для предложений или идей по его улучшению и, что более важно, помощи в улучшении части виртуальных кнопок., @rayank97
@rayank97: Строка типа "000000" будет работать не очень хорошо, так как вы не можете знать, где заканчивается одна строка и начинается следующая. Вам нужно было бы добавить какой-то разделитель. Было бы проще отправить один байт, где шесть позиций переключателя кодируются как отдельные биты., @Edgar Bonet
@rayank97: Что касается сглаживания: вы можете упростить его, удалив switch1Prev
и вместо этого используя switch1Smoothed
. Кроме того, если вы установите switch1 = 1200 + 1200 * digitalRead (A1);
, то switch1Smoothed
всегда будет находиться между 1200 и 2400: нет необходимости явно его зажимать., @Edgar Bonet
Спасибо за идею для сглаживания. Что касается виртуальных кнопок, я только что понял, что даже если бы я отправил один байт, как вы предложили, не приведет ли проблема с наличием 2 отдельных циклов к нарушению синхронизации моего исходного кода? Или я ошибаюсь? Если это не так, не могли бы вы сказать мне, как вы думаете, есть ли другой способ, которым я мог бы это сделать?, @rayank97
@rayank97: Здесь нет отдельных циклов. `virtualDigitalRead()' будет равно O(1) даже при шести переключателях. См. Исправленный ответ., @Edgar Bonet
- Контроллер радиоуправляемой машины с использованием библиотеки RC-switch
- Попытка переместить серводвигатель с помощью 3 кнопок
- Arduino ждет, пока кнопка не будет отпущена для активации
- Почему мой серводвигатель всегда включен?
- Я пытаюсь управлять сервоприводом нажатием кнопки, но это не работает ПОМОГИТЕ
- Не понимаю проблемы с моим кодом
- Нажмите кнопку для подачи питания на сервопривод
- Как добавить задержку, закрыть сервопривод и снова перейти к началу?
первым делом вставьте разделительный слой между нажатием кнопки и активацией сервопривода... нажатие кнопки устанавливает флаг
buttonPressed
, а отпускание кнопки устанавливает флагbuttonReleased
... никаких других действий не выполняется ... часть программы , посвященная движению сервопривода , перемещает сервопривод только в ответ на два флага ... он не реагирует ни на что другое ... когда это работает правильно, добавьте код, который устанавливает флаги в ответ на данные, полученные от serial, @jsotolaЯ попробую это сделать и свяжусь с вами как можно скорее. Если быть до конца честным, я здесь не в своей тарелке и ценю любую обратную связь. Мне потребовалась неделя только на то, чтобы придумать то, что у меня есть сейчас., @rayank97