Have to spam CAN messages for arduino to recognize

Hey Guys,

so im pretty new into working with CAN and i run into some issue.
I have a code that reads CAN messages out of a CAN network and should then do X when receiving X CAN ID. The issue that i know have is that on higher BUS loads i have to send the CAN messages multiple times for the Arduino to recognize. Its completly random sometimes it works on the first send and sometimes i have to spam 20 times before it gets the message.

I already double checked with an second code that just reads CAN messages and my sender node is working flawless.

that the code:

#include <SPI.h>
#include <mcp_can.h>
#include <Arduino.h>
#include <Wire.h>
#include "DisplayManager.h"

#define IDLE_STATE 0
#define PRE_CHARGE_STATE 1
#define FULL_CHARGE_STATE 2
#define OFF_STATE 3

#define IDLE_CMD_ID 0x01
#define CONNECT_CMD_ID 0x02
#define DISCONNECT_CMD_ID 0x03
#define OFF_CMD_ID 0x04

const int preChargeOutputPin = 6;
const int fullChargeOutputPin = 7;
const int idleOutputPin = 5;
int currentState = OFF_STATE;
unsigned long preChargeStartTime = 0;
DisplayManager displayManager;

MCP_CAN CAN(10); // CS Pin is 10

void setup() {
  Serial.begin(9600);
  
  pinMode(preChargeOutputPin, OUTPUT);
  pinMode(fullChargeOutputPin, OUTPUT);
  pinMode(idleOutputPin, OUTPUT);
  digitalWrite(preChargeOutputPin, LOW);
  digitalWrite(fullChargeOutputPin, LOW);
  digitalWrite(idleOutputPin, LOW);

  // Initialize the MCP2515 CAN controller
  if (CAN.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) {
    Serial.println("CAN-Bus initialized");
    CAN.setMode(MCP_NORMAL);
  } else {
    Serial.println("CAN-Bus initialization error");
    while (1);
  }

  // Initialize the OLED Display
  displayManager.begin();
}

void loop() {
  float voltageA0 = analogRead(A0) * (60.0 / 1023.0);
  float voltageA1 = analogRead(A1) * (60.0 / 1023.0);

  // Debugging-Nachrichten für die Spannungen hinzufügen
  //Serial.print("Voltage A0: ");
  //Serial.println(voltageA0, 2); // Anzeige auf zwei Dezimalstellen genau
  //Serial.print("Voltage A1: ");
  //Serial.println(voltageA1, 2); // Anzeige auf zwei Dezimalstellen genau

    if (currentState == IDLE_STATE) {
    displayManager.showStateAndVoltage("Idle", voltageA0, voltageA1);
  } else if (currentState == PRE_CHARGE_STATE) {
    displayManager.showStateAndVoltage("Pre-charge", voltageA0, voltageA1);
    if (abs(voltageA0 - voltageA1) <= 200.0 && millis() - preChargeStartTime >= 1000) {
      currentState = FULL_CHARGE_STATE;
      digitalWrite(fullChargeOutputPin, HIGH);
      delay(1500);
      digitalWrite(preChargeOutputPin, LOW);
      Serial.println("State: Full-charge");
    } else {
      // Debugging-Nachricht, wenn die Bedingung nicht erfüllt ist
      Serial.println("Pre-charge condition not met");
      Serial.print("Voltage A0: ");
      Serial.println(voltageA0);
      Serial.print("Voltage A1: ");
      Serial.println(voltageA1);
    }
  } else if (currentState == FULL_CHARGE_STATE) {
    displayManager.showStateAndVoltage("Full-charge", voltageA0, voltageA1);
  } else if (currentState == OFF_STATE) {
    displayManager.showStateAndVoltage("Off", voltageA0, voltageA1);
  }

  // Check for CAN messages
  if (CAN_MSGAVAIL == CAN.checkReceive()) {
    unsigned char len = 0;
    unsigned char buf[8];
    unsigned long canId = 0;
    CAN.readMsgBuf(&canId, &len, buf);

    if (canId == IDLE_CMD_ID) {
      handleCommand("idle");
    } else if (canId == CONNECT_CMD_ID) {
      if (currentState == IDLE_STATE && voltageA0 >= 30.0 && voltageA1 >= 30.0) {
        handleCommand("connect");
      }
    } else if (canId == DISCONNECT_CMD_ID) {
      handleCommand("disconnect");
    } else if (canId == OFF_CMD_ID) {
      handleCommand("off");
    }
  }

  // Check for serial messages
  if (Serial.available() > 0) {
    String command = Serial.readString();
    command.trim();
    handleCommand(command);
  }
}


