I am using the bluepad32 controller host from Ricardo Quesada and referencing the work of Rachel De Barros to drive DC motors and such with a generic PS4 controller over bluetooth on and ESP32 (also generic. seems to work with DOIT ESP32 DEV KIT selected.
I have no experience in programming but following web-based instruction I am able to use Left joystick to apply speed/direction as desired.
I have spent an inordinate amount of time trying to us Button A on the controller (bottom of the group on the right) to Toggle a hobby pump motor on and off. I can get the momentary method to work but it needs to toggle.
With the code below it toggles but if the button is held down it will cycle on/off at the rate of the delay (300) set at the bottom.
There are sooo many articles out there but all require an input button as and argument. I am just starting a briefly viewed a posting referred on this site that used "char incomingByte = Serial.read();" but I have no Idea how to proceed with that.
Is is possible, practical to use a more general method? I am not wrapping my brain with how to implement the toggle and debouncing instructions to the code below.
Please se my attempt starting at line 117. Apologies in advance for the mess.
```cpp
#include <Bluepad32.h>
#include <ESP32Servo.h>
//Assign Pin connections
const int builtInLed = 2;
int ENApin = 14; // motor 1 speed
int IN1pin = 27; // motor 1 dir1
int IN2pin = 26; // motor 1 dir2
int IN3pin = 25; // motor 2 dir1
int IN4pin = 33; // motor 2 dir2
int ENBpin = 21; // motor 2 speed
int xServoPin = 12;
int yServoPin = 13;
int IN1pump = 23;
int IN2pump = 22;
//assign Unique servo names
Servo xServo;
Servo yServo;
// Setting PWM properties
const int freq = 1000;
const int pwmChannel = 0;
const int resolution = 8;
//Toggle pin
int TogglePin = 19;
int ToggleState = LOW;
int LastToggleState = LOW;
ControllerPtr myControllers[BP32_MAX_GAMEPADS];
// This callback gets called any time a new gamepad is connected.
// Up to 4 gamepads can be connected at the same time.
void onConnectedController(ControllerPtr ctl) {
bool foundEmptySlot = false;
for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
if (myControllers[i] == nullptr) {
Serial.printf("CALLBACK: Controller is connected, index=%d\n", i);
// Additionally, you can get certain gamepad properties like:
// Model, VID, PID, BTAddr, flags, etc.
ControllerProperties properties = ctl->getProperties();
Serial.printf("Controller model: %s, VID=0x%04x, PID=0x%04x\n", ctl->getModelName().c_str(), properties.vendor_id, properties.product_id);
myControllers[i] = ctl;
foundEmptySlot = true;
break;
}
}
if (!foundEmptySlot) {
Serial.println("CALLBACK: Controller connected, but could not found empty slot");
}
}
void onDisconnectedController(ControllerPtr ctl) {
bool foundController = false;
for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
if (myControllers[i] == ctl) {
Serial.printf("CALLBACK: Controller disconnected from index=%d\n", i);
myControllers[i] = nullptr;
foundController = true;
break;
}
}
if (!foundController) {
Serial.println("CALLBACK: Controller disconnected, but not found in myControllers");
}
}
// ========= SEE CONTROLLER VALUES IN SERIAL MONITOR ========= //
void dumpGamepad(ControllerPtr ctl) {
Serial.printf(
"idx=%d, dpad: 0x%02x, buttons: 0x%04x, axis L: %4d, %4d, axis R: %4d, %4d, brake: %4d, throttle: %4d, "
"misc: 0x%02x, gyro x:%6d y:%6d z:%6d, accel x:%6d y:%6d z:%6d\n",
ctl->index(), // Controller Index
ctl->dpad(), // D-pad
ctl->buttons(), // bitmask of pressed buttons
ctl->axisX(), // (-511 - 512) left X Axis
ctl->axisY(), // (-511 - 512) left Y axis
ctl->axisRX(), // (-511 - 512) right X axis
ctl->axisRY(), // (-511 - 512) right Y axis
ctl->brake(), // (0 - 1023): brake button
ctl->throttle(), // (0 - 1023): throttle (AKA gas) button
ctl->miscButtons(), // bitmask of pressed "misc" buttons
ctl->gyroX(), // Gyro X
ctl->gyroY(), // Gyro Y
ctl->gyroZ(), // Gyro Z
ctl->accelX(), // Accelerometer X
ctl->accelY(), // Accelerometer Y
ctl->accelZ() // Accelerometer Z
);
}
// ========= GAME CONTROLLER ACTIONS SECTION ========= //
void processGamepad(ControllerPtr ctl) {
// There are different ways to query whether a button is pressed.
// By query each button individually:
// a(), b(), x(), y(), l1(), etc...
//== PS4 X button = 0x0001 ==//
/*
if (ctl->buttons() == 0x0001) {
// code for when X button is pushed
digitalWrite(builtInLed,HIGH);
*/
//Rewrite above to toggle
if (ctl->a() == 0x0001) {
ToggleState = 1;
if (ToggleState != LastToggleState);
{
digitalWrite(builtInLed, !digitalRead(builtInLed));
Serial.print("ToggleState ");
Serial.println(ToggleState);
Serial.print("LastToggleState ");
Serial.println(LastToggleState);
int pumpState = digitalRead(builtInLed); // set pumpState which is linked to builtInLed.
digitalWrite(IN1pump, HIGH);
digitalWrite(IN2pump, LOW); //Set IN2pump to LOW. Pump is on/off only (full power, no speed or direction)
digitalWrite(ENBpin, pumpState); //Set ENBpin to HIGH. Pump is on/off only (full power, no speed or direction)
Serial.print("pumpState "); //To check code in serial monitor
Serial.println(pumpState);
}
LastToggleState = ToggleState;
}
if (ctl->buttons() != 0x0001) {
// code for when X button is released
}
//== PS4 Square button = 0x0004 ==//
if (ctl->buttons() == 0x0004) {
// code for when square button is pushed
}
if (ctl->buttons() != 0x0004) {
// code for when square button is released
}
//== PS4 Triangle button = 0x0008 ==//
if (ctl->buttons() == 0x0008) {
// code for when triangle button is pushed
}
if (ctl->buttons() != 0x0008) {
// code for when triangle button is released
}
//== PS4 Circle button = 0x0002 ==//
if (ctl->buttons() == 0x0002) {
// code for when circle button is pushed
}
if (ctl->buttons() != 0x0002) {
// code for when circle button is released
}
//== PS4 Dpad UP button = 0x01 ==//
if (ctl->buttons() == 0x01) {
// code for when dpad up button is pushed
}
if (ctl->buttons() != 0x01) {
// code for when dpad up button is released
}
//==PS4 Dpad DOWN button = 0x02==//
if (ctl->buttons() == 0x02) {
// code for when dpad down button is pushed
}
if (ctl->buttons() != 0x02) {
// code for when dpad down button is released
}
//== PS4 Dpad LEFT button = 0x08 ==//
if (ctl->buttons() == 0x08) {
// code for when dpad left button is pushed
}
if (ctl->buttons() != 0x08) {
// code for when dpad left button is released
}
//== PS4 Dpad RIGHT button = 0x04 ==//
if (ctl->buttons() == 0x04) {
// code for when dpad right button is pushed
}
if (ctl->buttons() != 0x04) {
// code for when dpad right button is released
}
//== PS4 R1 trigger button = 0x0020 ==//
if (ctl->buttons() == 0x0020) {
// code for when R1 button is pushed
}
if (ctl->buttons() != 0x0020) {
// code for when R1 button is released
}
//== PS4 R2 trigger button = 0x0080 ==//
if (ctl->buttons() == 0x0080) {
// code for when R2 button is pushed
}
if (ctl->buttons() != 0x0080) {
// code for when R2 button is released
}
//== PS4 L1 trigger button = 0x0010 ==//
if (ctl->buttons() == 0x0010) {
// code for when L1 button is pushed
}
if (ctl->buttons() != 0x0010) {
// code for when L1 button is released
}
//== PS4 L2 trigger button = 0x0040 ==//
if (ctl->buttons() == 0x0040) {
// code for when L2 button is pushed
}
if (ctl->buttons() != 0x0040) {
// code for when L2 button is released
}
//== LEFT JOYSTICK - UP ==//
//Code for Forward
/*if (ctl->axisY() <= -25) {
// map joystick values to motor speed
int motorSpeed = map(ctl->axisY(), -25, -508, 50, 255);
digitalWrite(IN1pin, LOW);
digitalWrite(IN2pin, HIGH);
analogWrite(ENApin, motorSpeed);
} else {
// analogWrite(ENApin, LOW);
*/
if (ctl->axisY() <= -25) {
// map joystick values to motor speed
int motorSpeed = map(ctl->axisY(), -25, -512, 50, 255);
digitalWrite(IN1pin, LOW);
digitalWrite(IN2pin, HIGH);
analogWrite(ENApin, motorSpeed);
}
//== LEFT JOYSTICK - DOWN ==//
//Code for reverse
else if (ctl->axisY() >= 25) {
// map joystick values to motor speed
int motorSpeed = map(ctl->axisY(), 25, 512, 50, 255);
digitalWrite(IN1pin, HIGH);
digitalWrite(IN2pin, LOW);
analogWrite(ENApin, motorSpeed);
}
else {
analogWrite(ENApin, 0);
}
//== LEFT JOYSTICK - LEFT ==//
if (ctl->axisX() <= 1) {
// code for when left joystick is pushed left
}
//== LEFT JOYSTICK - RIGHT ==//
if (ctl->axisX() >= 6) {
// code for when left joystick is pushed right
}
/*
//== LEFT JOYSTICK DEADZONE ==//
if (ctl->axisY() > -25 && ctl->axisY() < 25) {
//if (ctl->axisY() >= -24 && ctl->axisY() <= 24) {
digitalWrite(ENApin, LOW);
// code for when left joystick is at idle
}
*/
//== RIGHT JOYSTICK - X AXIS ==//
if (ctl->axisRX()) {
// code for when right joystick moves along x-axis
}
//== RIGHT JOYSTICK - Y AXIS ==//
if (ctl->axisRY()) {
// code for when right joystick moves along y-axis
}
dumpGamepad(ctl);
}
void processControllers() {
for (auto myController : myControllers) {
if (myController && myController->isConnected() && myController->hasData()) {
if (myController->isGamepad()) {
processGamepad(myController);
} else {
Serial.println("Unsupported controller");
}
}
}
}
// Arduino setup function. Runs in CPU 1
void setup() {
digitalWrite(IN1pump, LOW); //start with motors off
digitalWrite(IN2pump, LOW); //Set IN2pump to LOW. Pump is on/off only (full power, no speed or direction)
digitalWrite(ENBpin, LOW); //Set ENBpin to HIGH. Pump is on/off only (full power, no speed or direction)
//Assign DC Motor &builtInLed pin INPUT/OUTPUT
pinMode(builtInLed, OUTPUT);
pinMode(ENApin, OUTPUT);
pinMode(IN1pin, OUTPUT);
pinMode(IN2pin, OUTPUT);
pinMode(IN3pin, OUTPUT);
pinMode(IN4pin, OUTPUT);
pinMode(ENBpin, OUTPUT);
pinMode(IN1pump, OUTPUT);
pinMode(IN2pump, OUTPUT);
pinMode(TogglePin, OUTPUT);
xServo.attach(xServoPin);
yServo.attach(yServoPin);
Serial.begin(115200);
Serial.printf("Firmware: %s\n", BP32.firmwareVersion());
const uint8_t* addr = BP32.localBdAddress();
Serial.printf("BD Addr: %2X:%2X:%2X:%2X:%2X:%2X\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
// Setup the Bluepad32 callbacks
BP32.setup(&onConnectedController, &onDisconnectedController);
// "forgetBluetoothKeys()" should be called when the user performs
// a "device factory reset", or similar.
// Calling "forgetBluetoothKeys" in setup() just as an example.
// Forgetting Bluetooth keys prevents "paired" gamepads to reconnect.
// But it might also fix some connection / re-connection issues.
BP32.forgetBluetoothKeys();
// Enables mouse / touchpad support for gamepads that support them.
// When enabled, controllers like DualSense and DualShock4 generate two connected devices:
// - First one: the gamepad
// - Second one, which is a "virtual device", is a mouse.
// By default, it is disabled.
BP32.enableVirtualDevice(false);
}
// Arduino loop function. Runs in CPU 1.
void loop() {
// This call fetches all the controllers' data.
// Call this function in your main loop.
bool dataUpdated = BP32.update();
if (dataUpdated)
processControllers();
// The main loop must have some kind of "yield to lower priority task" event.
// Otherwise, the watchdog will get triggered.
// If your main loop doesn't have one, just add a simple `vTaskDelay(1)`.
// Detailed info here:
// https://stackoverflow.com/questions/66278271/task-watchdog-got-triggered-the-tasks-did-not-reset-the-watchdog-in-time
// vTaskDelay(1);
delay(300);
}
