Problems interfacing my Holybro Pixhawk 6x with my ESP32

Hi everybody,

I am working on integrating an ESP32 LTE module for telemetry communication using the mobile network. To achieve this, I plan to connect the module to my ESP32.

However, I am having trouble connecting my Pixhawk 6x to my ESP32. Despite my attempts, I cannot establish a connection, and I am not receiving any error messages to help me troubleshoot.

Here are the components I am using:

Firebeetle ESP32-E (for testing only)

Holybro Pixhawk 6x

JST-GH to Pins cable

This is my setup:

I am using the "Telemetry 1" port on my Pixhawk.

The Baud rate is set to 57600.

MAVLink Protocol is on 2, but I have also tested it on 1 and switch to 2.

Regarding the wiring, I have connected:

Pin 1 (Red) -> Vcc

Pin 2 (Black) -> GPIO 16

Pin 3 (Black) -> GPIO 17

Pin 4/5 -> Nothing

Pin 6 -> GND

I have also tried testing the connection by powering the Pixhawk with a power bank and only having GND connected. I have also tested it with only RX/TX connected.

Unfortunately, I cannot establish a connection between the ESP32 and the Pixhawk. I have read the documentation on serial communication and UART wiring, but I am still unable to get it to work. I have also tried other people’s code, but it did not work as expected.

I have created a proof-of-concept code block that should short the SoftwareSerial and Hardware Serial connections, essentially creating a USB-to-Telemetry1 interface, but this also did not work.

Could anyone provide me with guidance on what I am doing wrong? I would greatly appreciate any help to verify that I can interface my Pixhawk with my ESP32.

Proof of concept code:

#include <SoftwareSerial.h>

#define RX_PIN 16
#define TX_PIN 17

SoftwareSerial softSerial(RX_PIN, TX_PIN);

void setup() {
  Serial.begin(115200);
  softSerial.begin(57600);
}

void loop() {
  // Read from USB serial and write to software serial
  while (Serial.available()) {
    byte data = Serial.read();
    softSerial.write(data);
  }
  
  // Read from software serial and write to USB serial
  while (softSerial.available()) {
    byte data = softSerial.read();
    Serial.write(data);
  }
}

Full Code (not claiming full authorship, partially copied):

#include <WiFi.h>
#include <SoftwareSerial.h>

// Wi-Fi network credentials
const char* ssid = [REDACTED];
const char* password = [REDACTED];

// TCP server settings
const char* server_address = [REDACTED];
const int server_port = 5761;

WiFiClient client;
SoftwareSerial swSerial(16, 17);  // RX, TX

void setup() {
  Serial.begin(115200);
  swSerial.begin(57600);
  delay(10);

  // Connect to Wi-Fi
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Connect to TCP server
  Serial.println("Connecting to TCP server...");
  if (client.connect(server_address, server_port)) {
    Serial.println("Connected to TCP server");
  } else {
    Serial.println("Failed to connect to TCP server");
  }
}

void loop() {
  // Read data from software serial and send it over the TCP connection
  if (swSerial.available()) {
    int numBytesToRead = swSerial.available();
    uint8_t* bytesRead = new uint8_t[numBytesToRead];
    int i = 0;

    while (swSerial.available() && i < numBytesToRead) {
      bytesRead[i++] = swSerial.read();
      delay(10);  // Allow some time for the next byte to arrive
    }

    Serial.print("Sending message: ");
    for (int j = 0; j < i; j++) {
      Serial.print(bytesRead[j], HEX);
      Serial.print(" ");
    }
    Serial.println();
    client.write(bytesRead, i);
    delete[] bytesRead;  // Free the dynamically allocated memory
  }

  // Read data from the TCP connection and write it to software serial
  if (client.available()) {
    int numBytesToRead = client.available();
    uint8_t* bytesRead = new uint8_t[numBytesToRead];
    int i = 0;

    while (client.available() && i < numBytesToRead) {
      bytesRead[i++] = client.read();
      delay(10);  // Allow some time for the next byte to arrive
    }

    Serial.print("Received message: ");
    for (int j = 0; j < i; j++) {
      Serial.print(bytesRead[j], HEX);
      Serial.print(" ");
      swSerial.write(bytesRead[j]);
    }
    Serial.println();
    delete[] bytesRead;  // Free the dynamically allocated memory
  }
}

