AK80-9 Motor Not Responding – Arduino Uno + CAN Bus Shield V2 – INT LED Blinks When Entering Motor Mode

Hi everyone,

I’m working on a project where I’m trying to control a Cubemars AK80-9 motor using an Arduino Uno and a SeeedStudio CAN Bus Shield V2 (the one that mounts directly on the Uno and uses the MCP2515).

I’m powering the motor with 24V, and I’ve added a 120-ohm termination resistor at the motor end of the CAN line. The CANH and CANL lines go directly from the shield to the motor.

I'm using the following code to control the motor:

// This skecth is for testing the cubemars Ak80-9 motor using arduino CAN shield
/// use serial monitor for input the commands to motor
/// o -- turns on the motor mode
/// f -- turns off the motor mode
/// u -- steps up the position increment according to fix offset, can be adjusted in loop
/// d -- steps down the position increment according to fix offset, can be adjusted in loop


#include <mcp_can.h>
#include <SPI.h>

// the cs pin of the version v1.4 is default to D10
// Set CS pin


#ifdef ARDUINO_SAMD_VARIANT_COMPLIANCE
#define SERIAL SerialUSB
#else
#define SERIAL Serial
#endif

// Value Limits--Standard Values, shouldn't be changed

#define P_MIN -12.5f
#define P_MAX 12.5f
#define V_MIN -65.0f
#define V_MAX 65.0f
#define KP_MIN 0.0f
#define KP_MAX 500.0f
#define KD_MIN 0.0f
#define KD_MAX 5.0f
#define T_MIN -18.0f
#define T_MAX 18.0f

// Set Values
unsigned int id = 0;
float p_in = 0.0f;
float v_in = 0.0f;
float kp_in = 20.0f;
float kd_in = 1.0f;
float t_in = 0.0f;

//measured values - responses from the motor
float p_out = 0.0f; // actual position
float v_out = 0.0f; // actual velocity
float t_out = 0.0f; // actual torque

const int SPI_CS_PIN = 9;// change it to 9

MCP_CAN CAN(SPI_CS_PIN); //Set CS pin

void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
delay(1000);
while (CAN_OK != CAN.begin(MCP_ANY, CAN_1000KBPS, MCP_16MHZ)) {
Serial.println("CAN BUS Shield init fail");
Serial.println("Init CAN BUS Shield again");
delay(1000);
}
Serial.println("CAN BUS Shield init ok!");
}

long previousMillis = 0;

boolean newData = false;


void loop() {

char rc;
float p_step = 0.1;
//delay(200);
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
Serial.println(rc);
if (rc == 's') {
Serial.println("True");
}


if (rc == 'u') {
p_in = p_in + p_step;

}

if (rc == 'd') {
p_in = p_in - p_step;
}

p_in = constrain(p_in, P_MIN, P_MAX);//

if (rc == 'o') {
EnterMotorMode();
}

if (rc == 'f') {
ExitMotorMode();
}
// send CAN
pack_cmd();
}
// receive CAN

if (CAN_MSGAVAIL == CAN.checkReceive()) // check if data coming
{
unpack_reply();
}

Serial.print(millis() - previousMillis);
previousMillis = millis();
Serial.print("ID ");
Serial.print(id);
Serial.print("position ");
Serial.print(p_in);
Serial.print(" ");

Serial.print("position output ");
Serial.print(p_out);
Serial.print(" ");
Serial.print("velocity output ");
Serial.print(v_out);
Serial.print(" ");
Serial.print("torque output ");
Serial.println(t_out);
}

void EnterMotorMode() {// standard values

byte buf[8];
buf[0] = 0xFF;
buf[1] = 0xFF;
buf[2] = 0xFF;
buf[3] = 0xFF;
buf[4] = 0xFF;
buf[5] = 0xFF;
buf[6] = 0xFF;
buf[7] = 0xFC;
CAN.sendMsgBuf(0x01, 0, 8, buf);
}

void ExitMotorMode() {

byte buf[8];
buf[0] = 0xFF;
buf[1] = 0xFF;
buf[2] = 0xFF;
buf[3] = 0xFF;
buf[4] = 0xFF;
buf[5] = 0xFF;
buf[6] = 0xFF;
buf[7] = 0xFD;
CAN.sendMsgBuf(0x01, 0, 8, buf);
}

