ESP32 use PS4 controller
from https://techtutorialsx.com/2020/03/15/esp32-ps4-controller-button-events/
ESP32: PS4 controller button events
In this tutorial we will learn how to process button pressed events on the PS4 controller, when connected to the ESP32. The tests from this tutorial were done using a DFRobot’s ESP32 module integrated in a ESP32 development board.
Introduction
In this tutorial we will learn how to process button pressed events on the PS4 controller, when connected to the ESP32. We will be using the Arduino core and this library.
https://github.com/aed3/PS4-esp32
As mentioned, we will work with button pressed events, which means that we need to provide an event handling function to process them. This approach is more efficient than polling the controller to check if a button is currently being pressed.
For an introductory tutorial on how to get started using the PS4 controller connected to the ESP32, please check here. This tutorial covers how to obtain the Bluetooth address stored on the controller, which is needed for the library initialization and to allow the connection of the controller to the ESP32 microcontroller.
The tests from this tutorial were done using a DFRobot’s ESP32 module integrated in a ESP32 development board.
The code
We will start by including the PS4Controller.h library, so we have access to the PS4 extern variable. This variable will allow us to interact with the controller.
1 | #include <PS4Controller.h> |
Moving on to the Arduino setup function, we will start by opening a serial connection. That way, we will be able to output the results of our program.
1 | Serial.begin(115200); |
After that we will specify the handling function that will be executed when an event occurs. We will call this function onEvent and check how to implement it later.
To specify this event handling function we simply need to call the attach method on the PS4 extern variable, passing as input our handling function.
1 | PS4.attach(onEvent); |
Then we are going to call the begin method on the PS4 extern variable to perform the initialization of the Bluetooth layer and make the ESP32 ready to listen to incoming PS4 controller connections.
As input we need to pass, as a string, the Bluetooth address stored in the PS4 controller. You can learn how to figure out this address by following this previous tutorial.
Note that this method returns as output a Boolean value indicating if the procedure was successful or not. For simplicity we are not doing an error check but please take in consideration that you should do it in a real application scenario to make sure everything went well.
1 | PS4.begin( "yourDeviceMAC" ); |
The complete setup function can be seen below.
1 2 3 4 5 6 7 8 9 | void setup() { Serial.begin(115200); PS4.attach(onEvent); PS4.begin( "yourDeviceMAC" ); Serial.println( "Initialization finished." ); } |
For this tutorial we are not going to do a check if the controller is already connected or not, but if you want to do it you can check this guide. Naturally, only after a controller is connected the button pressed events will start being generated.
Since we are working with events, we won’t need to have any code in the Arduino main loop. So, we are going to delete the corresponding FreeRTOS task with a call to the vTaskDelete function, passing as input the value NULL.
1 2 3 4 | void loop() { vTaskDelete(NULL); } |
To finalize, we will take a look at the implementation of the onEvent function. This function needs to follow a predefined signature: it must return void and receive no arguments.
1 2 3 | void onEvent(){ // function implementation } |
We will see an example for the cross button, to detect if it was pressed and if it stops being pressed.
Note that these two events are generated when the button goes from unpressed to pressed and also from pressed to unpressed (transitions).
While the button is being pressed (without transition, just holding the state) these two events are not generated.
So, in the event handling function, we will first check if the button is currently being pressed (not the transitory, just if it is pressed at the moment regardless of its previous status).
To do it, we first access the data attribute of the PS4 extern variable. This attribute is a struct of type ps4_t. Then we will access the button field of this struct, which is also a struct, of type ps4_button_t.
Finally, this struct contains an element per each of the supported buttons. The names of the attributes can be seen below:
- options
- l3
- r3
- share
- up
- right
- down
- left
- upright
- upleft
- downright
- downleft
- l2
- r2
- l1
- r1
- triangle
- circle
- cross
- square
- ps
- touchpad
So, we can see if the cross button is being pressed with the following code:
1 2 3 | if (PS4.data.button.cross){ Serial.println( "cross is being pressed" ); } |
Note that the callback function will be called while the button is being pressed, even if the button state doesn’t change.
Now, to check if the button had a change of status (transitory from not pressed to pressed or vice-versa) we can follow a similar approach. In this case, we start by accessing the event attribute of the PS4 extern variable. This attribute is a struct of type ps4_event_t.
Then we can either access the button_down or button_up fields if we want to check if the button transitioned from not pressed to pressed or from pressed to not pressed, respectively.
Both fields are structs of type ps4_button_t, which has a field per each button supported (the list was already shown before).
So, we can check if the button was pressed / unpressed with the following conditional statements:
1 2 3 4 5 6 7 | if (PS4.event.button_up.cross){ Serial.println( "cross up" ); } if (PS4.event.button_down.cross){ Serial.println( "cross down" ); } |
The full callback function code can be seen below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | void onEvent(){ if (PS4.data.button.cross){ Serial.println( "cross is being pressed" ); } if (PS4.event.button_up.cross){ Serial.println( "cross up" ); } if (PS4.event.button_down.cross){ Serial.println( "cross down" ); } } |
The final code can be seen below.
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 | #include <PS4Controller.h> void onEvent(){ // Check if the buttons is being presses if (PS4.data.button.cross){ Serial.println( "cross is being pressed" ); } // Check if the button went from pressed to not pressed if (PS4.event.button_up.cross){ Serial.println( "cross up" ); } // Check if the button went from unpressed to pressed if (PS4.event.button_down.cross){ Serial.println( "cross down" ); } } void setup() { Serial.begin(115200); PS4.attach(onEvent); PS4.begin( "yourDeviceMAC" ); Serial.println( "Initialization finished." ); } void loop() { vTaskDelete(NULL); } |
Testing the code
To test the code, simply compile it and upload it to you device, using the Arduino IDE. Once the procedure is finished, open the IDE serial monitor.
You should get a message indicating the initialization is finished. From this point onward, the ESP32 is ready to receive a controller connection.
After this, you should connect the PS4 controller by clicking the PS button. After that you can press the cross button and then stop pressing it. You should get an output similar to figure 1.
Code to cover all the buttons
To finalize this tutorial, we will test a code snippet that covers all the buttons supported. For simplicity, we will just check the changes from unpressed to pressed and vice-versa.
You can check below the code with the callback function that handles all the buttons.
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 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | #include <PS4Controller.h> void onEvent(){ if (PS4.event.button_up.right){ Serial.println( "right arrow up" ); } if (PS4.event.button_down.right){ Serial.println( "right arrow down" ); } if (PS4.event.button_up.left){ Serial.println( "left arrow up" ); } if (PS4.event.button_down.left){ Serial.println( "left arrow down" ); } if (PS4.event.button_up.down){ Serial.println( "down arrow up" ); } if (PS4.event.button_down.down){ Serial.println( "down arrow down" ); } if (PS4.event.button_up.up){ Serial.println( "up arrow up" ); } if (PS4.event.button_down.up){ Serial.println( "up arrow down" ); } if (PS4.event.button_up.r3){ Serial.println( "r3 stick up" ); } if (PS4.event.button_down.r3){ Serial.println( "r3 stick down" ); } if (PS4.event.button_up.l3){ Serial.println( "l3 stick up" ); } if (PS4.event.button_down.l3){ Serial.println( "l3 stick down" ); } if (PS4.event.button_up.l2){ Serial.println( "l2 button up" ); } if (PS4.event.button_down.l2){ Serial.println( "l2 buton down" ); } if (PS4.event.button_up.l1){ Serial.println( "l1 button up" ); } if (PS4.event.button_down.l1){ Serial.println( "l1 button down" ); } if (PS4.event.button_up.r2){ Serial.println( "r2 button up" ); } if (PS4.event.button_down.r2){ Serial.println( "r2 buton down" ); } if (PS4.event.button_up.r1){ Serial.println( "r1 button up" ); } if (PS4.event.button_down.r1){ Serial.println( "r1 button down" ); } if (PS4.event.button_up.touchpad){ Serial.println( "touchpad button up" ); } if (PS4.event.button_down.touchpad){ Serial.println( "touchpad button down" ); } if (PS4.event.button_up.options){ Serial.println( "options button up" ); } if (PS4.event.button_down.options){ Serial.println( "options button down" ); } if (PS4.event.button_up.share){ Serial.println( "share button up" ); } if (PS4.event.button_down.share){ Serial.println( "share button down" ); } if (PS4.event.button_up.ps){ Serial.println( "ps button up" ); } if (PS4.event.button_down.ps){ Serial.println( "ps button down" ); } if (PS4.event.button_up.triangle){ Serial.println( "triangle button up" ); } if (PS4.event.button_down.triangle){ Serial.println( "triangle button down" ); } if (PS4.event.button_up.circle){ Serial.println( "circle button up" ); } if (PS4.event.button_down.circle){ Serial.println( "circle button down" ); } if (PS4.event.button_up.square){ Serial.println( "square button up" ); } if (PS4.event.button_down.square){ Serial.println( "square button down" ); } if (PS4.event.button_up.cross){ Serial.println( "cross button up" ); } if (PS4.event.button_down.cross){ Serial.println( "cross button down" ); } } void setup() { Serial.begin(115200); PS4.attach(onEvent); PS4.begin( "01:01:01:01:01:01" ); Serial.println( "Initialization finished." ); } void loop() { vTaskDelete(NULL); } |
Upon uploading and running the code and connecting a controller to the ESP32, you can start experimenting with all the buttons. You should get a result similar to figure 2, which exemplifies some of the buttons being pressed.
留言
張貼留言