Как управлять несколькими сервоприводами с помощью контроллера PS4 с помощью Processing Game Control Plus - PullRequest
1 голос
/ 20 марта 2020

Последние 3 дня я провел, пытаясь выяснить это с моими базовыми c знаниями в области программирования, и вот чего я достиг: собрать данные с контроллера PS4 с помощью программы обработки и отправить их в Arduino через последовательный порт, чтобы иметь возможность контролировать один сервопривод с использованием последовательного интерфейса и библиотек Game Control Plus.

Хорошо, тогда у меня не должно быть больше проблем? Правильно? Нет. Я не имею ни малейшего представления о том, как я собираюсь передать другие 3 аналогичные оси управления PS4 и заставить Arduino получить их и разделить на переменные для управления другими сервоприводами.

Я собираюсь поделиться своими 2 кодами:

//Processing Code

import processing.serial.*;

import org.gamecontrolplus.gui.*;
import org.gamecontrolplus.*;
import net.java.games.input.*;

Serial myPort;

ControlIO control;
ControlDevice stick;
float px, py, pz, pw;


int[] lista;


public void setup() {
  lista = new int[4];
  String portName = Serial.list()[2]; 
  myPort = new Serial(this, portName, 9600);
  surface.setTitle("GCP Joystick example");

  control = ControlIO.getInstance(this);


  stick = control.filter(GCP.STICK).getMatchedDevice("joystick");
  if (stick == null) {
    println("No suitable device configured");
    System.exit(-1); 
  }

}


public void getUserInput() {
  px = map(stick.getSlider("X").getValue(), -1, 1, 0, width);
  py = map(stick.getSlider("Y").getValue(), -1, 1, 0, height);
  pz = map(stick.getSlider("Z").getValue(), -1, 1, 0, width);
  pw = map(stick.getSlider("W").getValue(), -1, 1, 0, height);
}

// Event handler for the SHADOW button


public void draw() {
  getUserInput(); // Polling the input device
  background(255, 255, 240);

  fill(0, 0, 255, 32);
  noStroke();
  println(int(px)); 
  myPort.write(int(px));

// I tried to pass this way, but I still don't know how make Arduino recognize and split them
/**  lista[0] = int(px); 
  lista[1] = int(py);
  lista[2] = int(pz);
  lista[3] = int(pw);

for (int i = 0; i < 4) {
  println(lista[i]); 
  myPort.write(lista[i]);
  if (myPort.readStringUntil('\n') == "k"){ 
    i += 1
}
  else{}
println("---------"); 
  */
}

Теперь код Arduino:

#include <Servo.h>

char val;
Servo servo1;
int x_pos;
int servo1_pin = 9;
int initial_position = 90;
int x_pos_pas = 50;

void setup() {
  Serial.begin(9600);
  servo1.attach(servo1_pin); 
  servo1.write (initial_position);
}

void loop() {
  if (Serial.available()) 
   { 
     val = Serial.read();
     x_pos = int(int(val) * 1.8);
   }
  if(x_pos != x_pos_pas){
    servo1.write(x_pos);    
  }
  else{}
  x_pos_pas = x_pos;
  delay(10);
}

1 Ответ

0 голосов
/ 21 марта 2020

Это немного сбивает с толку то, что вы пытаетесь сделать, но из того, что я могу понять, путаница связана с последовательной связью.

вот как я понимаю, как связь работает с вашим текущим кодом и несколько проблем с этим:

1. Усечение

Вы пишете px, который сопоставлен с размерами эскиза (который я нигде не вижу, поэтому думаю, что это значение по умолчанию 100x100?).

Если размеры больше 255, это может быть проблемой.

Даже при обработке Serial write (int) принимает int, за кулисами он использует jss c .Serial.writeInt () , который принимает значение из От 0 до 255. (По сути, он записывает один байт, но в диапазоне 0-255, где в качестве типа byte в обработке java от -127 до 128).

Если вы хотите отправить значения больше 255, вам нужно разделить их на отдельные байты. (см. пример разделения значения ARGB с использованием сдвига битов и маскировки И и использованием ИЛИ , чтобы объединить отдельные байты в многобайтовое целое число)

2 , Повторение

px отправляется снова в течение для l oop: myPort.write(lista[i]); Код все равно комментируется, но об этом следует помнить. Лично я делаю новые простые тестовые зарисовки, решающие отдельные простые задачи вместо больших неиспользуемых блоков, которые затрудняют чтение / навигацию и в конечном итоге отладку кода. Я призываю вас разбить большую проблему на несколько более простых одиночных задач и решать по одной за раз.

3. Завершение данных

Закомментированный код пытается отправить 4 целых числа, но Arduino не может узнать, в каком порядке поступают данные. Если что-то пойдет не так, очень сложно определить порядок.

Существует несколько способов сделать это. В идеале вы должны написать протокол связи. (и есть много ресурсов по этой теме c.)

Звучит как забавный проект, чтобы узнать о работе с битами / байтами / байтовыми массивами и составлении протокола связи из позаботьтесь о какой-либо форме проверки данных (контрольная сумма / CR C / et c.)

