Имитация нажатия кнопки с помощью последовательного монитора для запуска сервопривода

Я новичок в 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:

Serial Port Config on 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", двигатель просто очень быстро перемещается вперед и назад, вместо того, чтобы медленно перемещаться в заданное максимальное положение и останавливаться там, пока кнопка удерживается.

, 👍3

Обсуждение

первым делом вставьте разделительный слой между нажатием кнопки и активацией сервопривода... нажатие кнопки устанавливает флаг buttonPressed, а отпускание кнопки устанавливает флаг buttonReleased... никаких других действий не выполняется ... часть программы , посвященная движению сервопривода , перемещает сервопривод только в ответ на два флага ... он не реагирует ни на что другое ... когда это работает правильно, добавьте код, который устанавливает флаги в ответ на данные, полученные от serial, @jsotola

Я попробую это сделать и свяжусь с вами как можно скорее. Если быть до конца честным, я здесь не в своей тарелке и ценю любую обратную связь. Мне потребовалась неделя только на то, чтобы придумать то, что у меня есть сейчас., @rayank97


1 ответ


3

Может возникнуть проблема с вашей логикой сглаживания, которая довольно запутана. Однако, если первый код работает нормально, вы можете сохранить его почти как есть, и только замените 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