So, can you see any bytes flowing or is nothing arriving from the Pixhawk?

Is the wiring correct? Do you have ground connected? And are Tx/Rx crossed over? So Tx goes to Rx and vice-versa?

And have you checked mavlink status to check that MAVLink is being sent? And make sure flow control of the Telem 1 mavlink instance is OFF (unless you have flow control pins connected).

Its strange. Most of the time I see nothing, Sometimes I see obvious garbage (Single bytes or like 200 bytes in one message), and sometimes it just mirrors back what I send.

I think the wiring is correct, I have swapped RX and TX to check if this helps, nothing. I have tested it with ground and 5V connected, Only with ground and only RX/TX (Powered over USB Port). Still nothing.

I feel like I am running around in circles. If somebody could give me something that I could flash on my ESP and give me a quick guide what I have to do and what I should be able to see just to verify that I can even talk to my Pixhawk.

Thanks anyway for the advise so far :thumbsup:

That’s what binary MAVLink looks like…

By obvious garbage I did not mean non-string bytes. But it wasn’t in the expected format. (Frames starting with 0xFE, 0x…) but it seem random, so i assumed that it is garbage.

Right, dunno then. Make sure you have the baudrate correct :thinking:. Not sure what else could be wrong.

It seems there was something wrong with the pixhawk firmware. I re flashed the pixhawk with ardupilot and as soon as I did this, my old code started working. I had to fix some sequencing issues, but now it works like expected and I can talk to my Pixhawk using MissionPlanner and QGroundControl.
Thanks for taking a look at it, Your input caused me to consider other error sources other than my code :smiley:

If anybody is interested, this is the code that ended up working. It connects to a Server that acts like a TCP bridge:

#include <WiFi.h>
#include <SoftwareSerial.h>

// Wi-Fi network credentials
const char* ssid = "[REDACTED]";
const char* password = "[REDACTED]";

// TCP server settings
const char* server_address = "[REDACTED]";
const int server_port = 5761;

WiFiClient client;
SoftwareSerial swSerial(16, 17);  // RX, TX

void setup() {
  Serial.begin(115200);
  swSerial.begin(57600);
  delay(10);

  // Connect to Wi-Fi
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Connect to TCP server
  Serial.println("Connecting to TCP server...");
  if (client.connect(server_address, server_port)) {
    Serial.println("Connected to TCP server");
  } else {
    Serial.println("Failed to connect to TCP server");
  }
}

enum ReadState { WAIT_START, READ_LENGTH, READ_DATA };

ReadState state = WAIT_START;
uint8_t payload_length = 0;
size_t bytesRead = 0;
const size_t bufferSize = 1024;
uint8_t buffer[bufferSize];

void loop() {
  // Read data from software serial and send it over the TCP connection
  while (swSerial.available()) {
    uint8_t c = swSerial.read();
    switch (state) {
      case WAIT_START:
        if (c == 0xFD) {
          buffer[bytesRead++] = c;
          state = READ_LENGTH;
        }
        break;
      case READ_LENGTH:
        payload_length = c;
        buffer[bytesRead++] = c;
        state = READ_DATA;
        break;
      case READ_DATA:
        buffer[bytesRead++] = c;
        if (bytesRead == payload_length + 12) {
          client.write(buffer, bytesRead);
          bytesRead = 0;
          state = WAIT_START;
        }
        break;
    }
  }

  // Read data from the TCP connection and write it to software serial
  if (client.available()) {
    uint8_t c = client.read();
    swSerial.write(c);
  }

  //delay(1);
}

