0

CAN Bus example for ML-R 8x8 bicolor display, CAN Bus,UART, 4 switches (mrm-8x8a)

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.

Task

We will order the lidar to start measuring distance and sending the result every few milliseconds.

Pins

Note the necessary pins.

Connections

Starting from a basic connection, let's add a ML-R 8x8 bicolor display, CAN Bus,UART, 4 switches (mrm-8x8a). 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. Current supplied by the CAN Bus cable will not be sufficient to drive the LED array so a separate cable, like ML-R Cable KK254-KK254 10 cm (mrm-kk2.54-2.54-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.

Program

A library will be needed. Go to https://github.com/PribaNosati/MRMS, choose "Clone or download", "Download ZIP". Delete all directories that start with "mrm-" from Your Arduino libraries' directory: "C:\Users\<Your login name>\Documents\Arduino\libraries". Unzip the downloaded file and copy all the directories that start again with "mrm-" into the same Arduino libraries' directory. Restart Arduino IDE.

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.

The program is longer than majority of examples in this web, but it still hides some of the details in Mrm_can_bus class:

#include <mrm-can-bus.h<

#define COMMAND_SWITCH_ON 0x01
#define COMMAND_8X8_BITMAP_DISPLAY_PART1 0x05
#define COMMAND_8X8_BITMAP_DISPLAY_PART2 0x06
#define COMMAND_8X8_BITMAP_DISPLAY_PART3 0x07
#define COMMAND_SENSORS_MEASURE_CONTINUOUS 0x10
#define COMMAND_SENSORS_MEASURE_SENDING 0x13

Mrm_can_bus can;
uint8_t data[8];        // Message content: 8 bytes

void setup() {
  data[0] = COMMAND_SENSORS_MEASURE_CONTINUOUS;  // First byte of the content
  can.messageSend(0x200, 1, data);
}

void loop() {
  // Receive a message
  CANBusMessage* msg = can.messageReceive();
  if (msg != NULL && msg->data[0] == COMMAND_SWITCH_ON){

    uint8_t red[8] = { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000100, 0b00111000, 0b00000000, 0b00111100 };
    uint8_t green[8] = { 0b00111100, 0b01000010, 0b10101001, 0b10101001, 0b10000001, 0b10000001, 0b01000010, 0b00111100 };
      
    data[0] = COMMAND_8X8_BITMAP_DISPLAY_PART1;
    for (uint8_t i = 0; i < 7; i++) 
      data[i + 1] = green[i];
    can.messageSend(0x200, 8, data);

    data[0] = COMMAND_8X8_BITMAP_DISPLAY_PART2;
    data[1] = green[7];
    for (uint8_t i = 0; i < 6; i++) 
      data[i + 2] = red[i];
    can.messageSend(0x200, 8, data);

    data[0] = COMMAND_8X8_BITMAP_DISPLAY_PART3;
    for (uint8_t i = 0; i < 2; i++) 
      data[i + 1] = red[i + 6];
    can.messageSend(0x200, 3, data);
  }
}
The program uses Mrm_can_bus class to access ESP32s CAN Bus hardware. There are 6 #define commands in the beginning, in order to use human readable words instead of hexadecimal number. We will use them later.

Next 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.

setup() sends a message that instructs mrm-8x8a to send a message when state of one of its switches changes (off to on or on to off). The first command sets payload array, its first byte, to COMMAND_SENSORS_MEASURE_CONTINUOUS. That's the command we'd like to execute to start listening to switches. The second instruction sends CAN Bus message. First argument is target device's address. Most probably it is 0x200. However, as You can have more mrm-8x8a devices in Your CAN Bus, each is supposed to listen to just one message id, so 0x200 is not the only possibility. The whole range is 0x200, 0x202,..., 0x20E. The device is shipped with default address 0x200. If You changed it, You will have to use the changed address instead of 0x200.

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 switch state change. If it is, it will execute the commands in "if" block.

2 arrays follow, red and green. red defines red dots, green green ones. If both red and green dots are on, the result will be an orange dot. First number of each array defines first row of the display, etc.

Next 3 blocks are similar. Each sends a CAN Bus message. As there are only 8 bytes in one message in CAN Bus 2.0 format, 3 messages are needed to send all the information to the sensor. Therefore, each message has a different first byte and all the dots are split in 3 messages.

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 ML-R 8x8 bicolor display, CAN Bus,UART, 4 switches (mrm-8x8a) to display a bitmap, You just call a function. The framework takes care about the rest. The details of this work are not so easy to replicate.

Here is the complete CAN Bus interface:
CommandMCU -> sensorSensor -> MCUComment
Display custom bitmap, part 10x05 Byte1,..., Byte8Build Byte1,... as in the example above.
Display custom bitmap, part 20x06 Byte1,..., Byte7Build Byte1,... as in the example above.
Display custom bitmap, part 30x07 Byte1,..., Byte3Build Byte1,... as in the example above.
Store bitmap, part 10x08 Byte1,..., Byte8Build Byte1,... as in the example above.
Store bitmap, part 20x09 Byte1,..., Byte7Build Byte1,... as in the example above.
Store bitmap, part 30x0A Byte1,..., Byte4Build Byte1,... as in the example above. The only difference is Byte4 (note that the last above was Byte3). Byte4 is bitmap's id.
Display stored bitmap0x0B BitmapIdDisplays previously stored bitmap. Power loss doesn't erase stored bitmaps. They are displayed faster than custom bitmaps.
Display a built-in bitmap.0x00 BitmapIdBuilt-in bitmaps cannot be erased or changed. They represent ASCII signs and a few more. Valid BitmapId values are 0x01 - 0x04 and 0x30 - 0x5A. ASCII code of a character is its BitmapId.
Set rotation0x0C Byte3 rotations are possible (Byte values): 0 - 0º, 1 - 90º, 2 - 270º.
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 an addresses in range: 0x200, 0x202,..., 0x20E. 0 chooses 0x200, 1 0x202, etc.
Alive0xFF id0xFFIf the sensor with address "id" is present, it will return the message.
Reset0x1B
Measure continuous0x100x01 ByteInitiates monitoring of 4 switches. If any changes, a return message will follow. Byte & 1 will give the new state (0 - off, 1 - on). Byte >> 1 is switch number (0 - 3).
Stop measuring0x12

CAN Bus limitations

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