Project help!arduino brushless motor control

I've been working on the code to control two hoverboard motors using RioRand ZS-X11H and HotRC DS-600 transmitter and receiver. I have both motors working with the RC as intended except when the motors run in reverse the motor in reverse status comes to a hard stop while the other motor coasts to a stop. I've been busting my brain to figure out how to have both motors coast to a stop instead of hard stops. I'm still learning Arduino and I've had many bumps and bruises to get to this point. Any help or guidance will be appreciated.

#define SERIAL_PORT_SPEED 115200
#define RC_NUM_CHANNELS 2

#define RC_CH1_INPUT 2 // RC Channel 1 Input Pin
#define RC_CH2_INPUT 3 // RC Channel 2 Input Pin

uint16_t RC_VALUES[RC_NUM_CHANNELS];
uint32_t RC_START[RC_NUM_CHANNELS];
volatile uint16_t RC_SHARED[RC_NUM_CHANNELS];

uint16_t RC_LOW[RC_NUM_CHANNELS] = { 1013, 1013 };
uint16_t RC_MID[RC_NUM_CHANNELS] = { 1508, 1508 };
uint16_t RC_HIGH[RC_NUM_CHANNELS] = { 2005, 2005 };

uint16_t RC_CHANNEL_MODE[RC_NUM_CHANNELS] = { 0, 0 };
uint16_t RC_INVERT[RC_NUM_CHANNELS] = { 1, 0 }; // Uncommented this line to fix the error

float RC_TRANSLATED_VALUES[RC_NUM_CHANNELS];
float RC_TRANSLATED_LOW[RC_NUM_CHANNELS] = { -50, -50 };
float RC_TRANSLATED_MID[RC_NUM_CHANNELS] = { 0, 0 };
float RC_TRANSLATED_HIGH[RC_NUM_CHANNELS] = { 50, 50 };
uint16_t RC_DZPERCENT[RC_NUM_CHANNELS] = { 5, 5 };

// Motor Configuration
const int PIN_PWM_MOTOR1 = 9;    // Left motor speed control (PWM) pin
const int PIN_DIR_MOTOR1 = 4; // Left motor direction control pin

const int PIN_PWM_MOTOR2 = 10;     // Right motor speed control (PWM) pin
const int PIN_DIR_MOTOR2 = 7; // Right motor direction control pin
const int DELAY = 20;

int _pwmCtrl_Motor1 = 0; // PWM control signal for Motor 1 (0-255)
bool _dir_Motor1 = 1; // Direction of Motor 1

// Variables for Motor 2
int _pwmCtrl_Motor2 = 0; // PWM control signal for Motor 2 (0-255)
bool _dir_Motor2 = 0; // Direction of Motor 2

void setup() {
  Serial.begin(SERIAL_PORT_SPEED);
  pinMode(RC_CH1_INPUT, INPUT);
  pinMode(RC_CH2_INPUT, INPUT);
  attachInterrupt(digitalPinToInterrupt(RC_CH1_INPUT), READ_RC1, CHANGE);
  attachInterrupt(digitalPinToInterrupt(RC_CH2_INPUT), READ_RC2, CHANGE);

  pinMode(PIN_PWM_MOTOR1, OUTPUT);
  pinMode(PIN_DIR_MOTOR1, OUTPUT);

  pinMode(PIN_PWM_MOTOR2, OUTPUT);
  pinMode(PIN_DIR_MOTOR2, OUTPUT);
}

unsigned long current = 0;
unsigned long prev = 0;
const unsigned long interval = 100000UL;

