1

我过去 3 天试图用我的基本编程知识来解决这个问题,这就是我所取得的成就:使用处理程序从 PS4 控制器收集数据并通过串行端口将其发送到 Arduino 能够使用串行和游戏控制一个伺服Control Plus 库。

好吧,那我不应该有更多的问题吗?正确的?不,我不知道我将如何通过 PS4 控制的其他 3 个模拟轴并让 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);
}
4

1 回答 1

0

您尝试做的事情有点令人困惑,但据我所知,困惑在于串行通信。

以下是我如何理解通信如何与您当前的代码一起工作以及一些问题:

1. 截断

您正在编写px映射到草图尺寸的内容(我在任何地方都看不到,所以猜测它是默认的 100x100 吗?)。

如果尺寸大于 255,这可能是个问题。

即使通过处理串行write(int)需要一个 int,它在幕后使用jssc.Serial.writeInt()取值从 0 到 255。(本质上它写入一个字节,但在 0-255 范围内,其中byteProcessing java中的类型是从 -127 到 128)。

如果要发送大于 255 的值,则需要将它们拆分为单独的字节。(请参阅 ARGB 值拆分示例,使用位移位和 AND 掩码以及使用OR将各个字节组合成一个多字节整数)

2. 重复

px在 for 循环中再次发送:myPort.write(lista[i]); 代码无论如何都被注释了,但要注意这一点。就我个人而言,我制作了新的简单测试草图来解决单个简单问题,而不是大型未使用的块,这使得代码更难阅读/导航并最终调试。我鼓励你将更大的问题分解为多个更简单的单个问题,并一次解决一个。

3. 数据终止

注释代码尝试发送 4 个整数,但 Arduino 无法知道数据到达的顺序。如果出现任何问题,将很难说出顺序是什么。

有多种方法可以做到这一点。理想情况下,您会编写一个通信协议。(关于这个主题有很多资源。)

学习使用位/字节/字节数组并从头开始将通信协议与某种形式的数据验证(校验和 / CRC / 等)组合在一起,这听起来像是一个有趣的项目

如果您时间紧迫,并且您只想驱动伺服器而不必担心通过串行可靠地发送多个(可能)大值,我建议您尝试Firmata 处理

您需要将 Firmata 固件刷新到您的 Arduino,然后使用 Firmata 处理库连接到串行端口(使用正确的波特率)并arduino.servoWrite()根据需要调用。另请参阅库的arduino_servo示例。

根据您对 0 -> 100 之间的映射值的评论,您可以使用大于 100 的任何其他字符重新调整SerialEvent 示例的用途,以区分数据与字符串终止符字符。请记住,您将失去精度:

  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);
  }
}

阿杜诺:

/*
  Serial Event example

  When new serial data arrives, this sketch adds it to a String.
  When a newline is received, the loop prints the string and clears it.

  A good test for this is to try it with a GPS receiver that sends out
  NMEA 0183 sentences.

  NOTE: The serialEvent() feature is not available on the Leonardo, Micro, or
  other ATmega32U4 based boards.

  created 9 May 2011
  by Tom Igoe

  This example code is in the public domain.

  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;
    }
  }
}
于 2020-03-20T23:04:23.253 回答