0

CAN Bus example for MRMS reflectance sensors 9x, CAN, analog, I2C (mrm-ref-can)

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 order the reflective array to start measuring reflected light's intensity and sending the result every few milliseconds.

Connections

Starting from the basic connection, let's add a MRMS reflectance sensors 9x, CAN, analog, I2C (mrm-ref-can). This example uses 9-phototransistor version but it is valid for other versions, with 4, 6, and 8 transistors. 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. Reflective arrays, if many connected, could overstep current limits of the CAN Bus cable so a separate 3.3 V power supply cable is needed.

3.3 V supply can be made using a Dupont cable with 0.1" plugs. If there is only one sensor, You will be able to use Molex KK254 header of the MCU board, like in the right picture. If You have more of them, an adapter will be handy, like ML-R Distribution Pins 3x Passive (mrm-distrib-c).

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.

Program

The program is not very long, but it hides some of the details in Mrm_can_bus class:

#include <mrm-can-bus.h>

#define COMMAND_REF_CAN_SENDING_SENSORS_1_TO_3 0x06
#define COMMAND_REF_CAN_SENDING_SENSORS_4_TO_6 0x07
#define COMMAND_REF_CAN_SENDING_SENSORS_7_TO_9 0x08
#define COMMAND_SENSORS_MEASURE_CONTINUOUS 0x10

Mrm_can_bus can;

uint8_t data[8];        // Message content: 8 bytes
uint16_t reading[9];    // Measurement results

void setup() {
  Serial.begin(115200);

  data[0] = COMMAND_SENSORS_MEASURE_CONTINUOUS;  // First byte of the content
  for (uint8_t i = 0; i < 8; i++)
    can.messageSend(0x160 + 2 * i, 1, data);
}

void loop() {
  // Receive a message
  uint8_t startIndex;
  CANBusMessage* msg = can.messageReceive();
  if (msg != NULL){
    switch (msg->data[0]) {
    case COMMAND_REF_CAN_SENDING_SENSORS_1_TO_3:
      startIndex = 0;
      break;
    case COMMAND_REF_CAN_SENDING_SENSORS_4_TO_6:
      startIndex = 3;
      break;
    case COMMAND_REF_CAN_SENDING_SENSORS_7_TO_9:
      startIndex = 6;
      break;
    default:
      startIndex = 0xFF;
    }
    
    for (uint8_t i = 0; i <= 2 && startIndex != 0xFF; i++)
      reading[startIndex + i] = (msg->data[2 * i + 1] << 8) | msg->data[2 * i + 2];
  }

  // Display results
  static uint32_t ms = 0;
  if (millis() - ms > 500){
    for (uint8_t i = 0; i < 9; i++)
      Serial.print(reading[i]), Serial.print(" ");
    Serial.println();
    ms = millis();
  }
}




The program uses Mrm_can_bus class to access ESP32s CAN Bus hardware. First, library mrm-can-bus is included, which implements CAN Bus layer for ESP32. A few #DEFINE commands follow to enable us to write human readable names, like COMMAND_SENSORS_MEASURE_CONTINUOUS, instead of magic numbers, like 0x10. Next we define an object "can". As mentioned in various lessons, object-oriented programming is beyond scope of these lessons and it is not too important here. At last we declare 2 arrays as global variables: one for CAN Bus payloads, the other will hold measurement results.

setup() starts serial communication with PC so that Serial.print() can display the results. First we will use "data" array as payload for an outgoing message and so we set its first byte to COMMAND_SENSORS_MEASURE_CONTINUOUS. That's the command we'd like to execute. It will instruct the sensor to start sending measurement data continually. As You can have more than one reflective array of this kind in Your CAN Bus, each is supposed to listen to just one message id. The whole range is covered by the loop that follows: 0x160, 0x162,..., 0x16E. You can change address the sensor listens to but You may not know how the one You have is set up. So, we start them all.

loop() receives messages. You should be familiar with CAN Bus message structure to understand data sent and received. When a message is received, the program will check first byte of the payload to see if it is a measurement result. If it is, it will decode the results. A CAN Bus message can hold up to 8 bytes of data. That is not enough to transfer 9 analog readings, consisting of 2 bytes each. That is the reason the results will arrive split in 3 messages. switch() command will determine which of the 3 messages arrived and will reconstruct the readings it conveys in the for-loop that follows. In the end, another for-loop will display the results.

Note that the program is intended to be simple so will work only when a single MRMS reflectance sensors 9x, CAN, analog, I2C (mrm-ref-can) sensor is connected. If there are more of them, You will have to determine which one sent the results, according to message id, and reconstruct its reading array, which will have to a 2-dimensional array, instead of 1-dimensional, like here. Also note that, if a sensor contains less than 9 phototransistors, the unused ones will return results which are to be discarded.

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, by far 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. When You demand MRMS reflectance sensors 9x, CAN, analog, I2C (mrm-ref-can) to return reflected light intensities, You just call a function. The framework takes care that the reasonably fresh result is supplied. The details of this work are not so easy to replicate.

Here is the complete CAN Bus interface:
CommandMCU -> sensorSensor -> MCUComment
Calibration0x09Calibrate the sensor and store the values in its internal flash. Calibration determines the lowest and highest value each phototransistor measures.
Calibration data request0x0D6 messagesThe sensor will send calibration data it stored. Power loss doesn't erase the data. Calibration data will be delivered in 6 messages. Sample code is included in MRMS_ESP.ino code, which can be download from Github.
Firmware0x190x1A LowByte HighByteFirmware version = (HighByte << 8) & LowByte.
FPS0x300x31 LowByte HighByteFrames Per Second (sensor's local loop frequency) = (HighByte << 8) & LowByte.
Id change0x40 NewIdChanges message's id the sensor listens to. It is close to its address. Valid NewId values are 0 - 7. Each value chooses addresses in the ranges: 0x160, 0x162,..., 0x16E. 0 chooses 0x160, 1 0x162, etc.
Alive0xFF id0xFFIf the sensor with address "id" is present, it will return the message.
Reset0x1B
Measure once0x110x13 LowByte HighByteMeasures just once and returns the result. Results are returned in 3 messages, as described in the code example above.
Measure continuously0x100x13 LowByte HighByteInitiates measuring and returning of the reflected light's intensity each few ms, if the distance changes. Results are returned in 3 messages, as described in the code example above.
Stop measuring0x12

CAN Bus limitations

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