void loop() {
  rc_read_values();
  rc_invert_values();
  rc_deadzone_adjust();
  rc_translate_values();

  // Motor Control
  int motorSpeed = map(RC_VALUES[0], RC_LOW[0], RC_HIGH[0], -127, 127);
  motorSpeed = constrain(motorSpeed, -150, 150);

  int turnValue = map(RC_VALUES[1], RC_LOW[1], RC_HIGH[1], -127, 127);
  turnValue = constrain(turnValue, -150, 150);

  int leftMotorSpeed = motorSpeed + turnValue;
  int rightMotorSpeed = motorSpeed - turnValue;

  // Determine motor directions
  bool leftMotorForward = leftMotorSpeed >= 0;
  bool rightMotorForward = rightMotorSpeed >= 0;

  // Set motor directions
  digitalWrite(PIN_DIR_MOTOR1, leftMotorForward ? HIGH : LOW);
  digitalWrite(PIN_DIR_MOTOR2, rightMotorForward ? LOW : HIGH);

  // Set motor speeds
  analogWrite(PIN_PWM_MOTOR1, abs(leftMotorSpeed));
  analogWrite(PIN_PWM_MOTOR2, abs(rightMotorSpeed));

  // Output RC Controller Data
  current = micros();
  if (current - prev >= interval) {
    prev += interval;

    for (int i = 0; i < RC_NUM_CHANNELS; i++) {
      Serial.print("CH");
      Serial.print(i);
      Serial.print(":");
      Serial.print(RC_TRANSLATED_VALUES[i]);
      Serial.print(",");
    }

    Serial.print("LOW:");
    Serial.print(RC_TRANSLATED_LOW[0]);
    Serial.print(",");
    Serial.print("HIGH:");
    Serial.print(RC_TRANSLATED_HIGH[0]);

    Serial.println("");
  }

  // Perform movements with delays
  if (RC_VALUES[0] > RC_HIGH[0]) {
    // Move forward
    moveForward();
    delay(1000); // Adjust the delay time as needed
  } else if (RC_VALUES[0] < RC_LOW[0]) {
    // Move backward
    moveBackward();
    delay(1000); // Adjust the delay time as needed
  } else if (RC_VALUES[1] > RC_HIGH[1]) {
    // Turn left
    turnLeft();
    delay(1000); // Adjust the delay time as needed
  } else if (RC_VALUES[1] < RC_LOW[1]) {
    // Turn right
    turnRight();
    delay(1000); // Adjust the delay time as needed
  }
}

// Add your movement functions here
void moveForward() {
  // Set motor speeds for forward movement
_pwmCtrl_Motor1 = 50; // Max speed
_pwmCtrl_Motor2 = 50; // Max speed
}

void moveBackward() {
  // Set motor speeds for backward movement
  _pwmCtrl_Motor1 = -25; // Max speed in reverse
  _pwmCtrl_Motor2 = -25; // Max speed in reverse
}

void turnLeft() {
  // Set motor speeds for left turn
_pwmCtrl_Motor1 = 10; // Max speed for left turn
_pwmCtrl_Motor2 = -10; // Max speed for left turn
}

void turnRight() {
  // Set motor speeds for right turn
 _pwmCtrl_Motor1 = -15; // Max speed for right turn
 _pwmCtrl_Motor2 = 15; // Max speed for right turn
}

void READ_RC1() {
  Read_Input(0, RC_CH1_INPUT);
}

void READ_RC2() {
  Read_Input(1, RC_CH2_INPUT);
}

void Read_Input(uint8_t channel, uint8_t input_pin) {
  if (digitalRead(input_pin) == HIGH) {
    RC_START[channel] = micros();
  } else {
    uint16_t rc_compare = (uint16_t)(micros() - RC_START[channel]);
    RC_SHARED[channel] = rc_compare;
  }
}

void rc_read_values() {
  noInterrupts();
  memcpy(RC_VALUES, (const void *)RC_SHARED, sizeof(RC_SHARED));
  interrupts();
}

void rc_invert_values() {
  for (int i = 0; i < RC_NUM_CHANNELS; i++) {
    if (RC_INVERT[i] == 1) {
      RC_VALUES[i] = (RC_HIGH[i] + RC_LOW[i]) - RC_VALUES[i];
    }
    if (RC_VALUES[i] > RC_HIGH[i]) {
      RC_VALUES[i] = RC_HIGH[i];
    }
    if (RC_VALUES[i] < RC_LOW[i]) {
      RC_VALUES[i] = RC_LOW[i];
    }
  }
}

void rc_translate_values() {
  for (int i = 0; i < RC_NUM_CHANNELS; i++) {
    RC_TRANSLATED_VALUES[i] = translateValueIntoNewRange((float)RC_VALUES[i], (float)RC_HIGH[i], (float)RC_LOW[i], RC_TRANSLATED_LOW[i], RC_TRANSLATED_HIGH[i]);
  }
}