void handleCommand(const String& command) {
  float voltageA0 = analogRead(A0) * (60.0 / 1023.0);
  float voltageA1 = analogRead(A1) * (60.0 / 1023.0);

  if (command == "idle") {
    currentState = IDLE_STATE;
    digitalWrite(idleOutputPin, HIGH);
    Serial.println("State: Idle");
  } else if (command == "connect" && currentState == IDLE_STATE && voltageA0 >= 30.0 && voltageA1 >= 30.0) {
    currentState = PRE_CHARGE_STATE;
    digitalWrite(preChargeOutputPin, HIGH);
    preChargeStartTime = millis();
    Serial.println("State: Pre-charge");
  } else if (command == "disconnect") {
    currentState = IDLE_STATE;
    digitalWrite(preChargeOutputPin, LOW);
    digitalWrite(fullChargeOutputPin, LOW);
    Serial.println("State: Idle");
  } else if (command == "off") {
    currentState = OFF_STATE;
    digitalWrite(idleOutputPin, LOW);
    digitalWrite(preChargeOutputPin, LOW);
    digitalWrite(fullChargeOutputPin, LOW);
    Serial.println("State: Off");
  }

  // Update Display State
  if (currentState == IDLE_STATE) {
    displayManager.showStateAndVoltage("Idle", voltageA0, voltageA1);
  } else if (currentState == PRE_CHARGE_STATE) {
    displayManager.showStateAndVoltage("Pre-charge", voltageA0, voltageA1);
  } else if (currentState == FULL_CHARGE_STATE) {
    displayManager.showStateAndVoltage("Full-charge", voltageA0, voltageA1);
  } else if (currentState == OFF_STATE) {
    displayManager.showStateAndVoltage("Off", voltageA0, voltageA1);
  }
}

Use the MCP2515 interrupt to receive data as soon as it arrives and append it to a FIFO message queue that gets processed in the main loop. Also use a faster Arduino.

IMHO, if you make use of the filter feature on the MCP2515, that might potentially help with your current issue.

hope that helps...

i tried the filter feature already but same problem

#include <SPI.h>
#include <mcp_can.h>
#include <Arduino.h>
#include <Wire.h>
#include "DisplayManager.h"

#define IDLE_STATE 0
#define PRE_CHARGE_STATE 1
#define FULL_CHARGE_STATE 2
#define OFF_STATE 3

#define IDLE_CMD_ID 0x01
#define CONNECT_CMD_ID 0x02
#define DISCONNECT_CMD_ID 0x03
#define OFF_CMD_ID 0x04

const int preChargeOutputPin = 6;
const int fullChargeOutputPin = 7;
const int idleOutputPin = 5;
int currentState = OFF_STATE;
unsigned long preChargeStartTime = 0;
DisplayManager displayManager;

MCP_CAN CAN(10); // CS Pin is 10

#define REQUIRED_CAN_ID_1 0x01
#define REQUIRED_CAN_ID_2 0x02
#define REQUIRED_CAN_ID_3 0x03
#define REQUIRED_CAN_ID_4 0x04

void setup() {
  Serial.begin(9600);
  
  pinMode(preChargeOutputPin, OUTPUT);
  pinMode(fullChargeOutputPin, OUTPUT);
  pinMode(idleOutputPin, OUTPUT);
  digitalWrite(preChargeOutputPin, LOW);
  digitalWrite(fullChargeOutputPin, LOW);
  digitalWrite(idleOutputPin, LOW);

  // Initialize the MCP2515 CAN controller
  if (CAN.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) {
    Serial.println("CAN-Bus initialized");
    CAN.setMode(MCP_NORMAL);
  } else {
    Serial.println("CAN-Bus initialization error");
    while (1);
  }

  // Setze den CAN-Bus-Filter für die gewünschten CAN-IDs
  CAN.init_Mask(0, 0, 0x7FF);  // Setze Maskierung, um alle Bits in der ID zu berücksichtigen
  CAN.init_Filt(0, 0, REQUIRED_CAN_ID_1);  // Filter für CAN-ID 0x01
  CAN.init_Filt(1, 0, REQUIRED_CAN_ID_2);  // Filter für CAN-ID 0x02
  CAN.init_Filt(2, 0, REQUIRED_CAN_ID_3);  // Filter für CAN-ID 0x03
  CAN.init_Filt(3, 0, REQUIRED_CAN_ID_4);  // Filter für CAN-ID 0x04

  // Initialize the OLED Display
  displayManager.begin();
}