void Zero() {

byte buf[8];
buf[0] = 0xFF;
buf[1] = 0xFF;
buf[2] = 0xFF;
buf[3] = 0xFF;
buf[4] = 0xFF;
buf[5] = 0xFF;
buf[6] = 0xFF;
buf[7] = 0xFE;
CAN.sendMsgBuf(0x01, 0, 8, buf);
}


void pack_cmd() {

byte buf[8];


// limit data to be within bounds ///

float p_des = constrain(p_in, P_MIN, P_MAX);
float v_des = constrain(v_in, V_MIN, V_MAX);
float kp = constrain(kp_in, KP_MIN, KP_MAX);
float kd = constrain(kd_in, KD_MIN, KD_MAX);
float t_ff = constrain(t_in, T_MIN, T_MAX);

unsigned int p_int = float_to_uint(p_des, P_MIN, P_MAX, 16);
unsigned int v_int = float_to_uint(v_des, V_MIN, V_MAX, 12);
unsigned int kp_int = float_to_uint(kp, KP_MIN, KP_MAX, 12);
unsigned int kd_int = float_to_uint(kd, KD_MIN, KD_MAX, 12);
unsigned int t_int = float_to_uint(t_ff, T_MIN, T_MAX, 12);

//pack ints into can buffer ///

buf[0] = p_int >> 8;
buf[1] = p_int & 0xFF;
buf[2] = v_int >> 4;
buf[3] = ((v_int & 0xF) << 4) | (kp_int >> 8);
buf[4] = kp_int & 0xFF;
buf[5] = kd_int >> 4;
buf[6] = ((kd_int & 0xF) << 4) | (t_int >> 8);
buf[7] = t_int & 0xFF;
CAN.sendMsgBuf(0x01, 0, 8, buf);// canid
}

void unpack_reply() {

unsigned long int canId;
byte len = 0;
byte buf[8];


CAN.readMsgBuf(&canId, &len, buf);
//unsigned long canId = CAN.getCanId();

///unpack ints from can buffer///

unsigned int id = buf[0];
unsigned int p_int = (buf[1] << 8) | buf[2];
unsigned int v_int = (buf[3] << 4) | (buf[4] >> 4);
unsigned int i_int = ((buf[4] & 0xF) << 8) | buf[5];

/// convert uints to floats ///

p_out = uint_to_float(p_int, P_MIN, P_MAX, 16);
v_out = uint_to_float(v_int, V_MIN, V_MAX, 12);
t_out = uint_to_float(i_int, -T_MIN, T_MAX, 12);
}

unsigned int float_to_uint(float x, float x_min, float x_max, float bits) {

//convert a float to an unsigned int, given range and number of bits ///

float span = x_max - x_min;
float offset = x_min;
unsigned int pgg = 0;

if (bits == 12) {
pgg = (unsigned int)((x - offset) * 4095.0 / span);
}
if (bits == 16) {
pgg = (unsigned int)((x - offset) * 65535.0 / span);
}
return pgg;
}


float uint_to_float(unsigned int x_int, float x_min, float x_max, int bits) {
/// converts unsined int to float, given range and numbers of bits///
float span = x_max - x_min;
float offset = x_min;
float pgg = 0;
if (bits == 12) {
pgg = ((float)x_int) * span / 4095.0 + offset;
}
if (bits == 16) {
pgg = ((float)x_int) * span / 65535.0 + offset;
}
return pgg;
}

When I type o in the Serial Monitor (to send the “enter motor mode” command), the INT LED on the CAN shield blinks, but the motor doesn’t do anything. No sound, no movement, nothing.

Other commands like u, d, and f also print as expected — so the Arduino and CAN library seem to be running.
Any help would really be appreciated.

Thanks!

welcome to the forums. Please edit your post and put code tags around your code. It helps people help you. It allows people to easily copy and paste it into their own IDE. It also helps to format the code properly (Ctrl-T in the IDE) before posting.

Hi, @imilyas
Welcome to the forum.

https://forum.arduino.cc/t/how-to-get-the-best-out-of-this-forum

Have you got some simple cade that just tries to control the motor via CanBus?
If you developed you code in stages, you should have some code that JUST has motor control code.

If you haven't, then can I recommend you leave your main code for now and write some simple test code for the motor.

Tom.... :smiley: :+1: :coffee: :australia:

What about the other end, it is a 60 ohm bus. If in doubt power down and measure the buss resistance, it should be about 60 ohms.

There is a 120 ohms resistor already connected in Can Bus Shield V2 (seeedstudio). Only one is required at the motor's side.

