Using PS3/PS4 controller directly with X3 v2.0?

Hello
As I understand from the BLE controller example code, it is possible to connect a PS3 controller to a Mini Control Board X3 with an additional ESP32 module in between.

Since the newer X3 v2.0 now is also based on an ESP32 MCU, I was wondering if it is possible to directly connect a PS3 controller to an X3 v2.0?

We want to give a few Mini Troopers to a small class of children (aged 6+) to play but don’t want to hand out expensive and somewhat fragile smart phones to them. The combination of a cheap an more robust PS3 controller (clone) without additional tinkering would be ideal for us.

Regards
Jérémie

Short answer: This can’t be done unfortunately. We have plans how to overcome this, but will take some time until it happens.


As much as we want to do it - this is complicated topic and not only limited to programming.
We may not be able to embed PS controller support into our products. For personal use there are some libraries to do so, but they are not maintained or incompatible with Arduino. Also X3 v2.0 was not designed to be programmable.

Another issue - industry is shifting from ESP32 to ESP32S3, which does not support Bluetooth Classic (only BLE). This is a requirement for PS controllers to work so more and more products won’t be compatible in the future. As for BLE - there isn’t any popular and cheap/affordable controllers for this purpose.

So we are working on solution for this use case, but can’t specify any estimations at the moment.

1 Like

Many thanks for this in-depth answer, much appreciated.

1 Like

Some updates to this topic.

We have released RoboBoard X3 board which is backwards compatible with previous Mini Control Board and can be programmed with Arduino.

So, Mini Trooper control board can be swapped with new one and programmed for direct control with PS3 controller.

Don’t have full code at the moment, but some example to get going: Tank_MiniTrooper.ino

Hello!

Here is a more finished sample for the MiniTrooper model. It uses the left stick to drive and the X button to lift the flipper:

#include <Arduino.h>
#include <Ps3Controller.h>

void setFlipper(int pos) {
  Servo.A.spinPos(map(pos, 0, 100, -70, 80));
}

// Initialize program
void setup() {
  // Enable RoboBoard X3 3V3 LDO regulator
  // Note: always enabled "true" by default.
  Board.setEnable3V3(true);
  // Enable RoboBoard X3 charging mode
  // Powers off the board and displays RGB animation while charging
  // Note: not recommended if using RoboBoard X3 as development board
  Board.setChargingMode(false);
  // Enable board status indication with RGB lights
  // - displays battery state on power on
  // - blink red if restarted due low battery
  // - displays Totem App connection state
  Board.setStatusRGB(true);
  // Enable board status indication with motor beep sounds
  // - beep when board is powered up
  // - beep on Totem App connect / disconnect
  Board.setStatusSound(true);
  // Save configuration to memory
  // This code may be removed and all configuration
  // will be loaded automatically during board startup
  Board.settingsSave();

   /**
   * Tank drive configuration
   */
  // Assign left wheel to DC motor port A
  Drivetrain.setWheelLeft(DC.A);
  // Assign right wheel to DC motor port B
  Drivetrain.setWheelRight(DC.B);
  // Select tank drive
  Drivetrain.setDriveTank();
  // Wait for USB cable to be pulled out
  // Prevents start driving

  // Prevents start driving
  Board.waitUSB();

  delay(500);

  Serial.begin(115200);
  Ps3.begin("12:34:56:78:91:10");
}
// Loop program
void loop() {
    if(!Ps3.isConnected())
    {
      Serial.println("Not Connected.");
      return;
    }

    Ps3.setPlayer(1);
        
    int y = -Ps3.data.analog.stick.ly;
    int x = Ps3.data.analog.stick.lx;

    if (y > -30 && y < 30)
    {
      y = 0;
    }

    if (x > -30 && x < 30)
    {
      x = 0;
    }

    Drivetrain.driveTurn(map(y, -128, 128, -80, 80), map(x, -128, 128, -80, 80));
    
    delay(100);
    
    if (Ps3.data.button.cross)
    {
      setFlipper(100);
    }
    else
    {
      setFlipper(0);
    }

    delay(100);
}
1 Like

Hey @MikeP , thanks for sharing with your code!
I see there are too little examples for specific robotic kits. Sorry about that. We are currently working a lot with RoboBoard hardware and software improvements. Some features are still being developed and improved, but current software state is almost finalized.

I have improved your example code with a few things:

  • Using initRoboBoard() to change board settings.
    Avoid using Board.settingsSave() for now as it may be confusing. You can erase “saved” settings with Board.settingsErase() or Arduino IDE ToolsErase All Flash Before Sketch Upload.
  • Added wait for PS3 controller connection
  • Added RGB blink when controller connected
  • Added wait to pull USB cable out (optional)
  • Added “Joystick” module to smooth out controller input
  • Added flipper control with right trigger
  • Left stick for driving, right for turning

Note: some PS3 controllers has very bad sticks with huge deadzones. This limits precision.
Joystick module also applies quadratic function (digit 3) to allow MiniTrooper driving at low speed when stick is moved only a little bit.

#include <Arduino.h>
#include <Ps3Controller.h>

// Move flipper to position [0:100]%. 0 - full closed, 100 - full open
void moveFlipper(int pos) {
  Servo.A.spinPos(map(pos, 0, 100, -70, 80));
}

