如何使用 Processing Game Control Plus 使用 PS4 控制器控制多个伺服系统
How to control multiple servos with a PS4 Controller using Processing Game Control Plus
过去 3 天我试图用我的基本编程知识解决这个问题,这就是我取得的成就:使用处理程序从 PS4 控制器收集数据并通过串行端口将其发送到 Arduino能够使用串口和 Game 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);
}
您尝试做的事情有点令人困惑,但据我所知,这种混淆与串行通信有关。
以下是我对通信如何与您当前的代码一起工作的理解以及一些问题:
1.截断
您正在写 px
,它映射到草图尺寸(我在任何地方都看不到,所以猜测它是默认的 100x100 吗?)。
如果尺寸大于 255,这可能是个问题。
甚至通过 Processing Serial write(int) takes an int, behind the scenes it's using jssc.Serial.writeInt(),它的值从 0 到 255。
(本质上它写入一个字节,但在 0-255 范围内,其中 byte
type in Processing java 是从 -127 到 128)。
如果要发送大于 255 的值,则需要将它们拆分为单独的字节。 (请参阅使用 bit shifting and AND masking and using OR 将各个字节组合成一个多字节整数的 ARGB 值拆分示例)
2。重复
px
在for循环中再次发送:myPort.write(lista[i]);
代码无论如何都被注释了,但这是需要注意的事情。
我个人制作了新的简单测试草图来解决单个简单问题,而不是大块未使用的块,这会使代码更难 read/navigate 并最终调试。
我鼓励你把更大的问题分解成多个更简单的单一问题,一次解决一个。
3。数据终止
注释代码尝试发送 4 个整数,但 Arduino 无法知道数据到达的顺序。如果出现任何问题,将很难判断顺序是什么。
有多种方法可以做到这一点。理想情况下,您会编写一个通信协议。
(关于这个话题有很多 resources。)
学习如何使用 bits/bytes/byte 数组并从头开始将通信协议与某种形式的数据验证(校验和/CRC 等)放在一起听起来是一个有趣的项目
如果你的时间很紧,你只是想驱动伺服而不用担心通过串行可靠地发送多个(潜在的)大值,我建议尝试 Firmata Processing
您需要将 Firmata 固件闪存到 Arduino,然后使用 Firmata Processing 库连接到串行端口(使用正确的波特率)并根据需要调用 arduino.servoWrite()
。另请参阅图书馆的 arduino_servo
示例。
根据您对 0 -> 100 之间的映射值的评论,您可以使用任何其他 > 100 的字符重新调整 SerialEvent example 的用途,以将数据与字符串终止符 char 区分开来。请记住,您将失去精度:
getValue()
returns 具有 32 位精度的 float
- 理论上您可以对 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:
/*
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;
}
}
}
过去 3 天我试图用我的基本编程知识解决这个问题,这就是我取得的成就:使用处理程序从 PS4 控制器收集数据并通过串行端口将其发送到 Arduino能够使用串口和 Game 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);
}
您尝试做的事情有点令人困惑,但据我所知,这种混淆与串行通信有关。
以下是我对通信如何与您当前的代码一起工作的理解以及一些问题:
1.截断
您正在写 px
,它映射到草图尺寸(我在任何地方都看不到,所以猜测它是默认的 100x100 吗?)。
如果尺寸大于 255,这可能是个问题。
甚至通过 Processing Serial write(int) takes an int, behind the scenes it's using jssc.Serial.writeInt(),它的值从 0 到 255。
(本质上它写入一个字节,但在 0-255 范围内,其中 byte
type in Processing java 是从 -127 到 128)。
如果要发送大于 255 的值,则需要将它们拆分为单独的字节。 (请参阅使用 bit shifting and AND masking and using OR 将各个字节组合成一个多字节整数的 ARGB 值拆分示例)
2。重复
px
在for循环中再次发送:myPort.write(lista[i]);
代码无论如何都被注释了,但这是需要注意的事情。
我个人制作了新的简单测试草图来解决单个简单问题,而不是大块未使用的块,这会使代码更难 read/navigate 并最终调试。
我鼓励你把更大的问题分解成多个更简单的单一问题,一次解决一个。
3。数据终止
注释代码尝试发送 4 个整数,但 Arduino 无法知道数据到达的顺序。如果出现任何问题,将很难判断顺序是什么。
有多种方法可以做到这一点。理想情况下,您会编写一个通信协议。 (关于这个话题有很多 resources。)
学习如何使用 bits/bytes/byte 数组并从头开始将通信协议与某种形式的数据验证(校验和/CRC 等)放在一起听起来是一个有趣的项目
如果你的时间很紧,你只是想驱动伺服而不用担心通过串行可靠地发送多个(潜在的)大值,我建议尝试 Firmata Processing
您需要将 Firmata 固件闪存到 Arduino,然后使用 Firmata Processing 库连接到串行端口(使用正确的波特率)并根据需要调用 arduino.servoWrite()
。另请参阅图书馆的 arduino_servo
示例。
根据您对 0 -> 100 之间的映射值的评论,您可以使用任何其他 > 100 的字符重新调整 SerialEvent example 的用途,以将数据与字符串终止符 char 区分开来。请记住,您将失去精度:
getValue()
returns 具有 32 位精度的float
- 理论上您可以对 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:
/*
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;
}
}
}