Hi Tom,

Thanks for the suggestion!

Actually, I took this code from the forum as my starting point to run the motor. I thought it was a simple example just to get the motor running, and I planned to move on to proper control later once I got it working.

So this is the first code I’m trying — I haven’t broken it down or built it in stages myself yet.

I’ll try your suggestion of writing a very minimal test sketch now, just to enter motor mode and confirm CAN communication.

Thanks again,

Thank you.
It's done. :slightly_smiling_face:

Halfway done :slight_smile: since the formatting is still terrible. If you format it properly, you will notice that every time you send something from your PC to the program, it will Always call pack_cmd() which sends data on the CAN bus. Depending on how you have Serial Monitor configured (line endings or not) you may be sending 's' + CR + LF which means you are sending 3 packets and that doesn't seem correct.

Building on @TomGeorge suggestion, I'd start smaller and build up. If you look at the documentation for the motor https://www.cubemars.com/images/file/20211201/1638329381542610.pdf
[section 5.3] is what you are dealing with for your code and you could just try to read the state of the motor.

Also, there are lots of read/send examples for the Seeed CAN shield, if you are using that library. I don't think you are. If you go into Library Manager and search for "Seeed CAN" you will see their library. I'd install that since you are using their shield.

FYI... a lot of that code looks like it came from the motor documentation PDF.

// This skecth is for testing the cubemars Ak80-9 motor using arduino CAN shield
/// use serial monitor for input the commands to motor
/// o -- turns on the motor mode
/// f -- turns off the motor mode
/// u -- steps up the position increment according to fix offset, can be adjusted in loop
/// d -- steps down the position increment according to fix offset, can be adjusted in loop


#include <mcp_can.h>
#include <SPI.h>

// the cs pin of the version v1.4 is default to D10
// Set CS pin


#ifdef ARDUINO_SAMD_VARIANT_COMPLIANCE
#define SERIAL SerialUSB
#else
#define SERIAL Serial
#endif

// Value Limits--Standard Values, shouldn't be changed

#define P_MIN -12.5f
#define P_MAX 12.5f
#define V_MIN -65.0f
#define V_MAX 65.0f
#define KP_MIN 0.0f
#define KP_MAX 500.0f
#define KD_MIN 0.0f
#define KD_MAX 5.0f
#define T_MIN -18.0f
#define T_MAX 18.0f

// Set Values
unsigned int id = 0;
float p_in = 0.0f;
float v_in = 0.0f;
float kp_in = 20.0f;
float kd_in = 1.0f;
float t_in = 0.0f;

//measured values - responses from the motor
float p_out = 0.0f; // actual position
float v_out = 0.0f; // actual velocity
float t_out = 0.0f; // actual torque

const int SPI_CS_PIN = 9;// change it to 9

MCP_CAN CAN(SPI_CS_PIN); //Set CS pin

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  delay(1000);
  while (CAN_OK != CAN.begin(MCP_ANY, CAN_1000KBPS, MCP_16MHZ)) {
    Serial.println("CAN BUS Shield init fail");
    Serial.println("Init CAN BUS Shield again");
    delay(1000);
  }
  Serial.println("CAN BUS Shield init ok!");
}

long previousMillis = 0;

boolean newData = false;


void loop() {

  char rc;
  float p_step = 0.1;
  //delay(200);
  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();
    Serial.println(rc);
    if (rc == 's') {
      Serial.println("True");
    }


    if (rc == 'u') {
      p_in = p_in + p_step;

    }

    if (rc == 'd') {
      p_in = p_in - p_step;
    }

    p_in = constrain(p_in, P_MIN, P_MAX);//

    if (rc == 'o') {
      EnterMotorMode();
    }

    if (rc == 'f') {
      ExitMotorMode();
    }
    // send CAN
    pack_cmd();
  }
  // receive CAN

  if (CAN_MSGAVAIL == CAN.checkReceive()) // check if data coming
  {
    unpack_reply();
  }

  Serial.print(millis() - previousMillis);
  previousMillis = millis();
  Serial.print("ID ");
  Serial.print(id);
  Serial.print("position ");
  Serial.print(p_in);
  Serial.print(" ");

  Serial.print("position output ");
  Serial.print(p_out);
  Serial.print(" ");
  Serial.print("velocity output ");
  Serial.print(v_out);
  Serial.print(" ");
  Serial.print("torque output ");
  Serial.println(t_out);
}