void loop() {
  float voltageA0 = analogRead(A0) * (60.0 / 1023.0);
  float voltageA1 = analogRead(A1) * (60.0 / 1023.0);

  // Debugging-Nachrichten für die Spannungen hinzufügen
  //Serial.print("Voltage A0: ");
  //Serial.println(voltageA0, 2); // Anzeige auf zwei Dezimalstellen genau
  //Serial.print("Voltage A1: ");
  //Serial.println(voltageA1, 2); // Anzeige auf zwei Dezimalstellen genau

    if (currentState == IDLE_STATE) {
    displayManager.showStateAndVoltage("Idle", voltageA0, voltageA1);
  } else if (currentState == PRE_CHARGE_STATE) {
    displayManager.showStateAndVoltage("Pre-charge", voltageA0, voltageA1);
    if (abs(voltageA0 - voltageA1) <= 200.0 && millis() - preChargeStartTime >= 1000) {
      currentState = FULL_CHARGE_STATE;
      digitalWrite(fullChargeOutputPin, HIGH);
      delay(1500);
      digitalWrite(preChargeOutputPin, LOW);
      Serial.println("State: Full-charge");
    } else {
      // Debugging-Nachricht, wenn die Bedingung nicht erfüllt ist
      Serial.println("Pre-charge condition not met");
      Serial.print("Voltage A0: ");
      Serial.println(voltageA0);
      Serial.print("Voltage A1: ");
      Serial.println(voltageA1);
    }
  } else if (currentState == FULL_CHARGE_STATE) {
    displayManager.showStateAndVoltage("Full-charge", voltageA0, voltageA1);
  } else if (currentState == OFF_STATE) {
    displayManager.showStateAndVoltage("Off", voltageA0, voltageA1);
  }

  // Check for CAN messages
  if (CAN_MSGAVAIL == CAN.checkReceive()) {
    unsigned char len = 0;
    unsigned char buf[8];
    unsigned long canId = 0;
    CAN.readMsgBuf(&canId, &len, buf);

    // Überprüfe, ob die empfangene CAN-ID eine der erlaubten CAN-IDs ist
    if (canId == REQUIRED_CAN_ID_1) {
      handleCommand("idle");
    } else if (canId == REQUIRED_CAN_ID_2) {
      if (currentState == IDLE_STATE && voltageA0 >= 30.0 && voltageA1 >= 30.0) {
        handleCommand("connect");
      }
    } else if (canId == REQUIRED_CAN_ID_3) {
      handleCommand("disconnect");
    } else if (canId == REQUIRED_CAN_ID_4) {
      handleCommand("off");
    }
  }

  // Check for serial messages
  if (Serial.available() > 0) {
    String command = Serial.readString();
    command.trim();
    handleCommand(command);
  }
}

void handleCommand(const String& command) {
  float voltageA0 = analogRead(A0) * (60.0 / 1023.0);
  float voltageA1 = analogRead(A1) * (60.0 / 1023.0);

  if (command == "idle") {
    currentState = IDLE_STATE;
    digitalWrite(idleOutputPin, HIGH);
    Serial.println("State: Idle");
  } else if (command == "connect" && currentState == IDLE_STATE && voltageA0 >= 30.0 && voltageA1 >= 30.0) {
    currentState = PRE_CHARGE_STATE;
    digitalWrite(preChargeOutputPin, HIGH);
    preChargeStartTime = millis();
    Serial.println("State: Pre-charge");
  } else if (command == "disconnect") {
    currentState = IDLE_STATE;
    digitalWrite(preChargeOutputPin, LOW);
    digitalWrite(fullChargeOutputPin, LOW);
    Serial.println("State: Idle");
  } else if (command == "off") {
    currentState = OFF_STATE;
    digitalWrite(idleOutputPin, LOW);
    digitalWrite(preChargeOutputPin, LOW);
    digitalWrite(fullChargeOutputPin, LOW);
    Serial.println("State: Off");
  }

  // Update Display State
  if (currentState == IDLE_STATE) {
    displayManager.showStateAndVoltage("Idle", voltageA0, voltageA1);
  } else if (currentState == PRE_CHARGE_STATE) {
    displayManager.showStateAndVoltage("Pre-charge", voltageA0, voltageA1);
  } else if (currentState == FULL_CHARGE_STATE) {
    displayManager.showStateAndVoltage("Full-charge", voltageA0, voltageA1);
  } else if (currentState == OFF_STATE) {
    displayManager.showStateAndVoltage("Off", voltageA0, voltageA1);
  }
}