void rc_deadzone_adjust() {
  for (int i = 0; i < RC_NUM_CHANNELS; i++) {
    float newval = 0;
    if (RC_CHANNEL_MODE[i] == 0) {
      newval = translateValueIntoNewRange((float)RC_VALUES[i], (float)RC_HIGH[i], (float)RC_LOW[i], 50.0, -50.0);
      if (abs(newval) < RC_DZPERCENT[i]) {
        RC_VALUES[i] = RC_MID[i];
      }
    } else if (RC_CHANNEL_MODE[i] == 1) {
      newval = translateValueIntoNewRange((float)RC_VALUES[i], (float)RC_HIGH[i], (float)RC_LOW[i], 50.0, 0.0);
      if (abs(newval) < RC_DZPERCENT[i]) {
        RC_VALUES[i] = RC_LOW[i];
      }
    }
  }
}

float translateValueIntoNewRange(float currentvalue, float currentmax, float currentmin, float newmax, float newmin) {
  return (((currentvalue - currentmin) * (newmax - newmin)) / (currentmax - currentmin)) + newmin;
}

Delays and serial prints (left in a working loop) not a good practice.

Thanks for the feedback. Ok, I can remove the serial prints. I added the delays thinking it would fix the motors stopping abruptly, but that didn't do it.

Please supply a link to the data sheet of your motor driver. I suspect that the stop behavior can be configured for each driver.

1 Like

Post an annotated schematic of how you have it wired, include all connections, power, ground and power sources. Links to each of the hardware items giving technical data would help a lot.

Below is the data for the ZS-X11H. I can only find the detail illustration and not the actual data sheets.

That's why I never buy components without a data sheet and Arduino libraries and example code.

I used that board because it was used by others on the web with success. Below is my connection to the Arduino. The hall sensors of the motors are connected to the ZS-X11H. Thanks for the help!

I've been troubleshooting the code, and I am able to make the right motor coast to neutral in all directions, but the left motor still has a hard stop when the RC controller is Negative. I found that int (leftMotorSpeed= motorSpeed + turnValue) is causing the braking action on the left motor. When I change the plus sign to a minus, both motors rotate forward/back and coast to a stop, but then the direction is gone and both channels of the remote work as forward/backward. The reverse direction is gone for turning left or right. At some point, I may figure it out, but any help will be appreciated. Below is the change in code to make the right wheel coast but not the left.

#define SERIAL_PORT_SPEED 115200
#define RC_NUM_CHANNELS 2

#define RC_CH1_INPUT 2 // RC Channel 1 Input Pin
#define RC_CH2_INPUT 3 // RC Channel 2 Input Pin

uint16_t RC_VALUES[RC_NUM_CHANNELS];
uint32_t RC_START[RC_NUM_CHANNELS];
volatile uint16_t RC_SHARED[RC_NUM_CHANNELS];

uint16_t RC_LOW[RC_NUM_CHANNELS] = { 1013, 1013 };
uint16_t RC_MID[RC_NUM_CHANNELS] = { 1508, 1508 };
uint16_t RC_HIGH[RC_NUM_CHANNELS] = { 2005, 2005 };

uint16_t RC_CHANNEL_MODE[RC_NUM_CHANNELS] = { 0, 0 };
int RC_INVERT[RC_NUM_CHANNELS] = { 1, 0 };

float RC_TRANSLATED_VALUES[RC_NUM_CHANNELS];
float RC_TRANSLATED_LOW[RC_NUM_CHANNELS] = { -50, -50 };
float RC_TRANSLATED_MID[RC_NUM_CHANNELS] = { 0, 0 };
float RC_TRANSLATED_HIGH[RC_NUM_CHANNELS] = { 50, 50 };
uint16_t RC_DZPERCENT[RC_NUM_CHANNELS] = { 5, 5 };

// Motor Configuration
const int PIN_PWM_MOTOR1 = 9;    // Left motor speed control (PWM) pin
const int PIN_DIR_MOTOR1 = 4; // Left motor direction control pin

const int PIN_PWM_MOTOR2 = 10;     // Right motor speed control (PWM) pin
const int PIN_DIR_MOTOR2 = 7; // Right motor direction control pin