Если у вас мало времени и вы просто хотите управлять сервоприводом, не слишком заботясь о надежной отправке нескольких (потенциально) большие значения по сравнению с последовательным интерфейсом. Я рекомендую попробовать Firmata Processing

. Вам понадобится прошить sh прошивку Firmata на Arduino, а затем использовать библиотеку Firmata Processing для подключения к последовательному порту. (с правильной скоростью передачи) и, если требуется, наберите arduino.servoWrite(). См. Также пример библиотеки arduino_servo.

Исходя из вашего комментария к отображенным значениям в диапазоне от 0 до> 100, вы можете использовать перенаправление примера SerialEvent , используя любой другой символ> 100, чтобы дифференцировать данные из строкового терминатора char. Не забывайте, что вы потеряете точность:

  1. getValue() возвращает float с 32-битной точностью
  2. Теоретически вы можете использовать ту же технику с 0-254 (используя 255 в качестве символа-терминатора). Диапазон 0-100 использует 6 битов

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

Обработка:

import processing.serial.*;

import org.gamecontrolplus.gui.*;
import org.gamecontrolplus.*;
import net.java.games.input.*;

Serial myPort;

ControlIO control;
ControlDevice stick;
float px, py, pz, pw;

// px,py,pz,pw remapped to 0-100 range
int[] lista = new int[4];
// px,py,pz as bytes + terminator character (255)
byte[] toArduino = {0,0,0,0,(byte)255};

public void setup() {
  size(100,100);
  lista = new int[4];
  String portName = Serial.list()[2]; 
  try{
    myPort = new Serial(this, portName, 9600);
  }catch(Exception e){
    println("error connecting to serial port: " + portName);
    e.printStackTrace();
  }
  surface.setTitle("GCP Joystick example");

  control = ControlIO.getInstance(this);

  try{
    stick = control.filter(GCP.STICK).getMatchedDevice("joystick");
  }catch(Exception e){
    e.printStackTrace();
  }
  if (stick == null) {
    println("No suitable device configured");
  }

}


public void getUserInput() {
  if(stick == null){
    return;
  }
  // map values
  px = map(stick.getSlider("X").getValue(), -1, 1, 0, width);
  py = map(stick.getSlider("Y").getValue(), -1, 1, 0, height);
  pz = map(stick.getSlider("Z").getValue(), -1, 1, 0, width);
  pw = map(stick.getSlider("W").getValue(), -1, 1, 0, height);
  // cast to int
  lista[0] = (int)px;
  lista[1] = (int)py;
  lista[2] = (int)pz;
  lista[3] = (int)pw;
  // update bytearray
  toArduino[0] = (byte)lista[0];
  toArduino[1] = (byte)lista[1];
  toArduino[2] = (byte)lista[2];
  toArduino[3] = (byte)lista[3];
}

// Event handler for the SHADOW button


public void draw() {
  getUserInput(); // Polling the input device
  background(255, 255, 240);

  fill(0, 0, 255, 32);
  noStroke();

  text("px: " + px
    +"\npy: " + py
    +"\npz: " + pz
    +"\npw: " + pw
    ,5,15);

  if(myPort != null){
    myPort.write(toArduino);
  }
}

Arduino:

/ * Пример последовательного события

Когда поступают новые последовательные данные, этот эскиз добавляет их в строку. При получении новой строки l oop печатает строку и очищает ее.

Хороший тест для этого - попробовать его с приемником GPS, который отправляет предложения NMEA 0183.

ПРИМЕЧАНИЕ. Функция serialEvent () недоступна на платах на базе Leonardo, Micro или других платах на базе ATmega32U4.

, созданный 9 мая 2011 года Томом Айго

Этот пример кода находится в публикации c домен.

http://www.arduino.cc/en/Tutorial/SerialEvent * /

String inputString = "";         // a String to hold incoming data
bool stringComplete = false;  // whether the string is complete

const char TERMINATOR = (char)255;

void setup() {
  // initialize serial:
  Serial.begin(9600);
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);
}

void loop() {
  // in case serialEvent() isn't called automatically
  serialEvent();
  // print the string when a newline arrives:
  if (stringComplete) {
    // process string
    uint8_t px = inputString.charAt(0);
    uint8_t py = inputString.charAt(1);
    uint8_t pz = inputString.charAt(2);
    uint8_t pw = inputString.charAt(3);
    // TODO: pass this to servos
    // debug print
    Serial.print("px:");
    Serial.print(px);
    Serial.print("\tpy:");
    Serial.print(py);
    Serial.print("\tpz:");
    Serial.print(pz);
    Serial.print("\tpw:");
    Serial.println(pw);
    // clear the string:
    inputString = "";
    stringComplete = false;
  }
}

/*
  SerialEvent occurs whenever a new data comes in the hardware serial RX. This
  routine is run between each time loop() runs, so using delay inside loop can
  delay response. Multiple bytes of data may be available.
*/
void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag so the main loop can
    // do something about it:
    if (inChar == TERMINATOR) {
      stringComplete = true;
    }
  }
}
...