you incorrectly setup the filter:

you need to initialised BOTH masks when using it. so should be done like this:

  CAN.init_Mask(0, 0, 0x07FF0000);              // Init first mask (Std CAN. No masking applied)
  CAN.init_Filt(0, 0, REQUIRED_CAN_ID_1);              // Init first filter...
  CAN.init_Filt(1, 0, REQUIRED_CAN_ID_2);              // Init second filter...

  CAN.init_Mask(1, 0, 0x07FF0000);              // Init second mask (Std CAN. No masking applied)
  CAN.init_Filt(2, 0, REQUIRED_CAN_ID_3);              // Init third filter...
  CAN.init_Filt(3, 0, REQUIRED_CAN_ID_4);              // Init fourth filter...
  CAN.init_Filt(4, 0, REQUIRED_CAN_ID_4);              // Init fifth filter...
  CAN.init_Filt(5, 0, REQUIRED_CAN_ID_4);              // Init sixth filter...

cannot say for sure it will make any difference though...

also when you say high busload, how high is that please?

okey i will have a look into the filter.

Busload is approx. 50%

a 50% busload is well reasonable.

if your setup is still failing after correct filter setup, then you probably will need to consider some code optimistation/hardware change.

hope that helps....

Hey, so i updated my filter but its still not working :frowning:
this is the new code i implemented it like in your last post

#include <SPI.h>
#include <mcp_can.h>
#include <Arduino.h>
#include <Wire.h>
#include "DisplayManager.h"

#define IDLE_STATE 0
#define PRE_CHARGE_STATE 1
#define FULL_CHARGE_STATE 2
#define OFF_STATE 3

#define IDLE_CMD_ID 0x01
#define CONNECT_CMD_ID 0x02
#define DISCONNECT_CMD_ID 0x03
#define OFF_CMD_ID 0x04

const int preChargeOutputPin = 6;
const int fullChargeOutputPin = 7;
const int idleOutputPin = 5;
int currentState = OFF_STATE;
unsigned long preChargeStartTime = 0;
DisplayManager displayManager;

MCP_CAN CAN(10); // CS Pin is 10

#define REQUIRED_CAN_ID_1 0x01
#define REQUIRED_CAN_ID_2 0x02
#define REQUIRED_CAN_ID_3 0x03
#define REQUIRED_CAN_ID_4 0x04

void setup() {
  Serial.begin(9600);
  
  pinMode(preChargeOutputPin, OUTPUT);
  pinMode(fullChargeOutputPin, OUTPUT);
  pinMode(idleOutputPin, OUTPUT);
  digitalWrite(preChargeOutputPin, LOW);
  digitalWrite(fullChargeOutputPin, LOW);
  digitalWrite(idleOutputPin, LOW);

  // Initialize the MCP2515 CAN controller
  if (CAN.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) {
    Serial.println("CAN-Bus initialized");
    CAN.setMode(MCP_NORMAL);
  } else {
    Serial.println("CAN-Bus initialization error");
    while (1);
  }

  CAN.init_Mask(0, 0, 0x07FF0000);              // Init first mask (Std CAN. No masking applied)
  CAN.init_Filt(0, 0, REQUIRED_CAN_ID_1);              // Init first filter...
  CAN.init_Filt(1, 0, REQUIRED_CAN_ID_2);              // Init second filter...

  CAN.init_Mask(1, 0, 0x07FF0000);              // Init second mask (Std CAN. No masking applied)
  CAN.init_Filt(2, 0, REQUIRED_CAN_ID_3);              // Init third filter...
  CAN.init_Filt(3, 0, REQUIRED_CAN_ID_4);              // Init fourth filter...
  CAN.init_Filt(4, 0, REQUIRED_CAN_ID_4);              // Init fifth filter...
  CAN.init_Filt(5, 0, REQUIRED_CAN_ID_4);              // Init sixth filter...

  // Initialize the OLED Display
  displayManager.begin();
}

