The Adafruit_CAN library provides support for native CAN peripherals, like the one in the ATSAME51 processor on the Feather M4 CAN Express.

Install the Arduino Library

The Adafruit_CAN library can be installed via the Arduino Library Manager. Find it by searching for "Adafruit CAN". Verify the library by name in the results and then click Install.

You'll also need the NeoPixel library.

When uploading code make sure you have Feather M4 CAN (SAME51) selected, not the Feather M4 Express!

Transmission & Reception Examples

Our first examples will transmit packets from one board to another. In this example we're using two CAN Feather boards but of course you can replace either side with a different CAN-enabled platform.

Wire up L to L and H to H on the bus. You do not need to connect Ground (but it is available)

On one Feather load the transmitter example:

/*
 * Adafruit Feather M4 CAN Sender Example
 */

#include <CANSAME5x.h>

CANSAME5x CAN;

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);

  Serial.println("CAN Sender");

  pinMode(PIN_CAN_STANDBY, OUTPUT);
  digitalWrite(PIN_CAN_STANDBY, false); // turn off STANDBY
  pinMode(PIN_CAN_BOOSTEN, OUTPUT);
  digitalWrite(PIN_CAN_BOOSTEN, true); // turn on booster

  // start the CAN bus at 250 kbps
  if (!CAN.begin(250000)) {
    Serial.println("Starting CAN failed!");
    while (1) delay(10);
  }
  Serial.println("Starting CAN!");
}

void loop() {
  // send packet: id is 11 bits, packet can contain up to 8 bytes of data
  Serial.print("Sending packet ... ");

  CAN.beginPacket(0x12);
  CAN.write('h');
  CAN.write('e');
  CAN.write('l');
  CAN.write('l');
  CAN.write('o');
  CAN.endPacket();

  Serial.println("done");

  delay(1000);

  // send extended packet: id is 29 bits, packet can contain up to 8 bytes of data
  Serial.print("Sending extended packet ... ");

  CAN.beginExtendedPacket(0xabcdef);
  CAN.write('w');
  CAN.write('o');
  CAN.write('r');
  CAN.write('l');
  CAN.write('d');
  CAN.endPacket();

  Serial.println("done");

  delay(1000);
}

On the other Feather load the receiver example

/*
 * Adafruit Feather M4 CAN Receiver Example
 */

#include <CANSAME5x.h>

CANSAME5x CAN;

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);

  Serial.println("CAN Receiver");

  pinMode(PIN_CAN_STANDBY, OUTPUT);
  digitalWrite(PIN_CAN_STANDBY, false); // turn off STANDBY
  pinMode(PIN_CAN_BOOSTEN, OUTPUT);
  digitalWrite(PIN_CAN_BOOSTEN, true); // turn on booster

  // start the CAN bus at 250 kbps
  if (!CAN.begin(250000)) {
    Serial.println("Starting CAN failed!");
    while (1) delay(10);
  }
  Serial.println("Starting CAN!");
}

void loop() {
  // try to parse packet
  int packetSize = CAN.parsePacket();

  if (packetSize) {
    // received a packet
    Serial.print("Received ");

    if (CAN.packetExtended()) {
      Serial.print("extended ");
    }

    if (CAN.packetRtr()) {
      // Remote transmission request, packet contains no data
      Serial.print("RTR ");
    }

    Serial.print("packet with id 0x");
    Serial.print(CAN.packetId(), HEX);

    if (CAN.packetRtr()) {
      Serial.print(" and requested length ");
      Serial.println(CAN.packetDlc());
    } else {
      Serial.print(" and length ");
      Serial.println(packetSize);

      // only print packet data for non-RTR packets
      while (CAN.available()) {
        Serial.print((char)CAN.read());
      }
      Serial.println();
    }

    Serial.println();
  }
}
Make sure whenever you load CAN read/write code you match the bus speed. Some examples are 500000 baud but we recommend 250000 baud! Whatever you use, all boards must have the same

Now open the Serial console for both CAN Feathers (you may need to run Arduino two times, or use another serial port monitor so you can see both)

On the transmitter you'll see:

On the receiver you'll see the data getting received!

If not, check that L and H wires are both solidly connected

For more details on the Adafruit CAN library usage, check the documentation

Bi-Directional Communication Demo

OK now you can send and receive, but you'd like to do both? No problem, try this example!

On the two boards wired together, connect a potentiometer to each board, with the middle pin going to A5 and one side to ground and the other side to 3V

Upload the same code to both boards

/*
 * Adafruit Feather M4 CAN Transceiver Example
 */