Nice, glad you got it to work. And thanks for sharing the example code.

can you guide me what settings you have selected in the mission planner for this udp connection my mission planner is not getting connect with udp via esp32

I did not use UDP. I wrote a custom TCP server that routes packets sent from the ESP to MissionPlanner. Here is my Github repository if you wanna take a look at my integration. GitHub - nicololuescher/Pixhawk-LTE-Integration. I am not sure if it is up to date, but this should give you a head start.

THANKS for now i connected my esp32 with my telem2 and want to display the data of pixhawk sensors on my serial monitor but unfortunately i am receiving messages like this:


where as this is my code i used for this `

#include <SoftwareSerial.h>
#include <mavlink.h>

#define RX_PIN 16
#define TX_PIN 17
#define BAUD_RATE 57600

EspSoftwareSerial::BasicUARTEspSoftwareSerial::GpioCapabilities mavSerial(RX_PIN, TX_PIN);

void setup() {
Serial.begin(9600);
mavSerial.begin(BAUD_RATE);
}

void loop() {
// Check for incoming MAVLink messages
mavlink_message_t msg;
mavlink_status_t status;

while (mavSerial.available()) {
uint8_t c = mavSerial.read();
if (mavlink_parse_char(MAVLINK_COMM_0, c, &msg, &status)) {
// Message received, handle it
handleMavlinkMessage(msg);
}
}

// Send heartbeat message to Pixhawk
static uint32_t lastHeartbeatTime = 0;
uint32_t now = millis();
if (now - lastHeartbeatTime >= 1000) {
sendHeartbeat();
lastHeartbeatTime = now;
}

// Your existing loop code goes here
// …
}

void sendHeartbeat() {
mavlink_message_t msg;
mavlink_msg_heartbeat_pack(1, 200, &msg, MAV_TYPE_GCS, MAV_AUTOPILOT_INVALID, MAV_MODE_FLAG_MANUAL_INPUT_ENABLED, 0, MAV_STATE_ACTIVE);
uint16_t len = mavlink_msg_to_send_buffer((uint8_t*)&msg, &msg);
mavSerial.write((uint8_t*)&msg, len);
}

void handleMavlinkMessage(const mavlink_message_t &msg) {
switch (msg.msgid) {
case MAVLINK_MSG_ID_HEARTBEAT: {
Serial.println(“Received heartbeat from Pixhawk”);
break;
}
case MAVLINK_MSG_ID_ATTITUDE: {
mavlink_attitude_t attitude;
mavlink_msg_attitude_decode(&msg, &attitude);
// Print attitude data
Serial.print("Roll: “);
Serial.print(attitude.roll);
Serial.print(”, Pitch: “);
Serial.print(attitude.pitch);
Serial.print(”, Yaw: ");
Serial.println(attitude.yaw);
break;
}
case MAVLINK_MSG_ID_SYS_STATUS: {
mavlink_sys_status_t sys_status;
mavlink_msg_sys_status_decode(&msg, &sys_status);
// Print battery voltage and current
Serial.print("Battery Voltage: ");
Serial.print(sys_status.voltage_battery / 1000.0f);
Serial.print("V, Current: ");
Serial.print(sys_status.current_battery / 100.0f);
Serial.println(“A”);
// Additional battery health and fail-safe data can be printed here if available
break;
}
default: {
Serial.print("Received message with ID ");
Serial.println(msg.msgid);
break;
}
}
}

This does not look like it uses code of mine. If you have questions about the mavlink integration, you should talk to them. But before then, I urge to debug your code. Since you get messages like “Received message with ID #”, it seem like your switch case falls into the default block. You should try to find out what the message id’s represent. Also, you’ve left a lot of ChatGPT output in your code :wink: I have no problems with that, but you might not get any answers if you post to other forums.

Try to find out what exactly is not working at the moment and why that is befor you ask other people. Most of the time, you find the error yourself by doing this and if not, it is much easier to get help if you have clear questions.