int _pwmCtrl_Motor1 = 0; // PWM control signal for Motor 1 (0-255)
int _dir_Motor1 = 0; // Direction of Motor 1

int _pwmCtrl_Motor2 = 0; // PWM control signal for Motor 2 (0-255)
int _dir_Motor2 = 0; // Direction of Motor 2

void setup() {
  Serial.begin(SERIAL_PORT_SPEED);
  pinMode(RC_CH1_INPUT, INPUT);
  pinMode(RC_CH2_INPUT, INPUT);
  attachInterrupt(digitalPinToInterrupt(RC_CH1_INPUT), READ_RC1, CHANGE);
  attachInterrupt(digitalPinToInterrupt(RC_CH2_INPUT), READ_RC2, CHANGE);

  pinMode(PIN_PWM_MOTOR1, OUTPUT);
  pinMode(PIN_DIR_MOTOR1, OUTPUT);

  pinMode(PIN_PWM_MOTOR2, OUTPUT);
  pinMode(PIN_DIR_MOTOR2, OUTPUT);
}

void loop() {
  rc_read_values();
  rc_invert_values();
  rc_deadzone_adjust();
  rc_translate_values();

  // Motor Control
  int motorSpeed = map(RC_VALUES[0], RC_LOW[0], RC_HIGH[0], -127, 127);
  motorSpeed = constrain(motorSpeed, -150, 150);

  int turnValue = map(RC_VALUES[1], RC_LOW[1], RC_HIGH[1], -127, 127);
  turnValue = constrain(turnValue, -150, 150);

  int leftMotorSpeed = motorSpeed + turnValue;
  int rightMotorSpeed = motorSpeed - turnValue;

  // Determine motor directions
  int leftMotorForward = leftMotorSpeed >= 0;
  int rightMotorForward = rightMotorSpeed >= 0;

  // If motor direction changed, gradually decrease PWM to 0 for coasting
  if (leftMotorForward != _dir_Motor1) {
    for (int pwm = abs(_pwmCtrl_Motor1); pwm >= 0; pwm--) {
      analogWrite(PIN_PWM_MOTOR1, pwm);
      delay(50); // Small delay to gradually reduce PWM
    }
  }

  if (rightMotorForward != _dir_Motor2) {
    for (int pwm = abs(_pwmCtrl_Motor2); pwm >= 0; pwm--) {
      analogWrite(PIN_PWM_MOTOR2, pwm);
      delay(50); // Small delay to gradually reduce PWM
    }
  }
 // Update motor direction variables
  _dir_Motor1 = leftMotorForward;
  _dir_Motor2 = rightMotorForward;
  // Set motor directions
  digitalWrite(PIN_DIR_MOTOR1, leftMotorForward ? HIGH : LOW);
  digitalWrite(PIN_DIR_MOTOR2, rightMotorForward ? LOW : HIGH);

  // Set motor speeds
  analogWrite(PIN_PWM_MOTOR1, abs(leftMotorSpeed));
  analogWrite(PIN_PWM_MOTOR2, abs(rightMotorSpeed));

 
}

void READ_RC1() {
  Read_Input(0, RC_CH1_INPUT);
}

void READ_RC2() {
  Read_Input(1, RC_CH2_INPUT);
}

void Read_Input(uint8_t channel, uint8_t input_pin) {
  if (digitalRead(input_pin) == HIGH) {
    RC_START[channel] = micros();
  } else {
    uint16_t rc_compare = (uint16_t)(micros() - RC_START[channel]);
    RC_SHARED[channel] = rc_compare;
  }
}

void rc_read_values() {
  noInterrupts();
  memcpy(RC_VALUES, (const void *)RC_SHARED, sizeof(RC_SHARED));
  interrupts();
}

void rc_invert_values() {
  for (int i = 0; i < RC_NUM_CHANNELS; i++) {
    if (RC_INVERT[i] == 1) {
      RC_VALUES[i] = (RC_HIGH[i] + RC_LOW[i]) - RC_VALUES[i];
    }
    if (RC_VALUES[i] > RC_HIGH[i]) {
      RC_VALUES[i] = RC_HIGH[i];
    }
    if (RC_VALUES[i] < RC_LOW[i]) {
      RC_VALUES[i] = RC_LOW[i];
    }
  }
}