void loop() {
  float voltageA0 = analogRead(A0) * (60.0 / 1023.0);
  float voltageA1 = analogRead(A1) * (60.0 / 1023.0);


    if (currentState == IDLE_STATE) {
    displayManager.showStateAndVoltage("Idle", voltageA0, voltageA1);
  } else if (currentState == PRE_CHARGE_STATE) {
    displayManager.showStateAndVoltage("Pre-charge", voltageA0, voltageA1);
    if (abs(voltageA0 - voltageA1) <= 200.0 && millis() - preChargeStartTime >= 1000) {
      currentState = FULL_CHARGE_STATE;
      digitalWrite(fullChargeOutputPin, HIGH);
      delay(1500);
      digitalWrite(preChargeOutputPin, LOW);
      Serial.println("State: Full-charge");
    } else {
      // Debugging-Nachricht, wenn die Bedingung nicht erfüllt ist
      Serial.println("Pre-charge condition not met");
      Serial.print("Voltage A0: ");
      Serial.println(voltageA0);
      Serial.print("Voltage A1: ");
      Serial.println(voltageA1);
    }
  } else if (currentState == FULL_CHARGE_STATE) {
    displayManager.showStateAndVoltage("Full-charge", voltageA0, voltageA1);
  } else if (currentState == OFF_STATE) {
    displayManager.showStateAndVoltage("Off", voltageA0, voltageA1);
  }

  // Check for CAN messages
  if (CAN_MSGAVAIL == CAN.checkReceive()) {
    unsigned char len = 0;
    unsigned char buf[8];
    unsigned long canId = 0;
    CAN.readMsgBuf(&canId, &len, buf);

    // Überprüfe, ob die empfangene CAN-ID eine der erlaubten CAN-IDs ist
    if (canId == REQUIRED_CAN_ID_1) {
      handleCommand("idle");
    } else if (canId == REQUIRED_CAN_ID_2) {
      if (currentState == IDLE_STATE && voltageA0 >= 30.0 && voltageA1 >= 30.0) {
        handleCommand("connect");
      }
    } else if (canId == REQUIRED_CAN_ID_3) {
      handleCommand("disconnect");
    } else if (canId == REQUIRED_CAN_ID_4) {
      handleCommand("off");
    }
  }

  // Check for serial messages
  if (Serial.available() > 0) {
    String command = Serial.readString();
    command.trim();
    handleCommand(command);
  }
}

void handleCommand(const String& command) {
  float voltageA0 = analogRead(A0) * (60.0 / 1023.0);
  float voltageA1 = analogRead(A1) * (60.0 / 1023.0);

  if (command == "idle") {
    currentState = IDLE_STATE;
    digitalWrite(idleOutputPin, HIGH);
    Serial.println("State: Idle");
  } else if (command == "connect" && currentState == IDLE_STATE && voltageA0 >= 30.0 && voltageA1 >= 30.0) {
    currentState = PRE_CHARGE_STATE;
    digitalWrite(preChargeOutputPin, HIGH);
    preChargeStartTime = millis();
    Serial.println("State: Pre-charge");
  } else if (command == "disconnect") {
    currentState = IDLE_STATE;
    digitalWrite(preChargeOutputPin, LOW);
    digitalWrite(fullChargeOutputPin, LOW);
    Serial.println("State: Idle");
  } else if (command == "off") {
    currentState = OFF_STATE;
    digitalWrite(idleOutputPin, LOW);
    digitalWrite(preChargeOutputPin, LOW);
    digitalWrite(fullChargeOutputPin, LOW);
    Serial.println("State: Off");
  }

  // Update Display State
  if (currentState == IDLE_STATE) {
    displayManager.showStateAndVoltage("Idle", voltageA0, voltageA1);
  } else if (currentState == PRE_CHARGE_STATE) {
    displayManager.showStateAndVoltage("Pre-charge", voltageA0, voltageA1);
  } else if (currentState == FULL_CHARGE_STATE) {
    displayManager.showStateAndVoltage("Full-charge", voltageA0, voltageA1);
  } else if (currentState == OFF_STATE) {
    displayManager.showStateAndVoltage("Off", voltageA0, voltageA1);
  }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.