Teensy 4.1 with Dynamixel AX 18

I am trying to control a Dynamixel AX-18A servo using Teensy 4.1 via Serial2 (TX: pin 8, RX: pin 7). For half-duplex communication, I am using IC HD74LS241P as a buffer between Teensy and Dynamixel.

However, I ran into a problem where the servo did not respond to the commands I sent. I've tested the same code on Blackpill's STM32F4, and there the servo can move correctly.

The following are some of the things that I have checked:

  • Baudrate is set to 1,000,000 bps.

  • IC HD74LS241P:
    I used HD74LS241P to set up half-duplex communication.
    The DIR (direction control) pin is controlled for switching between TX and RX.
    Power Supply for Dynamixel is enough (using 12V 5A adapter).
    Code to send commands to the servo:

  • The Full Code

#include <Dynamixel2Arduino.h>

#define DXL_SERIAL Serial2
#define DEBUG_SERIAL Serial // Make sure this corresponds to the debugging port
const uint8_t DXL_DIR_PIN = 27;
const float DXL_PROTOCOL_VERSION = 1.0;
Dynamixel2Arduino dxl(DXL_SERIAL, DXL_DIR_PIN);

void setup() {
  DEBUG_SERIAL.begin(115200);
  while (!DEBUG_SERIAL);

  dxl.begin(1000000);
  dxl.setPortProtocolVersion(DXL_PROTOCOL_VERSION);

  // Wait a few seconds to make sure everything is ready
  delay(3000);

  // Make sure the servo is in the correct mode
  dxl.torqueOff(19);
  dxl.setOperatingMode(19, OP_POSITION);
  dxl.torqueOn(19);

  // Try to set the initial position
  if (dxl.setGoalPosition(19, 63, UNIT_DEGREE)) {
    DEBUG_SERIAL.println("Starting position successfully set to 63 degrees");
  } else {
    DEBUG_SERIAL.println("Failed to set the starting position");
  }
}

void loop() {
  delay(3000);
  if (dxl.setGoalPosition(19, 63, UNIT_DEGREE)) {
    DEBUG_SERIAL.println("Successfully set position to 63 degrees");
  } else {
    DEBUG_SERIAL.println("Failed to set position to 63 degrees");
  }

  delay(3000);
  if (dxl.setGoalPosition(19, 165, UNIT_DEGREE)) {
    DEBUG_SERIAL.println("Successfully set position to 165 degrees");
  } else {
    DEBUG_SERIAL.println("Failed to set position to 165 degrees");
  }
}

Despite all this I have done, the servo still does not respond. Has anyone experienced similar problems with Teensy 4.1 and HD74LS241P IC? Is there anything I need to fix in my circuit or code?

Thanks for any help!

Would you think I’m being rude if I asked for an annotated schematic ?

1 Like

Sorry it has been several years since I played with the DXL servos.

Back then I created a Serial port handler for the DXL servos.

No idea if this still works or not, but here is a sketch I had back then

#include <Dynamixel2Arduino.h>
//====================================================================================================
// Quick dirty test to find servos and track them...
// teensy version
//
//====================================================================================================
//============================================================================
// Global Include files
//=============================================================================

//=============================================================================
// Options...
//=============================================================================
typedef struct {
  uint8_t protocol_index;
  uint8_t first_servo_found;
  uint8_t last_servo_found;
} PortList;

class TeensySerialPortHandler : public DYNAMIXEL::SerialPortHandler
{
  public:
    TeensySerialPortHandler(HardwareSerial& port, const int dir_pin = -1)
      : SerialPortHandler(port, dir_pin), port_(port), dir_pin_(dir_pin) {}
    virtual void begin(unsigned long baud) override
    {
      baud_ = baud;

      if (dir_pin_ != -1) {
        port_.begin(baud_);
        port_.transmitterEnable(dir_pin_);
      } else {
        port_.begin(baud_, SERIAL_HALF_DUPLEX);
      }

      setOpenState(true);
    }

    virtual void end(void) override
    {
      port_.end();
      setOpenState(false);
    }

  private:
    HardwareSerial& port_;
    const int dir_pin_;
    uint32_t baud_;
};

#define DXL_SERIAL Serial1
#define DXL_DIR_PIN 2
#define DXL_BAUDRATE 1000000
#define DXL_SERVO_ENABLE_PIN 3
Dynamixel2Arduino dxl;
TeensySerialPortHandler dxl_port(DXL_SERIAL, DXL_DIR_PIN);

#define DEBUG_TOGGLE_PIN 13
#ifdef DEBUG_TOGGLE_PIN
#define DBGTogglePin(pin) digitalWrite(pin, !digitalRead(pin));
#else
#define DBGTogglePin(pin)
#endif

PortList portlist[] = {
  {1},
  {2},
};
#define COUNT_PORT_LIST  (sizeof(portlist)/sizeof(portlist[0]))


