Trying Out XIAO ESP32C3 Part 6 (Communication with DUALSHOCK 4 using Node-RED)

Info

This article is translated from Japanese to English.

https://404background.com/program/esp32c3-6/

Introduction

In this post, I tried operating a small robot equipped with a XIAO ESP32C3 using a DUALSHOCK 4 controller. In my previous research, I had concluded that direct communication between a DUALSHOCK 4 and a XIAO ESP32C3 was not possible because they use different Bluetooth standards.

▼I investigated the differences between Bluetooth Classic and BLE in this article:

A Little Research: ESP32C3 and Bluetooth Standards

Info This article is translated from Japanese to English. Introduction On this website, I frequently summarize how to connect an ESP32 with a PS4 controller (D…

However, I recently developed a Node-RED node called "dualshock4" that can communicate with the controller. I decided to test if I could use Node-RED as a relay to facilitate communication.

▼Details about the dualshock4 node:

Developing Nodes for Node-RED Part 4 (dualshock4 Node)

Info This article is translated from Japanese to English. Introduction In this post, I developed the "dualshock4" node, which enables communication between Nod…

▼The product page for the XIAO ESP32C3 is here:

https://akizukidenshi.com/catalog/g/g117454

ノーブランド品
¥1,275 (2026/02/21 19:12時点 | Amazon調べ)

▼Previous series articles:

Trying Out XIAO ESP32C3 Part 2 (analogWrite Function and Servo Motors)

Info This article is translated from Japanese to English. Introduction In this post, I experimented with the analogWrite function and servo motor control using…

Trying Out XIAO ESP32C3 Part 4 (WiFi Access Point and Two-Wheeled Robot)

Info This article is translated from Japanese to English. Introduction In this post, I configured the XIAO ESP32C3 as a WiFi access point so that it can be ope…

Node-RED Side Settings

Using the dualshock4 node, I receive inputs from the DUALSHOCK 4 and send them via MQTT. The wheels are set to rotate according to the Y-axis values of the Right and Left sticks. The output values of the sticks range from -1 to 1, but since I need to send values between 0 and 180 to the microcontroller, I use the "range" node for conversion.

▼It looks complex due to the number of pins, but the data is ultimately sent through the mqtt out node at the bottom.

[{"id":"6d44dbded6b925e9","type":"inject","z":"d6fad9e77ecc8414","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":2330,"y":460,"wires":[["1b99baa42461afc5"]]},{"id":"e448101471980908","type":"inject","z":"d6fad9e77ecc8414","name":"Finish","props":[{"p":"kill","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":2330,"y":500,"wires":[["1b99baa42461afc5"]]},{"id":"39761b80535b3527","type":"ui_gauge","z":"d6fad9e77ecc8414","name":"","group":"62a28eb47ad4b20e","order":6,"width":0,"height":0,"gtype":"compass","title":"Left X","label":"","format":"{{value}}","min":"1","max":"-1","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":2930,"y":400,"wires":[]},{"id":"3ce2279ab30c4ce3","type":"ui_gauge","z":"d6fad9e77ecc8414","name":"","group":"62a28eb47ad4b20e","order":6,"width":0,"height":0,"gtype":"compass","title":"Left Y","label":"","format":"{{value}}","min":"-1","max":"1","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":2930,"y":440,"wires":[]},{"id":"d712775e6b8074bd","type":"ui_gauge","z":"d6fad9e77ecc8414","name":"","group":"6e28d9e8ae4ff93d","order":6,"width":0,"height":0,"gtype":"compass","title":"Right X","label":"","format":"{{value}}","min":"1","max":"-1","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":2940,"y":480,"wires":[]},{"id":"76d35c2ab7bfa619","type":"ui_gauge","z":"d6fad9e77ecc8414","name":"","group":"6e28d9e8ae4ff93d","order":6,"width":0,"height":0,"gtype":"compass","title":"Right Y","label":"","format":"{{value}}","min":"-1","max":"1","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":2940,"y":520,"wires":[]},{"id":"81258300e2a180ca","type":"ui_gauge","z":"d6fad9e77ecc8414","name":"","group":"62a28eb47ad4b20e","order":6,"width":0,"height":0,"gtype":"gage","title":"L2","label":"","format":"{{value}}","min":"-1","max":"1","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":2930,"y":560,"wires":[]},{"id":"8a00aa6ab2b170be","type":"ui_gauge","z":"d6fad9e77ecc8414","name":"","group":"6e28d9e8ae4ff93d","order":6,"width":0,"height":0,"gtype":"gage","title":"R2","label":"","format":"{{value}}","min":"-1","max":"1","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":2930,"y":600,"wires":[]},{"id":"d6ec5892bc127fe9","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":"3","height":"1","name":"","label":"Cross","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2770,"y":260,"wires":[]},{"id":"9cc87a9c3e6f0035","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":"3","height":"1","name":"","label":"Circle","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2770,"y":300,"wires":[]},{"id":"1f57f89452d3afe7","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":"3","height":"1","name":"","label":"Square","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2780,"y":340,"wires":[]},{"id":"46f52d05fa3c247a","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":"3","height":"1","name":"","label":"Triangle","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2780,"y":380,"wires":[]},{"id":"1a75d59c7a27052a","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":0,"height":0,"name":"","label":"PS","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2770,"y":460,"wires":[]},{"id":"aa67e9490b49afb8","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":0,"height":0,"name":"","label":"Share","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2770,"y":420,"wires":[]},{"id":"a54da400d2fea125","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":0,"height":0,"name":"","label":"Options","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2780,"y":500,"wires":[]},{"id":"0c3040e3a045dde5","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":"3","height":"1","name":"","label":"L3","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2770,"y":540,"wires":[]},{"id":"75885030a7baedc9","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":"3","height":"1","name":"","label":"R3","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2770,"y":580,"wires":[]},{"id":"2c5e7d6378b5313d","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":"3","height":"1","name":"","label":"L1","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2770,"y":620,"wires":[]},{"id":"c0e5db9534442249","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":"3","height":"1","name":"","label":"R1","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2770,"y":660,"wires":[]},{"id":"ac98c870a5f214d2","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":"3","height":"1","name":"","label":"Up","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2770,"y":700,"wires":[]},{"id":"066bb1ab3979c7ae","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":"3","height":"1","name":"","label":"Down","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2770,"y":740,"wires":[]},{"id":"4dd05ee6ec60fadb","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":"3","height":"1","name":"","label":"Left","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2770,"y":780,"wires":[]},{"id":"91fd7eba418305a4","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":"3","height":"1","name":"","label":"Right","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2770,"y":820,"wires":[]},{"id":"c5f5d5bb1ed13491","type":"ui_text","z":"d6fad9e77ecc8414","group":"227d3ca77163ce82","order":0,"width":0,"height":0,"name":"","label":"TouchPad","format":"{{msg.payload}}","layout":"row-spread","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":2780,"y":860,"wires":[]},{"id":"f3e18b9fa99890ea","type":"range","z":"d6fad9e77ecc8414","minin":"-1","maxin":"1","minout":"0","maxout":"180","action":"scale","round":false,"property":"payload","name":"","x":2780,"y":940,"wires":[["3592dfa80b138081"]]},{"id":"4cf5f35918f4293c","type":"range","z":"d6fad9e77ecc8414","minin":"-1","maxin":"1","minout":"180","maxout":"0","action":"scale","round":false,"property":"payload","name":"","x":2780,"y":900,"wires":[["790ecd8e383b167b"]]},{"id":"790ecd8e383b167b","type":"mqtt out","z":"d6fad9e77ecc8414","name":"","topic":"/right","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"f414466379b9f58d","x":2930,"y":900,"wires":[]},{"id":"3592dfa80b138081","type":"mqtt out","z":"d6fad9e77ecc8414","name":"","topic":"/left","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"f414466379b9f58d","x":2930,"y":940,"wires":[]},{"id":"1b99baa42461afc5","type":"dualshock4_multi","z":"d6fad9e77ecc8414","name":"","sleep":"100","x":2520,"y":480,"wires":[["d6ec5892bc127fe9"],["9cc87a9c3e6f0035"],["1f57f89452d3afe7"],["46f52d05fa3c247a"],["aa67e9490b49afb8"],["1a75d59c7a27052a"],["a54da400d2fea125"],["0c3040e3a045dde5"],["75885030a7baedc9"],["2c5e7d6378b5313d"],["c0e5db9534442249"],["ac98c870a5f214d2"],["066bb1ab3979c7ae"],["4dd05ee6ec60fadb"],["91fd7eba418305a4"],["c5f5d5bb1ed13491"],["39761b80535b3527"],["3ce2279ab30c4ce3","f3e18b9fa99890ea"],["d712775e6b8074bd"],["76d35c2ab7bfa619","4cf5f35918f4293c"],["81258300e2a180ca"],["8a00aa6ab2b170be"]]},{"id":"62a28eb47ad4b20e","type":"ui_group","name":"Left","tab":"f6642a80f4f5605a","order":3,"disp":true,"width":"6","collapse":false,"className":""},{"id":"6e28d9e8ae4ff93d","type":"ui_group","name":"Right","tab":"f6642a80f4f5605a","order":4,"disp":true,"width":"6","collapse":false,"className":""},{"id":"227d3ca77163ce82","type":"ui_group","name":"Buttons","tab":"f6642a80f4f5605a","order":2,"disp":true,"width":"6","collapse":false,"className":""},{"id":"f414466379b9f58d","type":"mqtt-broker","name":"","broker":"localhost","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""},{"id":"f6642a80f4f5605a","type":"ui_tab","name":"Controller","icon":"dashboard","disabled":false,"hidden":false}]

▼The value range is adjusted using the range node.

Specifying the speed in the 0–180 range is a leftover from when I was developing a node for a 180-degree rotation servo. Of course, the value range could also be handled on the microcontroller side.

Microcontroller Side Settings

I used the small robot from a previous project and set it up to be operated via MQTT communication with Node-RED.

▼In this previous article, the robot was moved by receiving specific commands, but this time I am sending the rotation speed for each continuous rotation servo.

Control Microcontrollers with Voice (Node-RED, Gemma2, Faster Whisper, XIAO ESP32C3)

Introduction  In this article, I tried to control a small robot using a microcontroller by voice. This is a summary of what I have tried before.  To operate …

I created the program while consulting with ChatGPT. The continuous rotation servos rotate based on the values received from the /right and /left MQTT topics.

#include <WiFi.h>
#include <PubSubClient.h>

const char* ssid = "<your SSID>";
const char* password = "<your password>";
const char* mqtt_server = "<your ip>";  

const int ServoPin1 = D1;
const int ServoPin2 = D2;
const int DutyMax = 2300;
const int DutyMin = 700;
int speed1 = 0;
int speed2 = 0;

WiFiClient espClient;
PubSubClient client(espClient);

void ServoSpeed(int pin, int speed) {
  int Duty = map(speed, -10, 10, DutyMin, DutyMax);
  digitalWrite(pin, HIGH);
  delayMicroseconds(Duty);
  digitalWrite(pin, LOW);
  delayMicroseconds(20000 - Duty);
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (client.connect("ESP32Client")) {
      Serial.println(" Connection successful");
      client.subscribe("/right");
      client.subscribe("/left");
    } else {
      Serial.print(" Failed (rc=");
      Serial.print(client.state());
      Serial.println(") Retrying in 5 seconds");
      delay(1000);
    }
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  String message;
  for (int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  int value = message.toInt();
  int mappedSpeed = map(value, 0, 180, -10, 10);

  if (strcmp(topic, "/right") == 0) {
    speed1 = mappedSpeed;
    Serial.print("Right wheel speed: "); Serial.println(speed1);
  } else if (strcmp(topic, "/left") == 0) {
    speed2 = mappedSpeed;
    Serial.print("Left wheel speed: "); Serial.println(speed2);
  }
}

void setup() {
  Serial.begin(115200);

  pinMode(ServoPin1, OUTPUT);
  pinMode(ServoPin2, OUTPUT);
  ServoSpeed(ServoPin1, 0);
  ServoSpeed(ServoPin2, 0);

  Serial.print("Connecting to WiFi...");
  WiFi.begin(ssid, password);
  int attempt = 0;
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
    attempt++;
    if (attempt > 20) {
      Serial.println("\nWiFi connection failed. Restarting.");
      ESP.restart();
    }
  }
  Serial.println("\nConnected to WiFi");

  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  reconnect();
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  ServoSpeed(ServoPin1, speed1);
  ServoSpeed(ServoPin2, speed2);
}

The continuous rotation servo is controlled without using a library.
▼I tested this method in this article:

Trying Out XIAO ESP32C3 Part 3 (OLED Display and Continuous Rotation Servo)

Info This article is translated from Japanese to English. Introduction In this post, I experimented with controlling an OLED display and a continuous rotation …

Essentially, it controls the pins by specifying the pulse width.

Checking the Operation

I actually tested the communication and movement.
▼There is some communication lag, causing the machine to wobble and making it difficult to control.

▼Operating it after getting a bit used to it. Since the rotation speed cannot be finely adjusted, turning is quite a challenge.

▼This is how it looks alongside the dashboard:

The rotation speeds of the left and right wheels don't seem perfectly synced, so I’d like to either control them using encoders or use higher-precision motors in the future.

Finally

By using the newly developed dualshock4 node, I am now able to communicate with the XIAO ESP32C3. I want to eliminate the communication lag as much as possible.

▼I feel like the reaction was faster when I was communicating in this article:

Node-REDを使ってみる その4(MQTT通信、ロボットアーム)

はじめに  今回は以前Amazonで購入したロボットアームを、Node-REDでMQTT通信を利用して制御できるようにしてみました。  これまではボタンで制御していたのですが、ネ…

Now that I’ve gathered various technologies that can be used for robot software development, I’m planning to design a new chassis and circuit board for the first time in a while.

▼By the way, when I asked ChatGPT if the XIAO ESP32C3 could communicate directly with a DUALSHOCK 4, it initially said it was possible. When I asked again, it correctly answered that the DUALSHOCK 4 cannot perform BLE communication. It seems AI still has some weaknesses when it comes to hardware specifics.

Leave a Reply

Your email address will not be published. Required fields are marked *