#include <CANSAME5x.h>
#include <Adafruit_NeoPixel.h>

CANSAME5x CAN;

Adafruit_NeoPixel strip(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);

#define MY_PACKET_ID 0xAF

uint32_t timestamp;

void setup() {
  Serial.begin(115200);
  //while (!Serial) delay(10);

  Serial.println("CAN NeoPixel Potentiometer RX/TX demo");

  pinMode(PIN_CAN_STANDBY, OUTPUT);
  digitalWrite(PIN_CAN_STANDBY, false); // turn off STANDBY
  pinMode(PIN_CAN_BOOSTEN, OUTPUT);
  digitalWrite(PIN_CAN_BOOSTEN, true); // turn on booster

  strip.begin();
  strip.setBrightness(50);

  // start the CAN bus at 250 kbps
  if (!CAN.begin(250000)) {
    Serial.println("Starting CAN failed!");
    while (1) delay(10);
  }

  timestamp = millis();
}

void loop() {
  // every 100 ms send out a packet
  if ((millis() - timestamp) > 100) {
    uint16_t pot = analogRead(A5);
    // send a packet with the potentiometer value  
    Serial.print("Sending packet with value ");
    Serial.print(pot);

    CAN.beginPacket(MY_PACKET_ID);
    CAN.write(pot >> 8);
    CAN.write(pot & 0xFF);
    CAN.endPacket();
  
    Serial.println("...sent!");
    timestamp = millis();
  }

  // try to parse any incoming packet
  int packetSize = CAN.parsePacket();

  if (packetSize) {
    // received a packet
    Serial.print("Received ");

    if (CAN.packetExtended()) {
      Serial.print("extended ");
    }

    if (CAN.packetRtr()) {
      // Remote transmission request, packet contains no data
      Serial.print("RTR ");
    }

    Serial.print("packet with id 0x");
    Serial.print(CAN.packetId(), HEX);

    if (CAN.packetRtr()) {
      Serial.print(" and requested length ");
      Serial.println(CAN.packetDlc());
    } else {
      Serial.print(" and length ");
      Serial.println(packetSize);

      uint8_t receivedData[packetSize];
      for (int i=0; i<packetSize; i++) {
        receivedData[i] = CAN.read();
        Serial.print("0x");
        Serial.print(receivedData[i], HEX);
        Serial.print(", ");
      }
      Serial.println();

      uint16_t value = (uint16_t)receivedData[0] << 8 | receivedData[1];
      strip.setPixelColor(0, Wheel(value / 4));
      strip.show();
    }

    Serial.println();
  }
}

uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

Ten times a second, each board sends out the analog reading from A5 as two bytes of data. If you want to have more than two boards, you can give each board a unique ID by editing #define MY_PACKET_ID 0xAF to change it to a different byte value

uint16_t pot = analogRead(A5);
    //...
    CAN.beginPacket(MY_PACKET_ID);
    CAN.write(pot >> 8);
    CAN.write(pot & 0xFF);
    CAN.endPacket();

We also check to read any packets, there are a few different types of CAN packets (not covered in this guide) so we make sure its a non-return-request type...

// try to parse any incoming packet
int packetSize = CAN.parsePacket();

if (packetSize) {
  // received a packet
  Serial.print("Received ");
  //...
  Serial.print("packet with id 0x");
  Serial.print(CAN.packetId(), HEX);

  if (CAN.packetRtr()) {
    Serial.print(" and requested length ");
    Serial.println(CAN.packetDlc());
  } else {
    Serial.print(" and length ");
    Serial.println(packetSize);

We then read the data from the CAN buffer, convert the first two bytes back into a 16-bit number. That number is going to range from 0 to 1023 (because the analog input signal from the sender is 10-bit). We divide it by 4 so it ranges from 0-255 and then use our color Wheel function to convert into a rainbow color.

Note we do very little checking to verify the packet data and length, this is a very barebones example!

uint8_t receivedData[packetSize];
    for (int i=0; i<packetSize; i++) {
      receivedData[i] = CAN.read();
      Serial.print("0x");
      Serial.print(receivedData[i], HEX);
      Serial.print(", ");
    }
    Serial.println();

    uint16_t value = (uint16_t)receivedData[0] << 8 | receivedData[1];
    strip.setPixelColor(0, Wheel(value / 4));
    strip.show();
  }

Twist the knob on one Feather breadboard to see the NeoPixel on the other Feather change colors!

This guide was first published on Jan 25, 2021. It was last updated on Jul 09, 2024.

This page (Arduino CAN Examples) was last updated on Jul 09, 2024.

Text editor powered by tinymce.