// Called before function setup() (and RoboBoard firmware start)
// Settings inside this function will be default ones
void initRoboBoard() {
  // Display battery state when board is powered on
  Board.setStatusRGB(true);
  // Beep sound with motors when board is powered on
  Board.setStatusSound(true);
  // Note: Board.settingsSave() is not required here
}
// Initialize program
void setup() {
  Serial.begin(115200);
  // Configure "Drivetrain" module for MiniTrooper
  // Assign left wheel to DC motor port A
  Drivetrain.setWheelLeft(DC.A);
  // Assign right wheel to DC motor port B
  Drivetrain.setWheelRight(DC.B);
  // Select tank drive logic
  Drivetrain.setDriveTank();
  
  // Begin communication with PS3 controller
  Ps3.begin("00:02:03:04:05:06"); // <<<---- Set MAC address of your PS3 controller
  
  // Wait for PS3 controller to connect
  while (!Ps3.isConnected()) {
    Serial.println("Waiting for PS3 controller...");
    delay(1000);
  }
  Serial.println("PS3 controller connected !");

  // Blink RGB lights when controller is connected
  RGB.setDim(255); // Set maximum brightness
  for (int i=0; i<5; i++) { // Loop 5 times
    RGB.color(Color::Green);
    delay(50);
    RGB.off();
    delay(50);
  }
  RGB.setDim(128); // Lower brightness (more comfortable to look at)
  RGB.colorTotem(); // Set lights to Totem colors

  // Don't drive if USB cable is plugged in.
  // Not required, just a precaution for robot
  // not to drive around when plugged into a PC.
  while (!Board.waitUSB(1000)) {
    Serial.println("Waiting for USB cable to be pulled out...");
  }
}
// Loop program
void loop() {
  // Special "Joystick" wrapper that helps to process input values
  // Convert raw joystick trigger input to [0:100]% value
  int trigger = Joystick::getTrigger(Ps3.data.analog.button.r2);
  // Convert left joystick stick Y axis position to [-100:100]% value (with smooth filter)
  int drive = Joystick::getAxis(3, -Ps3.data.analog.stick.ly);
  // Convert right joystick stick X axis position to [-100:100]% value (with smooth filter)
  int turn = Joystick::getAxis(3, Ps3.data.analog.stick.rx); // Change to "lx" if want to control turn with left stick

  // Update motors with joystick values
  Drivetrain.driveTurn(drive, turn);

  // If cross button is pressed - open flipper fully
  if (Ps3.data.button.cross) trigger = 100;

  // Update flipper position with input from controller
  moveFlipper(trigger);
}

Hi Arnas,

Great stuff, I’ve updated my code and all is working great! A couple of additional points:

  1. For the MiniTrooper robot the Servo is restricted and the setFlipper method needs to be changed as follows to set the range to -70 to 80:
void moveFlipper(int pos) {
  Servo.A.spinPos(map(pos, 0, 100, -70, 80));
}
  1. For me it was necessary to change the MAC address to start with a 00. I noticed that you did the same (00:02:03:04:05:06) I think it is to make sure the the address is in local unicast format which seems to be required (I used SixaxisPairTool for this).

  2. In the drive commands I added an inversion of the x axis for driving backwards so that a right stick command will make the robot turn to the right when it is going backwards as well:

if (drive < 0) {turn = -turn;};

  // Update motors with joystick values
  Drivetrain.driveTurn(drive, turn);

Thanks again,
Mike

For the MiniTrooper robot the Servo is restricted and the setFlipper method needs to be changed as follows to set the range to -70 to 80

By “restricted” you mean it physically can’t move further (ramming into frame), or motor does not spin? Frame is designed to work in full servo range. Position “100” (full open) helps MiniTrooper to easier flip back onto wheels (when it’s upside down). Maybe something is not right.

There also was single batch of servo motors that does not work well above 90% position. For this reason TotemApp limits range to [-85:85]%.

But it’s perfectly fine to use [-70:80]% range. Just curious what’s the issue.

For me it was necessary to change the MAC address to start with a 00

Yes, starting with ESP32 Arduino 2.0.0 version, Bluetooth API requires that MAC address starts with 00. Previously it did work with any value (as I remember). Library calls this function: esp_base_mac_addr_set(const uint8_t *mac)

if (drive < 0) {turn = -turn;};

Ha, interesting! Haven’t thought about this. It’s probably everyone’s control preference, but a nice option for sure.

Hello!

Thanks for the extra info!

With the servos I have on the MiniTrooper, a command above 89 degrees will not be executed i.e. the servo does not move… This also happens when I turn off the setting restricting the movement to -85 to 85 in the totem app. Maybe I have motors of that batch…

Regards,
Mike

Sorry, one more question:

I saw on the Arduino Tool options that there is a setting to switch remote control between default and TotemApp. Is it always one or the other or is it possible to switch it on and connect the App or the PS3 depending on what we want to do? Or do I need to recompile and download every time to switch from the App to the controller?

Thanks,
Mike

Yeah, that option (ToolsRemote control) may be misleading.

  • Default - default state (not enabled). Totem App connectivity is not added to the code.
  • Totem App - connection with Totem smartphone app is enabled.

Selecting option Totem App is equivalent to:

void setup() {
  TotemApp.begin();
}
void loop() { }

it simply starts Bluetooth server for Totem app to connect to. Option Default does not add this line to the code.

Before ESP32 Arduino core version 2.0.0, PS3 and Totem App did work both at the same time. With these new versions - something has changed and it doesn’t anymore. You have to use either PS3 or Totem App (need to recompile the code).
The thing is - PS3 uses Bluetooth Classic and Totem app - Bluetooth Low Energy. Probably don’t play along anymore.

One interesting fact: we use regular “ESP32” exclusively, because it’s the only one Espressif chip that supports Bluetooth Classic. There are newer variants (like ESP32S3), but they only have Bluetooth Low Energy, so no PS3 and PS4 gamepads!

1 Like