//=============================================================================
// Define different robots..
//=============================================================================

// Protocol version
#define PROTOCOL_VERSION                1.0                 // See which protocol version is used in the Dynamixel
#define PROTOCOL_VERSION2                2.0                 // See which protocol version is used in the Dynamixel
#define DEVICENAME                      "3"                 // Check which port is being used on your controller

/** EEPROM AREA **/
#define AX_PRESENT_POSITION_L       36
//=============================================================
// Defines for X series (XL430)
//=============================================================================
#define DXL_X_MODEL_NUMBER         0    // 2 (R)
#define DXL_X_PRESENT_POSITION     132 // 4 (R)



//=============================================================================
// Globals
//=============================================================================
// Global objects
// Handle to port handler and packet handler;
uint8_t g_servo_port_index[254];
int g_servo_positions[254];
uint32_t g_baud_rate = DXL_BAUDRATE;
bool find_servos = true;

void wait_keyboard() {
  Serial.println("Hit any key to continue"); Serial.flush();
  while (!Serial.available()) ;
  while (Serial.read() != -1) ;
}
//====================================================================================================
// Setup
//====================================================================================================
void setup() {

  while (!Serial && (millis() < 3000)) ;  // Give time for Teensy and other USB arduinos to create serial port
  Serial.begin(38400);  // start off the serial port.
  if (CrashReport) {
    Serial.print(CrashReport);
    Serial.println("Press any key to continue");
    while (Serial.read() == -1);
    while (Serial.read() != -1);
  }

  Serial.println("\nDyn2Arduino Track Servos program");
  //  wait_keyboard();
#ifdef DXL_SERVO_ENABLE_PIN
  pinMode(DXL_SERVO_ENABLE_PIN, OUTPUT);
  digitalWrite(DXL_SERVO_ENABLE_PIN, HIGH);
#endif
#ifdef DEBUG_TOGGLE_PIN
  pinMode(DEBUG_TOGGLE_PIN, OUTPUT);
#endif
  dxl.setPort(dxl_port);
  SetBaudRate(DXL_BAUDRATE);

  find_servos = true;

}


//====================================================================================================
// Loop
//====================================================================================================
void loop() {
  // Output a prompt
#ifdef USBHOST_SERIAL
  myusb.Task();
#endif
  if (find_servos) {
    FindServos();
    find_servos = false;
    // lets toss any charcters that are in the input queue
    Serial.println("\nFind Again, enter new baud or just hit enter");
    Serial.println("Or move servos to track their movement");
    Serial.flush();  // make sure the complete set of prompts has been output...
    while (Serial.read() != -1) ; // Remove any stuff still in serial buffer.
  }

  if (Serial.available()) {
    // Get a command
    uint32_t new_baud = CheckForNewBaud();
    if (new_baud) {
      SetBaudRate(new_baud);
    } else {
      wait_keyboard();
    }
    find_servos = true;
  } else {
    TrackServos();
  }
}

