系列文章目錄
提示:
第一章 基于索尼PS4手柄的遙控器雙向控制
文章目錄
- 系列文章目錄
- 前言
- 一、PS手柄是什么?
- 二、一般如何用PS手柄?
- 1.案例一
- 2.常見的使用方法
- 三、!!!我的解決方案
- 1.首先要了解PS4與工控機的通信機制--藍牙
- 2.讓PS4連接上藍牙
- 總結
前言
隨著人工智能的不斷發展,做無人駕駛除錯肯定需要一個遙控器來除錯,對于價值比較大的無人駕駛車輛會有專業的遙控器控制,但是對于剛入門的小白可能沒有那么多機會接觸高端無人駕駛設備,這里另辟蹊徑用打游戲的手柄進行控制,提示:以下是本篇文章正文內容,下面案例可供參考
一、PS手柄是什么?
示例:PS系列手柄 是索尼提供的游戲機的手柄,該工具是為了解決游戲玩家設計的,
二、一般如何用PS手柄?
1.案例一
之前我用一個PS3做小車雙向控制,優點是通過修改硬體把藍牙換成了Lora,實作遠距離雙向互傳,我的應用場景之一:可以通過ROS發送相關反饋給手柄,比如震動幾下等等,這樣在機器人除錯程序中可以模擬車輛在ROS或者真實小車發生碰撞的情況下讓手柄震動給開發者人員,
同時,手柄會把按鍵的模擬量和鍵值資料發送給通過無線傳輸發送給ROS,進而控制小車,
但是也有美中不足的事情:
1.PS3實在太丑了,,相比PS4
2.PS4多了RGB燈,所以可以反向反饋燈光,PS3則只能反饋震動
3.PS4內部有陀螺儀,但是由于我買的國產的,內部IMU芯片沒有焊接,ε=(′ο`*)))唉
4.重要的事情再說一遍,PS4還是比PS3帥啊
2.常見的使用方法
常見使用藍牙與工控機這里我用的是NUC11,配準是11代i71185G7,16G記憶體,系統安裝的是Ubuntu16.04:
sudo apt-get install jstest-gtk
sudo jstest /dev/input/js0
然后在PS4手柄與工控機連接前后對比/dev/input/下面你會看到多出5個檔案,分別是一個js0、event*/event*+1、event*+2和一個mouse,這三個分別對應jstest通過JavaScript決議后的鍵值,和三個未決議的鍵值,好像有一個是IMU資料,因為一直在發送資料,但是我的PS4是國產的沒有IMU,,,另外一個是滑鼠資料,pyPS4Controller 庫,然后就是再安裝這個庫在Ubuntu上開發,
pip install pyPS4Controller
優點:不需要額外接收器,成本低直接可以在Linux系統上開發
缺點:1.不靈活 2.很難做到反向控制手柄的燈效和震動等等,很難做ROS與手柄的雙向互動,目前只能拿到手柄的鍵值資料單向控制小車車,
該處使用的url網路請求的資料,
三、!!!我的解決方案
1.首先要了解PS4與工控機的通信機制–藍牙
藍牙通訊具有兩種作業模式:命令回應作業模式和自動
連接作業模式,在自動連接作業模式下模塊又可分為主(Master)、從(Slave)和回環(Loopback)三種作業角色,當模塊處于自動連接作業模式時,將自動根據事先設定的方式連接的資料傳輸,
通過兩天的摸索:
1.SPP 規范庫(Initialisethesppprofilelib)在第一次配對前需要初始化(這個是藍牙規范,不需要看懂)
2.—查詢設備類為 0x1f1f的藍牙設備,也是藍牙規范,為此我拆解了PS4,通過一些設備和方法得到一些資訊,PS4的藍牙芯片是用的是RDA5850的芯片,通過查詢資料手冊看到這是一個多媒體藍牙芯片,感覺集成化程度很高啊,不管了我們首先要做的是自己做個藍牙接收器能夠連接上這個PS4,我給出兩種方案:
1.單片機+藍牙模塊(雙模藍牙才OK)
4.集成單片機的藍牙芯片,esp8266或者esp32


2.讓PS4連接上藍牙
在連接藍牙模塊之前我們先連接一次手機,看一下這個PS4的MAC地址是多少我通過一個HC藍牙助手的安卓app看到的

下面是PS4的反饋代碼,用VSCODE就可以開發PS4接受模組了
#include "PS4Controller.h"
#include <esp_bt_defs.h>
#include <esp_bt_main.h>
extern "C" {
#include "ps4.h"
}
#define ESP_BD_ADDR_HEX_PTR(addr) \
(uint8_t*)addr + 0, (uint8_t*)addr + 1, (uint8_t*)addr + 2, \
(uint8_t*)addr + 3, (uint8_t*)addr + 4, (uint8_t*)addr + 5
PS4Controller::PS4Controller() {}
bool PS4Controller::begin() {
ps4SetEventObjectCallback(this, &PS4Controller::_event_callback);
ps4SetConnectionObjectCallback(this, &PS4Controller::_connection_callback);
if (!btStarted() && !btStart()) {
log_e("btStart failed");
return false;
}
esp_bluedroid_status_t btState = esp_bluedroid_get_status();
if (btState == ESP_BLUEDROID_STATUS_UNINITIALIZED) {
if (esp_bluedroid_init()) {
log_e("esp_bluedroid_init failed");
return false;
}
}
if (btState != ESP_BLUEDROID_STATUS_ENABLED) {
if (esp_bluedroid_enable()) {
log_e("esp_bluedroid_enable failed");
return false;
}
}
ps4Init();
return true;
}
#define ESP_BD_ADDR_STR "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
bool PS4Controller::begin(const char* mac) {
esp_bd_addr_t addr;
if (sscanf(mac, ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX_PTR(addr)) != ESP_BD_ADDR_LEN) {
log_e("Could not convert %s\n to a MAC address", mac);
return false;
}
ps4SetBluetoothMacAddress(addr);
return begin();
}
void PS4Controller::end() {}
bool PS4Controller::isConnected() { return ps4IsConnected(); }
//下面這個函式是通過上位機發送指令控制PS4手柄的RGB燈顏色的
void PS4Controller::setLed(uint8_t r, uint8_t g, uint8_t b) {
output.r = r;
output.g = g;
output.b = b;
}
//下面這個函式是通過上位機發送指令控制PS4手柄的震動的基礎庫
void PS4Controller::setRumble(uint8_t small, uint8_t large) {
output.smallRumble = small;
output.largeRumble = large;
}
void PS4Controller::setFlashRate(uint8_t onTime, uint8_t offTime) {
output.flashOn = onTime / 10;
output.flashOff = offTime / 10;
}
void PS4Controller::sendToController() { ps4SetOutput(output); }
void PS4Controller::attach(callback_t callback) { _callback_event = callback; }
void PS4Controller::attachOnConnect(callback_t callback) {
_callback_connect = callback;
}
void PS4Controller::attachOnDisconnect(callback_t callback) {
_callback_disconnect = callback;
}
void PS4Controller::_event_callback(
void* object, ps4_t data, ps4_event_t event) {
PS4Controller* This = (PS4Controller*)object;
memcpy(&This->data, &data, sizeof(ps4_t));
memcpy(&This->event, &event, sizeof(ps4_event_t));
if (This->_callback_event) {
This->_callback_event();
}
}
void PS4Controller::_connection_callback(void* object, uint8_t isConnected) {
PS4Controller* This = (PS4Controller*)object;
if (isConnected) {
delay(250); // ToDo: figure out how to know when the channel is free again
// so this delay can be removed
if (This->_callback_connect) {
This->_callback_connect();
}
}
else {
if (This->_callback_disconnect) {
This->_callback_disconnect();
}
}
}
#if !defined(NO_GLOBAL_INSTANCES)
PS4Controller PS4;
#endif
然后在PS4手柄與工控機連接前后對比/dev/input/下面你會看到多出5個檔案,分別是一個js0、event*/event*+1、event*+2和一個mouse,這三個分別對應jstest通過JavaScript決議后的鍵值,和三個未決議的鍵值,好像有一個是IMU資料,因為一直在發送資料,但是我的PS4是國產的沒有IMU,,,另外一個是滑鼠資料,pyPS4Controller 庫,然后就是再安裝這個庫在Ubuntu上開發,
#include "ps4.h"
#include <esp_system.h>
#include <string.h>
#include "ps4_int.h"
/********************************************************************************/
/* PS4接受資料的常量部分 */
/********************************************************************************/
static const uint8_t hid_cmd_payload_ps4_enable[] = {0x43, 0x02};
/********************************************************************************/
/* 本地變數 */
/********************************************************************************/
static ps4_connection_callback_t ps4_connection_cb = NULL;
static ps4_connection_object_callback_t ps4_connection_object_cb = NULL;
static void* ps4_connection_object = NULL;
static ps4_event_callback_t ps4_event_cb = NULL;
static ps4_event_object_callback_t ps4_event_object_cb = NULL;
static void* ps4_event_object = NULL;
static bool is_active = false;
/********************************************************************************/
/* 下面這些是基礎功能 */
/********************************************************************************/
/*******************************************************************************
**
** Function ps4Init
**
** Description 初始化藍牙接受服務,然后連接手柄
**
**
** Returns void
**
*******************************************************************************/
void ps4Init() {
sppInit();
ps4_l2cap_init_services();
}
/*******************************************************************************
**
** Function ps4IsConnected
**
** Description 當兩個設備之間成功發生握手后回傳一個值
**
**
**
** Returns bool
**
*******************************************************************************/
bool ps4IsConnected() { return is_active; }
/*******************************************************************************
**
** Function ps4Enable
**
** Description PS4使能后持續發送資料
**
**
**
** Returns void
**
*******************************************************************************/
void ps4Enable() {
uint16_t length = sizeof(hid_cmd_payload_ps4_enable);
hid_cmd_t hidCommand;
hidCommand.code = hid_cmd_code_set_report | hid_cmd_code_type_feature;
hidCommand.identifier = hid_cmd_identifier_ps4_enable;
memcpy(hidCommand.data, hid_cmd_payload_ps4_enable, length);
ps4_l2cap_send_hid(&hidCommand, length);
ps4SetLed(32, 32, 200);
}
/*******************************************************************************
**
** Function ps4Cmd
**
** Description 發送給PS4讓其震動或者燈光的控制的命令
**
**
** Returns void
**
*******************************************************************************/
void ps4Cmd(ps4_cmd_t cmd) {
hid_cmd_t hidCommand = {.data = {0x80, 0x00, 0xFF}};
uint16_t length = sizeof(hidCommand.data);
hidCommand.code = hid_cmd_code_set_report | hid_cmd_code_type_output;
hidCommand.identifier = hid_cmd_identifier_ps4_control;
hidCommand.data[ps4_control_packet_index_small_rumble] = cmd.smallRumble; // Small Rumble
hidCommand.data[ps4_control_packet_index_large_rumble] = cmd.largeRumble; // Big rumble
hidCommand.data[ps4_control_packet_index_red] = cmd.r; // Red
hidCommand.data[ps4_control_packet_index_green] = cmd.g; // Green
hidCommand.data[ps4_control_packet_index_blue] = cmd.b; // Blue
// Time to flash bright (255 = 2.5 seconds)
hidCommand.data[ps4_control_packet_index_flash_on_time] = cmd.flashOn;
// Time to flash dark (255 = 2.5 seconds)
hidCommand.data[ps4_control_packet_index_flash_off_time] = cmd.flashOff;
ps4_l2cap_send_hid(&hidCommand, length);
}
/*******************************************************************************
**
** Function ps4SetLedOnly
**
** Description 設定燈光顏色RGB三原色控制的資料
**
**
** Returns void
**
*******************************************************************************/
void ps4SetLed(uint8_t r, uint8_t g, uint8_t b) {
ps4_cmd_t cmd = {0};
cmd.r = r;
cmd.g = g;
cmd.b = b;
ps4Cmd(cmd);
}
/*******************************************************************************
**
** Function ps4SetOutput
**
** Description 設定回呼
**
**
** Returns void
**
*******************************************************************************/
void ps4SetOutput(ps4_cmd_t prevCommand) { ps4Cmd(prevCommand); }
/*******************************************************************************
**
** Function ps4SetConnectionCallback
**
** Description 當PS4發送一次手柄鍵值給接識訓時候,進行一次中斷處理的回呼
**
**
**
** Returns void
**
*******************************************************************************/
void ps4SetConnectionCallback(ps4_connection_callback_t cb) {
ps4_connection_cb = cb;
}
/*******************************************************************************
**
** Function ps4SetConnectionObjectCallback
**
** Description 當PS4連接的時候進行連接通知回呼
**
**
**
** Returns void
**
*******************************************************************************/
void ps4SetConnectionObjectCallback(void* object, ps4_connection_object_callback_t cb) {
ps4_connection_object_cb = cb;
ps4_connection_object = object;
}
/*******************************************************************************
**
** Function ps4SetEventCallback
**
** Description 接收PS4的回呼事件
**
**
** Returns void
**
*******************************************************************************/
void ps4SetEventCallback(ps4_event_callback_t cb) { ps4_event_cb = cb; }
/*******************************************************************************
**
** Function ps4SetEventObjectCallback
**
** Description PS4接受控制事件
**
**
** Returns void
**
*******************************************************************************/
void ps4SetEventObjectCallback(void* object, ps4_event_object_callback_t cb) {
ps4_event_object_cb = cb;
ps4_event_object = object;
}
/*******************************************************************************
**
** Function ps4SetBluetoothMacAddress
**
** Description 設定接識訓需要連接的PS4手柄位移MAC地址
**
**
** Returns void
**
*******************************************************************************/
void ps4SetBluetoothMacAddress(const uint8_t* mac) {
// The bluetooth MAC address is derived from the base MAC address
// https://docs.espressif.com/projects/esp-idf/en/stable/api-reference/system/system.html#mac-address
uint8_t baseMac[6];
memcpy(baseMac, mac, 6);
baseMac[5] -= 2;
esp_base_mac_addr_set(baseMac);
}
/********************************************************************************/
/* 本地化的一些功能 */
/********************************************************************************/
void ps4ConnectEvent(uint8_t is_connected) {
if (is_connected) {
ps4Enable();
} else {
is_active = false;
}
}
void ps4PacketEvent(ps4_t ps4, ps4_event_t event) {
// Trigger packet event, but if this is the very first packet
// after connecting, trigger a connection event instead
if (is_active) {
if(ps4_event_cb != NULL) {
ps4_event_cb(ps4, event);
}
if (ps4_event_object_cb != NULL && ps4_event_object != NULL) {
ps4_event_object_cb(ps4_event_object, ps4, event);
}
} else {
is_active = true;
if(ps4_connection_cb != NULL) {
ps4_connection_cb(is_active);
}
if (ps4_connection_object_cb != NULL && ps4_connection_object != NULL) {
ps4_connection_object_cb(ps4_connection_object, is_active);
}
}
}
等我完成PS4的回環流程后再重新組織架構文章,
總結
提示:這里對文章進行總結:
目的:使用PS4連接小車,可以遙控控制一些開關或者其他,當小車在ROS里發生碰撞會反饋給手柄發生碰撞震動,ROS小車的電量或者一些狀態通過PS4的RGB燈光進行反饋,實時可以看得到,
完成度:50%,后面開發完成了再整理一下發出來,
祝大家元旦快樂,2022年元旦,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/401450.html
標籤:其他
上一篇:8266 mqtt 純c開發