void rc_translate_values() {
  for (int i = 0; i < RC_NUM_CHANNELS; i++) {
    RC_TRANSLATED_VALUES[i] = translateValueIntoNewRange((float)RC_VALUES[i], (float)RC_HIGH[i], (float)RC_LOW[i], RC_TRANSLATED_LOW[i], RC_TRANSLATED_HIGH[i]);
  }
}

void rc_deadzone_adjust() {
  for (int i = 0; i < RC_NUM_CHANNELS; i++) {
    float newval = 0;
    if (RC_CHANNEL_MODE[i] == 0) {
      newval = translateValueIntoNewRange((float)RC_VALUES[i], (float)RC_HIGH[i], (float)RC_LOW[i], 50.0, -50.0);
      if (abs(newval) < RC_DZPERCENT[i]) {
        RC_VALUES[i] = RC_MID[i];
      }
    } else if (RC_CHANNEL_MODE[i] == 1) {
      newval = translateValueIntoNewRange((float)RC_VALUES[i], (float)RC_HIGH[i], (float)RC_LOW[i], 50.0, 0.0);
      if (abs(newval) < RC_DZPERCENT[i]) {
        RC_VALUES[i] = RC_LOW[i];
      }
    }
  }
}

float translateValueIntoNewRange(float currentvalue, float currentmax, float currentmin, float newmax, float newmin) {
  return (((currentvalue - currentmin) * (newmax - newmin)) / (currentmax - currentmin)) + newmin;
}

Sry, looking through the tiny window.

Have you tried physically swapping elements of your hardware to se if the problem is definitely software and not some hardware thing that makes one moto differ to the other?

a7

Yes, if I flop the +/- on the below code, then the right motor works great, but then the left motor has the problem.

int leftMotorSpeed = motorSpeed - turnValue;
int rightMotorSpeed = motorSpeed + turnValue;

You stop the left motor first, then the right motor. I.e. the left motor stands still while the right motor decelerates. This pause may look like a hard stop?

Thanks for the reply. At really slow speeds that isn't a problem, but at moderate or higher speeds the left motor wants to jump off the workbench when RC goes back to neutral. It is a really hard stop.

I would start by adding Serial.print() statements to confirm the plausibility of the values variables you are passing digitalWrite() and analogWrite() .

What are the typical raw values you get from the radio?

  rc_read_values();

// here I mean. What are the raw values ranging like?

  rc_invert_values();
  rc_deadzone_adjust();
  rc_translate_values();

If you can say what that is, I can run your code using alternate means of getting the rad data.

TIA no promises.

a7

Here are my raw numbers. Thanks for your help
image

OK, thanks.

I thought I'd get further with the time I had just now. I did place your code into the simulator and dropped the reading of the radio in favor of some slide faders.

I added print messages, and wrote modified analogWrite() and digitalWrite() functions to facilitate keeping close track of the outputs.

Now... what sequence of moves on the stick (slide faders) can we use to demonstrate the issue(s)?

The sim can be paused and the slide faders moved during the pause, then the code can be un-paused and the reaction observed.

Or, a sequence of stick movements could be stored and marched out on a schedule.

BTW I placed a large delay in the loop() function. There is no reason to run it as very fast as you were doing - a 50 ms delay would probably be fine. My longer delay just lets the printing not overwhelm.


Wokwi_badge Your code mostly.


The only thing I see right now is that the gradual changes are handled sequentially. So the left side will ramp down, then the right side, if both were going to need rampation. Seems like in the final version, you'll want those to happen simultaneously.

HTH

a7

OK, not my eyes but someone trying to get me to leave this for now sees the left fade happen in one step:

fwd left @ 116  fwd right @ 118
              left decrease gradually
left grade pin 9 value 0