//=======================================================================================
XelInfoFromPing_t ping_info[32];
void FindServos(void) {

  Serial.print("\nSearch for all servos at baud rate: ");
  Serial.println(g_baud_rate, DEC);

  // Initialize to no servos...
  for (int i = 0; i < 254; i++) {
    g_servo_port_index[i] = 0xff; // not found
  }

  for (uint8_t port_index = 0; port_index < COUNT_PORT_LIST; port_index++) {
    DBGTogglePin(DEBUG_TOGGLE_PIN);
    Serial.print("Begin Searching");
    portlist[port_index].first_servo_found = 0xff;
    portlist[port_index].last_servo_found = 0xff;

    dxl.setPortProtocolVersionUsingIndex(portlist[port_index].protocol_index);
    Serial.println(dxl.getPortProtocolVersion(), 2);
    //  wait_keyboard();
    if (portlist[port_index].protocol_index == 1) {
      Serial.println("  Begin Protocol 1: ");
      for (int i = 0; i < 254; i++) {
        //Serial.print(".");
        if (dxl.ping(i)) {
          if (g_servo_port_index[i] != 0xff) {
            Serial.println("Multiple servos found with same ID");
          }
          g_servo_port_index[i] = port_index;
          portlist[port_index].last_servo_found = i;
          if (portlist[port_index].first_servo_found == 0xff) portlist[port_index].first_servo_found = i;
          g_servo_positions[i] = static_cast<int>(dxl.getPresentPosition(i));
          Serial.printf("  %d Type:%d Position:%d(%3.2f)\n", i,
                        dxl.getModelNumber(i), g_servo_positions[i],
                        dxl.getPresentPosition(i, UNIT_DEGREE)) ;
        }
      }

      Serial.println("  Done");
    } else {
      Serial.println("  Begin Protocol 2 Simple Ping: ");
      for (uint8_t i = 0; i < 254; i++) {
        //Serial.print("-");
        if (dxl.ping(i)) {
          if (g_servo_port_index[i] != 0xff) {
            Serial.println("Multiple servos found with same ID");
          }
          g_servo_port_index[i] = port_index;
          Serial.printf("  %d Type:%d Position:%d\n", i,
                        dxl.getModelNumber(i), static_cast<int>(dxl.getPresentPosition(i))) ;
        }
      }
      Serial.println("  Begin Protocol 2 ping data: ");
      for (uint8_t i = 0; i < 254; i++) {
        if (dxl.ping(i, ping_info, 1)) {
          //        if (((DYNAMIXEL::Master)dxl).ping(i, ping_info, 1)) {
          if (g_servo_port_index[i] == 1) {
            Serial.println("Multiple servos found with same ID");
          }
          g_servo_port_index[i] = 2;
          portlist[port_index].last_servo_found = i;
          if (portlist[port_index].first_servo_found == 0xff) portlist[port_index].first_servo_found = i;
          Serial.printf("  %d Type:%x Ver:%x Position:%d \n", i,
                        ping_info[0].model_number, ping_info[0].firmware_version,
                        static_cast<int>(dxl.getPresentPosition(i))) ;
        }
      }
      /*
        Serial.println("  Try Protocol 2 - broadcast ping: ");
        Serial.flush(); // flush it as ping may take awhile...

        if (uint8_t count_pinged = dxl.ping(DXL_BROADCAST_ID, ping_info,
                                          sizeof(ping_info) / sizeof(ping_info[0]))) {
        Serial.print("Detected Dynamixel : \n");
        for (int i = 0; i < count_pinged; i++)
        {
          Serial.print("    ");
          Serial.print(ping_info[i].id, DEC);
          Serial.print(", Model:");
          Serial.print(ping_info[i].model_number, HEX);
          Serial.print(", Ver:");
          Serial.println(ping_info[i].firmware_version, HEX);
          g_servo_port_index[i] = 2;
        }
        } else Serial.printf("Broadcast returned no items(%x)\n", dxl.getLastLibErrCode());
      */
      Serial.println("  Done");
    }
  }
}
//=======================================================================================
// TrackServos
//=======================================================================================
#define TRACK_FUDGE 3
void TrackServos() {
  for (uint8_t port_index = 0; port_index < COUNT_PORT_LIST; port_index++) {
    //Serial.printf("%u %u - %u\n", port_index, portlist[port_index].first_servo_found, portlist[port_index].last_servo_found);
    if (portlist[port_index].first_servo_found != 0xff) {
      dxl.setPortProtocolVersionUsingIndex(portlist[port_index].protocol_index);
      for (uint8_t i = portlist[port_index].first_servo_found; i <= portlist[port_index].last_servo_found; i++) {
        if (g_servo_port_index[i] == port_index) {
          int servo_pos = static_cast<int>(dxl.getPresentPosition(i));
          if (abs(servo_pos - g_servo_positions[i]) > TRACK_FUDGE) {
            Serial.print(i, DEC);
            Serial.print("(");
            Serial.print(portlist[port_index].protocol_index, DEC);
            Serial.print("):");
            Serial.print(servo_pos, DEC);
            Serial.print("(");
            float servo_angle = dxl.getPresentPosition(i, UNIT_DEGREE);
            Serial.print(servo_angle, 3);
            Serial.println(")");
            g_servo_positions[i] = servo_pos;
          }
        }
      }
    }
  }
}

//====================================================================================================
// Process command line, optionally return new baud rate
uint32_t CheckForNewBaud(void) {
  int ch;
  uint32_t new_baud = 0;

  for (;;) {
    // throw away any thing less than CR character...
    ch = Serial.read();
    if (ch != -1) {
      if ((ch >= '0') && (ch <= '9')) {
        new_baud = new_baud * 10 + (uint32_t)(ch - '0');
      } else {
        break;
      }
    }
  }
  while (Serial.read() != -1) ; // remove any trailing stuff.
  return new_baud;
}


//=======================================================================================
void SetBaudRate(uint32_t new_baud)
{
  Serial.print("Setting Baud to: ");
  Serial.println(new_baud);

  dxl.begin(new_baud);
  for (uint8_t i = 0; i < COUNT_PORT_LIST; i++) {
    DBGTogglePin(DEBUG_TOGGLE_PIN);
    Serial.printf("  Index: %d\n", i);
    delay(100);
    dxl.setPortProtocolVersionUsingIndex(portlist[i].protocol_index);
    if (portlist[i].protocol_index == 2) {
      for (uint8_t id = 0; id < 254; id++) dxl.reboot(id);
    }
  }
  g_baud_rate = new_baud;
  Serial.println("Finished Setting Baud");
}