void EnterMotorMode() {// standard values

  byte buf[8];
  buf[0] = 0xFF;
  buf[1] = 0xFF;
  buf[2] = 0xFF;
  buf[3] = 0xFF;
  buf[4] = 0xFF;
  buf[5] = 0xFF;
  buf[6] = 0xFF;
  buf[7] = 0xFC;
  CAN.sendMsgBuf(0x01, 0, 8, buf);
}

void ExitMotorMode() {

  byte buf[8];
  buf[0] = 0xFF;
  buf[1] = 0xFF;
  buf[2] = 0xFF;
  buf[3] = 0xFF;
  buf[4] = 0xFF;
  buf[5] = 0xFF;
  buf[6] = 0xFF;
  buf[7] = 0xFD;
  CAN.sendMsgBuf(0x01, 0, 8, buf);
}

void Zero() {

  byte buf[8];
  buf[0] = 0xFF;
  buf[1] = 0xFF;
  buf[2] = 0xFF;
  buf[3] = 0xFF;
  buf[4] = 0xFF;
  buf[5] = 0xFF;
  buf[6] = 0xFF;
  buf[7] = 0xFE;
  CAN.sendMsgBuf(0x01, 0, 8, buf);
}


void pack_cmd() {

  byte buf[8];


  // limit data to be within bounds ///

  float p_des = constrain(p_in, P_MIN, P_MAX);
  float v_des = constrain(v_in, V_MIN, V_MAX);
  float kp = constrain(kp_in, KP_MIN, KP_MAX);
  float kd = constrain(kd_in, KD_MIN, KD_MAX);
  float t_ff = constrain(t_in, T_MIN, T_MAX);

  unsigned int p_int = float_to_uint(p_des, P_MIN, P_MAX, 16);
  unsigned int v_int = float_to_uint(v_des, V_MIN, V_MAX, 12);
  unsigned int kp_int = float_to_uint(kp, KP_MIN, KP_MAX, 12);
  unsigned int kd_int = float_to_uint(kd, KD_MIN, KD_MAX, 12);
  unsigned int t_int = float_to_uint(t_ff, T_MIN, T_MAX, 12);

  //pack ints into can buffer ///

  buf[0] = p_int >> 8;
  buf[1] = p_int & 0xFF;
  buf[2] = v_int >> 4;
  buf[3] = ((v_int & 0xF) << 4) | (kp_int >> 8);
  buf[4] = kp_int & 0xFF;
  buf[5] = kd_int >> 4;
  buf[6] = ((kd_int & 0xF) << 4) | (t_int >> 8);
  buf[7] = t_int & 0xFF;
  CAN.sendMsgBuf(0x01, 0, 8, buf);// canid
}

void unpack_reply() {

  unsigned long int canId;
  byte len = 0;
  byte buf[8];


  CAN.readMsgBuf(&canId, &len, buf);
  //unsigned long canId = CAN.getCanId();

  ///unpack ints from can buffer///

  unsigned int id = buf[0];
  unsigned int p_int = (buf[1] << 8) | buf[2];
  unsigned int v_int = (buf[3] << 4) | (buf[4] >> 4);
  unsigned int i_int = ((buf[4] & 0xF) << 8) | buf[5];

  /// convert uints to floats ///

  p_out = uint_to_float(p_int, P_MIN, P_MAX, 16);
  v_out = uint_to_float(v_int, V_MIN, V_MAX, 12);
  t_out = uint_to_float(i_int, -T_MIN, T_MAX, 12);
}

unsigned int float_to_uint(float x, float x_min, float x_max, float bits) {

  //convert a float to an unsigned int, given range and number of bits ///

  float span = x_max - x_min;
  float offset = x_min;
  unsigned int pgg = 0;

  if (bits == 12) {
    pgg = (unsigned int)((x - offset) * 4095.0 / span);
  }
  if (bits == 16) {
    pgg = (unsigned int)((x - offset) * 65535.0 / span);
  }
  return pgg;
}


float uint_to_float(unsigned int x_int, float x_min, float x_max, int bits) {
  /// converts unsined int to float, given range and numbers of bits///
  float span = x_max - x_min;
  float offset = x_min;
  float pgg = 0;
  if (bits == 12) {
    pgg = ((float)x_int) * span / 4095.0 + offset;
  }
  if (bits == 16) {
    pgg = ((float)x_int) * span / 65535.0 + offset;
  }
  return pgg;
}