and she wonders about

    for (int pwm = abs(_pwmCtrl_Motor2); pwm >= 0; pwm--) {

as pwmCtrlMotor2 seems to be zero here always?

Sry, now I gotta jet.

a7

Thanks for all your help. LOL, I've been busting my brain for the last few weeks trying to figure out this final piece. I'm an Ardunino Newbie, so I'm learning every day. The left wheel only comes to a complete stop (when moving backward) when the RC Translated values are negative. When the RC is in a positive state, the left wheel (moving forward) coasts to a stop. Right wheel is coasting in all directions.

Please list a sequence of stick or r/c values along with what the code is doing (and what it is suppoed to be doing).

I would arrange to have that sequence spoon-fed to the code. One new pair of r/c channel readings per loop.

I just can't tell so far from your description or your code what inputs to use to illustrate the problem.

Surely you have a "go to" set of test throws.

TIA, again no promises.

Did you consider the matter raised in #17? I added something plausible and only then did I see the gradual PWM adjustment happening.

a7

Thanks for the advice. I tried your suggestions, but I wasn't able to get that problem resolved. I decided to scratch that sketch and start over. I have a new simplified sketch that operates as it should. Again, I'm a novice at Arduino so any advice to improve the new sketch will be appreciated.

//FULLY WORKING!!!!!

// Motor 1
const int motor1PWM = 9;
const int motor1Dir = 4;

// Motor 2
const int motor2PWM = 10;
const int motor2Dir = 7;

// RC Controller
const int rcChannel1 = 2;
const int rcChannel2 = 3;

volatile unsigned long channel1StartTime = 0;
volatile unsigned long channel2StartTime = 0;

volatile int channel1Value = 1516; // Initial midpoint value
volatile int channel2Value = 1516; // Initial midpoint value

void READ_RC1() {
  if (digitalRead(rcChannel1) == HIGH) {
    channel1StartTime = micros();
  } else {
    channel1Value = micros() - channel1StartTime;
  }
}

void READ_RC2() {
  if (digitalRead(rcChannel2) == HIGH) {
    channel2StartTime = micros();
  } else {
    channel2Value = micros() - channel2StartTime;
  }
}

void setup() {
  pinMode(motor1PWM, OUTPUT);
  pinMode(motor1Dir, OUTPUT);
  pinMode(motor2PWM, OUTPUT);
  pinMode(motor2Dir, OUTPUT);
  pinMode(rcChannel1, INPUT);
  pinMode(rcChannel2, INPUT);
  attachInterrupt(digitalPinToInterrupt(rcChannel1), READ_RC1, CHANGE);
  attachInterrupt(digitalPinToInterrupt(rcChannel2), READ_RC2, CHANGE);

  Serial.begin(9600);
}

void loop() {
  // Map RC channel values to motor speeds
  int motorSpeed;
  if (channel2Value >= 1600 && channel2Value <= 2000) {
    motorSpeed = map(channel2Value, 1600, 2000, 0, 100);
} else if (channel2Value >= 1000 && channel2Value <= 1400) {
    motorSpeed = map(channel2Value, 1000, 1400, 50, 50);
} 

  // Forward or backward
  if (channel2Value > 1600 || channel2Value < 1400) {
    int motorDirection = (channel2Value > 1600) ? HIGH : LOW;
  //delay(100);
    digitalWrite(motor1Dir, motorDirection);
    analogWrite(motor1PWM, abs(motorSpeed));

    digitalWrite(motor2Dir, motorDirection);
    analogWrite(motor2PWM, abs(motorSpeed));
  }
  // Turn left
  else if (channel1Value < 1400) {
    digitalWrite(motor1Dir, HIGH);
    analogWrite(motor1PWM, abs(motorSpeed));
    digitalWrite(motor2Dir, HIGH);
    analogWrite(motor2PWM, abs(motorSpeed) / 2);
    //delay(100);
  }
  // Turn right
  else if (channel1Value > 1600) {
    digitalWrite(motor1Dir, HIGH);
    analogWrite(motor1PWM, abs(motorSpeed) / 2);
    digitalWrite(motor2Dir, HIGH);
    analogWrite(motor2PWM, abs(motorSpeed));
    //delay(100);
  }
  // Stop
  else {
    analogWrite(motor1PWM, 0);
    analogWrite(motor2PWM, 0);
  }

  // Send data over serial
  Serial.print("Channel 1: ");
  Serial.print(channel1Value);
  Serial.print("\tChannel 2: ");
  Serial.print(channel2Value);
  Serial.print("\tMotor Speed: ");
  Serial.println(motorSpeed);
}