• PS4 + ESP32 制作无线遥控器


    一. 说明

    通过微控制器控制 PS4 手柄大概有两套方案,第一种是使用 PS4-esp32 库,通过 ESP32 芯片的蓝牙与 PS4 建立连接。另一种是使用 USB_Host_Shield_2.0 中的 PS4BT 库,通过蓝牙接收器和 PS4 手柄配对,再通过 USB 芯片完成主控和蓝牙接收器间的通信。

    我采用的是第一种方案,成功使用一块 ESP32 开发板实现对 PS4 的控制,但我非常不推荐这种方案,因为它还有很多问题没有解决,我把我尝试的整个流程记录下来也只是方便其他人继续研发,如果是基于成本考虑,或者是完全出于兴趣,可以尝试这种方案;如果只是想快速稳定地建立手柄和控制器间的连接,我还是推荐第二种成熟的方案。

    二. 准备工作

    1. PS4 手柄

    PS4 手柄尽量选择正版,盗版手柄有可能无法连接(事实上我没有遇到这个问题),在 Issue#7Issue#19Issue#38 中提到了这个问题,没有人给出解决方案,也没有确定可以使用 PS4-esp32 库的盗版手柄。

    判别手柄是否为正版可以参考这篇文章

    2. ESP32 开发板

    PS4 仅支持使用 BT (Bluetooth) 进行连接,故要选择 ESP32 芯片,C2, C3, S2, S3 系列芯片均不支持 BT (Bluetooth) ( 注意 BluetoothBluetooth LE 的区别 ),详见 芯片概览。我使用的是 NodeMCU-32S 开发板。

    3. 修改手柄 Mac 地址

    必须保证 ESP32 的 Mac 地址和手柄的 Mac 地址相同才可以连接,使用 SixaxisPairTool 工具读取并修改手柄的 Mac 地址,Mac 地址在保证单播的情况下可以任取,例如1a:2b:3c:01:01:01。记住此处设置的手柄的 Mac 地址,之后要填写到 ESP32 的程序中。

    注意:如果之前手柄已经和电脑配过对,此时手柄的 Mac 地址会被修改成电脑蓝牙的 Mac 地址,不要使用和电脑蓝牙相同的 Mac 地址!!

    三. 程序编写

    使用 VSCode + PlatformIO 基于 Arduino 框架进行开发。

    新建 PlatformIO 工程,根据所用板子型号配置 platformio.ini 文件。

    ; platformio.ini
    [env:nodemcu-32s]
    platform = espressif32
    board = nodemcu-32s
    framework = arduino
    monitor_speed = 115200
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    将整个 PS4-esp32 项目放到工程的 lib 文件夹下,文件树如下图所示:

    请添加图片描述
    编写 main.cpp

    /*main.cpp*/
    #include 
    #include 
    #include 
    
    int r = 255, g = 0, b = 0;
    
    // Calculates the next value in a rainbow sequence
    void nextRainbowColor()
    {    
        if (r > 0 && b == 0) 
        {
            r--;
            g++;
        }
        if (g > 0 && r == 0) 
        {
            g--;
            b++;
        }
        if (b > 0 && g == 0) 
        {
            r++;
            b--;
        }
    }
    
    void setup() 
    {
        Serial.begin(115200);
        PS4.begin("14:5A:FC:1B:02:CE"); // 替换此处的Mac地址!
        Serial.println("Ready.");
    }
    
    void loop() 
    {
        int cnt = 0;
        // Below has all accessible outputs from the controller
        if (PS4.isConnected()) 
        {
            if(++cnt == 100) // 1000ms
            {
                if (PS4.LStickX()) Serial.printf("Left Stick x at %d\n", PS4.LStickX());
                if (PS4.LStickY()) Serial.printf("Left Stick y at %d\n", PS4.LStickY());
                if (PS4.RStickX()) Serial.printf("Right Stick x at %d\n", PS4.RStickX());
                if (PS4.RStickY()) Serial.printf("Right Stick y at %d\n", PS4.RStickY());
                if (PS4.Charging()) Serial.println("The controller is charging");
                if (PS4.Audio()) Serial.println("The controller has headphones attached");
                if (PS4.Mic()) Serial.println("The controller has a mic attached");
                Serial.printf("Battery Level : %d\n", PS4.Battery());
                Serial.println();
                nextRainbowColor();
                cnt = 0;
            }
            
            if (PS4.Right()) Serial.println("Right Button");
            if (PS4.Down()) Serial.println("Down Button");
            if (PS4.Up()) Serial.println("Up Button");
            if (PS4.Left()) Serial.println("Left Button");
    
            if (PS4.Square()) Serial.println("Square Button");
            if (PS4.Cross()) Serial.println("Cross Button");
            if (PS4.Circle()) Serial.println("Circle Button");
            if (PS4.Triangle()) Serial.println("Triangle Button");
    
            if (PS4.UpRight()) Serial.println("Up Right");
            if (PS4.DownRight()) Serial.println("Down Right");
            if (PS4.UpLeft()) Serial.println("Up Left");
            if (PS4.DownLeft()) Serial.println("Down Left");
    
            if (PS4.L1()) Serial.println("L1 Button");
            if (PS4.R1()) Serial.println("R1 Button");
    
            if (PS4.Share()) Serial.println("Share Button");
            if (PS4.Options()) Serial.println("Options Button");
            if (PS4.L3()) Serial.println("L3 Button");
            if (PS4.R3()) Serial.println("R3 Button");
    
            if (PS4.PSButton()) Serial.println("PS Button");
            if (PS4.Touchpad()) Serial.println("Touch Pad Button");
    
            if (PS4.L2()) Serial.printf("L2 button at %d\n", PS4.L2Value());
            if (PS4.R2()) Serial.printf("R2 button at %d\n", PS4.R2Value());
    
            // Sets the color of the controller's front light
            // Params: Red, Green,and Blue
            // See here for details: https://www.w3schools.com/colors/colors_rgb.asp
            PS4.setLed(r, g, b);
    
            // Sets how fast the controller's front light flashes
            // Params: How long the light is on in ms, how long the light is off in ms
            // Range: 0->2550 ms, Set to 0, 0 for the light to remain on
            PS4.setFlashRate(PS4.LStickY() * 10, PS4.RStickY() * 10);
    
            // Sets the rumble of the controllers
            // Params: Weak rumble intensity, Strong rumble intensity
            // Range: 0->255
            PS4.setRumble(PS4.L2Value(), PS4.R2Value());
            // Sends data set in the above three instructions to the controller
            PS4.sendToController();
            // Don't send data to the controller immediately, will cause buffer overflow
            delay(10);
        }
        else
        {
            Serial.println("Connecting...");
            delay(1000);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109

    使用 PlatformIO 插件编译并上传代码。

    四. 其他

    • 如果不能连接,尝试擦除 ESP32 的闪存并重新烧录,可能需要 ESP32 每次上电前都擦除一次:

      pip install esptool
      esptool --chip esp32 erase_flash
      
      • 1
      • 2

      该问题在 Issue#2Issue#4Issue#9Issue#18 中被提到过。

    • rickwinter2 提到使用 esp-wroom-32 dev module v3 连接会更稳定,不需要频繁地擦除闪存,我没有进行尝试。

    • 之前出现过的很多问题都是蓝牙配对/连接的方式导致的,比如盗版手柄可以很容易的和 PS 主机或者电脑进行连接,但却无法和 ESP32 进行连接,本质上其实是 PS4-esp32 库没办法模拟与 PS4 手柄配对的过程。

      关于 PS4-esp32 库与 PS4 手柄的配对问题,作者在 Issue#11 中进行了解释,我没有完全理解,大意是由于 ESP32 硬件的限制,ESP32 不像无线蓝牙接收器那样支持很多配对协议。

    • PS4 配对过程和 ESP32 芯片的蓝牙功能进行进一步研究,解决好配对问题,PS4-esp32 库 + ESP32 的方案一定会成为主流,毕竟 USB_Host_Shield_2.0 方案需要昂贵的蓝牙接收器和 USB 芯片,接收器和板子的价格甚至与一个盗版手柄的价格相当。因为我只是想快速制作一个机器人遥控器,并没有兴趣与时间去深入研究蓝牙,故转向了 USB_Host_Shield_2.0 方案。

  • 相关阅读:
    煤焦油称重管理软件你了解吗
    2023-09-21 LeetCode每日一题(收集树中金币)
    C语言:一级指针访问二维数组
    从另一个角度谈谈Redis的常用数据结构
    欧洲fba海运详解:欧洲fba海运怎么样?有哪些优势?
    明明的随机数
    java IO流【常用流对象一】
    SIEMENS S7-1200 汽车转弯灯程序 编程与分析
    flutter 一键打出不同包名、应用名、版本名、签名、应用图标、版本号的安装包
    C++语言的广泛应用领域
  • 原文地址:https://blog.csdn.net/weixin_45467056/article/details/126551443