Sure, here the schematic

Hi, thank you Kurt. I will try it, and inform the result later ^^

And i was try another library too, like Bioloid Serial from ur Github, and i found some error while compile it. I hope you have some solution about that.

Thank you ^^

I had the same problem when using Teensy 4.1 and HD74LS241P IC. I have tried using the Dynamixel2Arduino library from the following repositories:
Dynamixel2Arduino

For the circuit, I used the same schematic as @nyomantayo have. I have also read some of your posts related to this issue on the PJRC Forum, including the following threads:
Teensy 4.0 and Dynamixel

Do you have a way to drive a Dynamixel AX-18A servo using the Dynamixel2Arduino library? Or maybe there is another library that is more suitable? If possible, could you share a proven code example that works?

Thank you :pray:

Hi, i have been try your code, but no responses from dynamixel even thought i use the general baud rate, like 1000000, 115200, 9600, etc. For additional information:

  • I use serial 5, and even try use teensy 4.0, but have some result
  • I use ID 19 at Dynamixel, setting with Roboplus
  • The library like : Dynamixel2Arduino, DynamixelSerial make same result. But when is try in STM32, the servo can move

I hope you have some advice from this situation. Thank you

As mentioned on PJRC forums your circuit could damage the teensy as it is not 5v tolerant, let alone 8v

Previously, I tried connecting the TX2 and RX2 pins of the Teensy 4.1 directly to IC 74LS241, but this caused my Teensy to break.

Now, I have added a logic level shifter, where TX2 and RX2 from Teensy 4.1 are first connected to the logic level shifter, then the output is connected to IC 74LS241 before finally going to the Dynamixel AX-18A servo.

However, despite using the logic level shifter, the AX-18A servo still does not move. Here the another schematic i have been used now.

I am assuming you are the same person as posting on PJRC forum this morning in the threads like: How to Move Dynamixel ax18 with Teensy 4.x | Teensy Forum

Note On your last drawing, not sure if the 74LS241 Is Bidirectional? It looks like an
output buffer, but I am not an EE but retired software developer.

My last board, that I had DXL support on was for a MicroMod Teensy:

Used two different level shifters, one for RX other for TX, differences where on the
OE pins, one was OE and other was ~OE. So in this case I did not use the
Half duplex support of the Teensy 4.x...

On some other boards or for testing, I have used the Teensy UART in half duplex mode, and simply connected up the TX pin of a UART directly to the DXL buss, no chip involved. I might have put in an inline resistor to hopefully limit current if something goes wrong. There were some different forum threads up on PJRC where some members insisted you did not need anything, including:
Rs485 halfduplex for Dynamixel with Teensy 3.1 without external Buffer chip | Teensy Forum

Other times I might have added in a bidirectional level shifter on that pin, just
to be safe.

Thank you for all the feedback you have provided.

Beforehand, I would like to explain that I am not the same person as @nyomantayo, but I know him as a university friend. Currently, we are both learning how to drive Dynamixel AX-18 servos using Teensy 4.x.

Regarding the question of whether IC 74LS241 is bidirectional, I cannot give a definitive answer as I am still in the learning stage and do not fully understand this concept. However, I have collected some sources that use this IC to drive Dynamixel, such as:

Based on several sources I read, IC 74LS241 is an octal (8-bit) buffer/driver with three output states (tri-state) used to improve the performance of digital systems in data communication.
Function of IC 74LS241

  1. Signal Amplifier (Buffering)
    o Serves as a buffer to amplify signals from the microcontroller to other devices, such as actuators or TTL-based serial communication.
  2. Data Line Separator (Bus Driver)
    o Controls the data flow between the microcontroller and external devices by differentiating between transmitter and receiver signals, especially in half-duplex communication.
  3. Improved Microcontroller Port Efficiency
    o In the research I found, this IC was used to control multiple Dynamixel AX-12A servos on a biped robot using only three data ports (RX, TX, and Data). This saves the number of ports used, so that the other ports can be utilized for additional sensors or actuators.
  4. Improves Communication Stability
    o Enables more stable communication in systems that use many TTL-based actuators by preventing signal interference between devices.
  5. TTL Voltage Support (5V)
    o Compatible with TTL (Transistor-Transistor Logic) logic, which is commonly used in microcontroller-based systems such as Arduino and Teensy.

Is IC 74LS241 Bidirectional?
Based on my understanding, IC 74LS241 is not a bidirectional IC. It is an octal buffer/line driver with a tri-state output, but it only supports unidirectional data flow. This means that it can only be used to send or receive data in one specific direction, but it cannot dynamically change direction like a bidirectional bus transceiver.

Also related to your explanation on the forum:
How to Move Dynamixel ax18 with Teensy 4.x
I am starting to understand and want to ask something more, maybe for the continuation of my question I will ask on the forum. Please help