1. Do not share user accounts! Any account that is shared by another person will be blocked and closed. This means: we will close not only the account that is shared, but also the main account of the user who uses another person's account. We have the ability to detect account sharing, so please do not try to cheat the system. This action will take place on 04/18/2023. Read all forum rules.
    Dismiss Notice
  2. For downloading SimTools plugins you need a Download Package. Get it with virtual coins that you receive for forum activity or Buy Download Package - We have a zero Spam tolerance so read our forum rules first.

    Buy Now a Download Plan!
  3. Do not try to cheat our system and do not post an unnecessary amount of useless posts only to earn credits here. We have a zero spam tolerance policy and this will cause a ban of your user account. Otherwise we wish you a pleasant stay here! Read the forum rules
  4. We have a few rules which you need to read and accept before posting anything here! Following these rules will keep the forum clean and your stay pleasant. Do not follow these rules can lead to permanent exclusion from this website: Read the forum rules.
    Are you a company? Read our company rules

Question Trying to run AC Servo with Arduino (code help)

Discussion in 'New users start here - FAQ' started by arda057, Apr 20, 2025.

  1. arda057

    arda057 New Member

    Joined:
    Apr 18, 2025
    Messages:
    2
    Balance:
    12Coins
    Ratings:
    +0 / 0 / -0
    My Motion Simulator:
    2DOF, AC motor, Arduino
    Hi all,

    In this forum there is a great tutorial for 2 RC Servo to get started. As a newbie i cannot share the link but i'm copy-paste the tutorial content.

    I set the Surge 50% Sway 50% for both axis in simtools with the above RCModelSimToolsMode.ino and everything works fine with the LFS.

    Code for RCModelSimToolsMode.ino
    Code:
    //********************************************************************************************
    // RC Model Servo
    // Original code By EAOROBBIE (Robert Lindsay)
    // Completely mangled by aarondc
    // For free use for Sim Tool Motion Software
    //********************************************************************************************
    #include <Servo.h>
    //#define DEBUG 1                                    // comment out this line to remove debuggin Serial.print lines
    const int kActuatorCount = 2;                       // how many Actuators we are handling
    
    // the letters ("names") sent from Sim Tools to identify each actuator
    // NB: the order of the letters here determines the order of the remaining constants kPins and kActuatorScale
    const char kActuatorName[kActuatorCount] = { 'R', 'L' };
    const int kPins[kActuatorCount] = {4, 5};                       // pins to which the Actuators are attached
    const int kActuatorScale[kActuatorCount][2] = { { 0, 179 } ,    // Right Actuator scaling
                                                    { 179, 0 }      // Left side Actuator scaling
                                                   };    
    const char kEOL = '~';                              // End of Line - the delimiter for our acutator values
    const int kMaxCharCount = 3;                        // some insurance...
    Servo actuatorSet[kActuatorCount];                  // our array of Actuators
    int actuatorPosition[kActuatorCount] = {90, 90};    // current Actuator positions, initialised to 90
    int currentActuator;                                // keep track of the current Actuator being read in from serial port
    int valueCharCount = 0;                             // how many value characters have we read (must be less than kMaxCharCount!!
    
    // set up some states for our state machine
    // psReadActuator = next character from serial port tells us the Actuator
    // psReadValue = next 3 characters from serial port tells us the value
    enum TPortState { psReadActuator, psReadValue };  
    TPortState currentState = psReadActuator;
    
    void setup()
    {
        // attach the Actuators to the pins
        for (int i = 0; i < kActuatorCount; i++)
            actuatorSet[i].attach(kPins[i]);
       
        // initialise actuator position
        for (int i = 0; i < kActuatorCount; i++)
            updateActuator(i);
       
        Serial.begin(9600); // opens serial port at a baud rate of 9600
    }
     
    void loop()
    {
    
    }
    
    // this code only runs when we have serial data available. ie (Serial.available() > 0).
    void serialEvent() {
        char tmpChar;
        int tmpValue;
    
        while (Serial.available()) {
            // if we're waiting for a Actuator name, grab it here
            if (currentState == psReadActuator) {
                tmpChar = Serial.read();
                // look for our actuator in the array of actuator names we set up
    #ifdef DEBUG          
    Serial.print("read in ");          
    Serial.println(tmpChar);          
    #endif
                for (int i = 0; i < kActuatorCount; i++) {
                    if (tmpChar == kActuatorName[i]) {
    #ifdef DEBUG          
    Serial.print("which is actuator ");          
    Serial.println(i);          
    #endif
                        currentActuator = i;                        // remember which actuator we found
                        currentState = psReadValue;                 // start looking for the Actuator position
                        actuatorPosition[currentActuator] = 0;      // initialise the new position
                        valueCharCount = 0;                         // initialise number of value chars read in
                        break;
                    }
                }
            }
           
            // if we're ready to read in the current Actuator's position data
            if (currentState == psReadValue) {
                while ((valueCharCount < kMaxCharCount) && Serial.available()) {
                    tmpValue = Serial.read();
                    if (tmpValue != kEOL) {
                        tmpValue = tmpValue - 48;
                        if ((tmpValue < 0) || (tmpValue > 9)) tmpValue = 0;
                        actuatorPosition[currentActuator] = actuatorPosition[currentActuator] * 10 + tmpValue;
                        valueCharCount++;
                    }
                    else break;
                }
               
                // if we've read the value delimiter, update the Actuator and start looking for the next Actuator name
                if (tmpValue == kEOL || valueCharCount == kMaxCharCount) {
    #ifdef DEBUG          
    Serial.print("read in ");          
    Serial.println(actuatorPosition[currentActuator]);          
    #endif
                    // scale the new position so the value is between 0 and 179
                    actuatorPosition[currentActuator] = map(actuatorPosition[currentActuator], 0, 255, kActuatorScale[currentActuator][0], kActuatorScale[currentActuator][1]);
    #ifdef DEBUG          
    Serial.print("scaled to ");          
    Serial.println(actuatorPosition[currentActuator]);          
    #endif
                    updateActuator(currentActuator);
                    currentState = psReadActuator;
                }
            }
        }
    }
    
    
    // write the current Actuator position to the passed in Actuator
    void updateActuator(int thisActuator) {
        actuatorSet[thisActuator].write(actuatorPosition[thisActuator]);
    }
    

    I want to transition my motion rig from small 9 V RC servos (driven via Servo.h) to full‑blown T3D‑L20A AC servos on an Arduino Uno, under SimTools (LFS).
    I think the only way to run AC from Arduino is using 5V PULS/SIGN pins.
    I’m looking for advice on exactly what changes I need to make—in both driver settings and Arduino code—to get smooth, proportional motion and true return‑to‑center behavior when the user lets go of the throttle or steering.

    Specs: 1KW AC Servo 80AST-A1C04025
    Driver: T3D-L20A-RABF (17 bit Absolute Encoder) (i cannot share the manual link)

    Current Setup
    1. SimTools

      Same as RC Servos.
      Serial @ 9600 baud sends ASCII frames:
      Rxxx~Lyyy~

    2. Ardunio - Driver Setup

      COM+ (pin 31) → 5 V (common anode)
      D2 → PULS+ (pin 25)
      GND → PULS– (pin 9)
      D3 → SIGN+ (pin 24)
      GND → SIGN– (pin 8)
      D4 → DI1/SON (pin 16, active‑LOW enable)
    3. Driver panel parameters

      P‑004 = 1 // Speed control mode
      P‑025 = 3 // Pulse‑speed command source
      P‑035 = 0 // Pulse + direction (default)
      P‑036 = 0 // Normal direction (LOW=CW)
      P‑048 = 0 // Speed command direction
    I'm not good at coding the Arduino either. So i try some code with the AI, like this:
    Code:
    /*
      Dual‐AC‐Servo Pulse‐Speed Control for T3D Series
    
      Prerequisites on each T3D driver (via front panel):
        • P‑004 = 1   // Speed control mode
        • P‑025 = 3   // Pulse‐speed as command source
        • E‑SET       // Save parameters to EEPROM
    */
    
    #include <Arduino.h>
    
    // — SimTools parsing (unchanged) —
    const int kActuatorCount = 2;
    const char kActuatorName[kActuatorCount] = { 'R', 'L' };
    const char kEOL = '~';
    
    volatile int actuatorRaw[kActuatorCount] = {127, 127};
    enum TPortState { psReadActuator, psReadValue };
    TPortState currentState = psReadActuator;
    int currentActuator = -1, valueCharCount = 0;
    
    void serialEvent() {
      while (Serial.available()) {
        char c = Serial.read();
        if (currentState == psReadActuator) {
          for (int i = 0; i < kActuatorCount; i++) {
            if (c == kActuatorName[i]) {
              currentActuator = i;
              actuatorRaw[i] = 0;
              valueCharCount = 0;
              currentState = psReadValue;
              break;
            }
          }
        }
        else if (currentState == psReadValue) {
          if (c != kEOL && valueCharCount < 3) {
            int d = constrain(c - '0', 0, 9);
            actuatorRaw[currentActuator] = actuatorRaw[currentActuator] * 10 + d;
            valueCharCount++;
          } else {
            actuatorRaw[currentActuator] = constrain(actuatorRaw[currentActuator], 0, 255);
            currentState = psReadActuator;
          }
        }
      }
    }
    
    // — Pin assignments for two drivers —
    const int pulsPin[kActuatorCount]   = { 2, 5 };  // PULS+
    const int dirPin[kActuatorCount]    = { 3, 6 };  // SIGN+
    const int enablePin[kActuatorCount] = { 4, 7 };  // DI1/SON (active LOW)
    
    // Timing & speed mapping
    const unsigned int PULSE_WIDTH_US = 500;    // minimum ON‐time of each pulse
    const float MAX_RPM = 3000.0;               // driver’s rated speed (r/min)
    const float MAX_FREQ = MAX_RPM * (2500.0*4) / 60.0;
    //   2500*4 pulses per rev for a 17‑bit encoder in QEI = 10000; 10000 * RPM/60 = pulses per second
    
    unsigned long lastStepTime[kActuatorCount] = {0,0};
    
    void setup() {
      Serial.begin(9600);
      for (int i = 0; i < kActuatorCount; i++) {
        pinMode(pulsPin[i],   OUTPUT);
        pinMode(dirPin[i],    OUTPUT);
        pinMode(enablePin[i], OUTPUT);
        digitalWrite(pulsPin[i],   HIGH);  // idle HIGH (no pulses)
        digitalWrite(dirPin[i],    LOW);   // default CW = LOW
        digitalWrite(enablePin[i], LOW);   // enable servo (active LOW)
      }
    }
    
    void loop() {
      unsigned long now = micros();
    
      for (int i = 0; i < kActuatorCount; i++) {
        int raw = actuatorRaw[i];
        int delta = raw - 127;            // center at 127
        if (abs(delta) < 5) continue;     // dead‑band ±4 units
    
        // direction pin: LOW = CW, HIGH = CCW (P‑048 = 0 default)
        bool cw = (delta > 0);
        digitalWrite(dirPin[i], cw ? LOW : HIGH);
    
        // map |delta| (0…128) → 0…MAX_FREQ pulses/sec
        float freq = (abs(delta) / 128.0) * MAX_FREQ;
        if (freq < 1) continue;           // too slow to bother
    
        // compute interval between pulses
        unsigned long interval = (unsigned long)(1e6 / freq);
    
        // time to fire next pulse?
        if (now - lastStepTime[i] >= interval) {
          digitalWrite(pulsPin[i], LOW);
          delayMicroseconds(PULSE_WIDTH_US);
          digitalWrite(pulsPin[i], HIGH);
          lastStepTime[i] = now;
        }
      }
    }
    
    Problems

    - Spins at “idle”
    Even raw=127±1 still produces a small delta, so you get a faint pulse rate and drift.

    - Never returns to centre (i guess there is no concept as centre in AC Servos)

    - Too slow and do not respond the Sway or Surge (Responds to Axis1a, Axis2a test in simtools but again too slow)

    I also tried to mapping logic with position mode but no luck in there.

    Basically what i want is achieving same RC Servo code effect with my new AC Servos.

    My main question is: how or where can i learn the AC Servo coding, that's the main problem for me right now.

    Thanks in advance.
    Last edited: Apr 21, 2025
  2. Qui

    Qui New Member

    Joined:
    Sep 22, 2023
    Messages:
    16
    Balance:
    - 116Coins
    Ratings:
    +2 / 0 / -0
    My Motion Simulator:
    2DOF
    Which arduino circuit are you using, uno r3 can control 4 motors with 16bit