0

CAN Bus example for MRMS Motor Driver 4x3.6A CAN Bus (mrm-mot4x3.6can)

Prerequisites

If not already done so already, install Arduino software and make basic connections, as described in page for MRMS ESP32: Arduino, IMU, eFuse, BT, WiFi, CAN Bus (mrm-esp32). This setup is convenient, but it is not mandatory. Read on and we will describe how to connect some other microcontroller and power supply.

This setup is necessary for the rest of ML-R system. If You want to try just this example, don't copy all the libraries but only directories "mrm-common" and "mrm-can-bus". They provide CAN Bus support, but again just for ESP32 microcontroller, ML-R or any other. If You want to use some other MCU, You will have to start its CAN Bus driver. If You do so, this example will work for that board, too.

Task

We will send CAN Bus messages to the controller in order to control motor's speed and direction.

Pinout

Note positions of the necessary connectors:
  • VIn,
  • CAN,
  • 4 outputs for motors.

Connections

Starting from a basic connection, let's add a MRMS Motor Driver 4x3.6A CAN Bus (mrm-mot4x3.6can). Connecting a ML-R CAN Bus device is easy. Only a cable, like MRMS CAN Bus cable 10 cm (mrm-jst-can10), is necessary. The cable will connect 2 CAN Bus lines (low and high) and will also supply 0 V and 5 V through 3. and 4. wire. The voltage and current supplied by the CAN Bus cable will not be sufficient to drive the motors so a separate cable, like ML-R Cable KK396-KK396 10 cm (mrm-kk3.96-3.96-10) will be needed.

However, this bus is not only intended for ML-R devices, but for any other CAN Bus compatible. You can read here how to connect Your boards to ML-R CAN Bus. For this board, You can supply 5 V to the motor controller by using 5 V (labeled red in the picture left) and GND pins of the 4-pin Dupont 0.1" connector. CAN Bus can be connected to the inner 2 pins of the same connector, CAN L and CAN H (low and high).

Program

The program is simple, but it hides some of the details in Mrm_can_bus class:

#include <mrm-can-bus.h>

#define ID_MOTOR1 0x230
#define ID_MOTOR2 0x232
#define ID_MOTOR3 0x234
#define ID_MOTOR4 0x236

#define COMMAND_SPEED_SET 0x20

uint32_t motorId[4] = {ID_MOTOR1, ID_MOTOR2, ID_MOTOR3, ID_MOTOR4};
Mrm_can_bus can;
uint8_t data[8];        // Message content: 8 bytes

void setSpeed(uint8_t motorNumber, int8_t speed) {
  data[0] = COMMAND_SPEED_SET;
  data[1] = speed + 128;
  can.messageSend(motorId[motorNumber], 2, data);
}

void setup() {
}

void loop() {
  for (uint16_t motor = 0; motor < 4; motor++){    
    int8_t step = 1;
    for (int8_t speed = 1; !(speed == 0 && step == 1); speed += step){
      setSpeed(motor, speed);
      delay(30);
      if (abs(speed) == 127)
        step = -step;
    }
  }
}
The code uses Mrm_can_bus class to access ESP32s CAN Bus hardware. First line includes Mrm_can_bus library. It handles CAN Bus messages. 4 #define commands follow. They enable us, for example, to write word "ID_MOTOR1" later in the code (and it seems meaningful), instead of "0x230" (a magic number which doesn't). In this way we increase clarity of the code. So, these 4 are CAN Bus message identification bytes, 1 for each of the 4 motor controllers in the MRMS Motor Driver 4x3.6A CAN Bus (mrm-mot4x3.6can). Each of them listens only to its message id (like an address). You can use 2 controllers mrm-mot4x3.6can in Your system, each with its own range of addresses. The first range is in the program: 0x230, 0x232, 0x234, and 0x236. The second one is: 0x238, 0x23A, 0x23C, and 0x23E. If You changed Your controller's addresses, this example would not work. In that case use the second range (#define ID_MOTOR1 0x238, etc).

The same can be said for COMMAND_SPEED_SET 0x20. After this line, we can write "COMMAND_SPEED_SET" and the compiler will swap this term with 0x20, command to set speed.

motorId is an array which is not necessary, but will make the code verbatim as we can iterate an array. We want to do the same action for every motor, an ideal opportunity for iteration. After that we define "can" as an object of class Mrm_can_bus. Explaining objects is beyond scope of this short lesson and is not very important for this program. We can say that it is something that can encapsulate functions, like the one we need: sending a CAN Bus message. The last global variable is data[8], an array of bytes, which will be used for CAN Bus message's payload.

setSpeed() is a function to drive motors. First argument selects a motor (0 - 3), the second its speed (-127 to 127). A negative values spins the motor in the other direction of the positive one. The function sets first byte as the command we want to execute, setting speed. The second byte chooses the speed, translating the input into a positive number. It finally sends the CAN Bus message, using the object "can".

loop() contains 2 nested loops: the outer one iterates all the motors, the inner sets speed: first gradually from standstill to maximum rotation, then gradually to maximum opposite rotation, and in the end towards stop.

You can use an oscilloscope or a logic analyzer to check the signals.

The program uses mrm-can-bus library which can be found in C:\Users\<Your login name>\Documents\Arduino\libraries\mrm-can-bus\src. Open mrm-can-bus.cpp in an editor. Notepad++ is a good choice. If You want to change bus speed from the default 250 kbps, find line with "CAN_TIMING_CONFIG_250KBITS" and change the constant. If You like to have messages' content displayed, change "#define VERBOSE 0" into "#define VERBOSE 1". This library is based on Espressif's native driver so Espressif original documentation will give You all the information.

If You use ML-R CAN Bus devices, the easiest option for CAN Bus usage will be to stick to the ML-R framework which takes care about messaging so CAN Bus layer is not exposed at all. You can call a simple function to set the speed of the motor controller and the function is the same for all of the ML-R motor controllers.

Here is the complete CAN Bus interface:
CommandMCU -> controllerController -> MCUComment
Firmware0x190x1A LowByte HighByteFirmware version = (HighByte << 8) & LowByte.
FPS0x300x31 LowByte HighByteFrames Per Second (sensor's local loop frequency) = (HighByte << 8) & LowByte.
Id change0x40 NewIdChanges ids the motor controller listens to. It is close to its address. NewId can have value 0 or 1. 0 sets the controllers addresses to 0x230, 0x232, 0x234, and 0x236. 1 chooses the other range: 0x238, 0x23A, 0x23C, and 0x23E.
Alive0xFF id0xFFIf the controller with address "id" is present, it will return the message.
Reset0x1B
Set speed0x20 SpeedSets speed of the selected motor to be "Speed" (value of that byte).

CAN Bus limitations

For 1 node You will have no problems. If You plan to have many of them, check this page.