Neopixel interrupting CanBus : Project Problem

Hello friends
I don't know much about electronics and I just started a project and ran into a problem
I use uno R3, mcp2515, ws2812
I want to display car water temp in an LEDs

As long as Led libraries are not called, the project works correctly and displays car information
But when I want to use a library like adafruit or fastled, project not work correctly and says
: "Starting CAN failed!".

how can i fix this ?
is my code has problem ?
i have to use another module?

According to my research from Google and... the problem is SPI communication and I don't know how to solve this problem.

Thanks.

#include <SPI.h>
#include <CAN.h>
#include <U8g2lib.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>

const int SPI_CS_PIN = 10;
const int INT_PIN = 2;
const int LED_PIN = 6;   // Pin where the NeoPixel is connected
const int NUM_LEDS = 8;  // Number of LEDs in the strip
unsigned int temp= 0;
unsigned char wtemp[20];

U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0);

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

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

  strip.begin();
  strip.show();

  while (!Serial)
    ;
  Serial.println("CAN Receiver");

  // Start the CAN bus at 500 kbps
  if (!CAN.begin(500E3)) {
    Serial.println("Starting CAN failed!");
    while (1)
      ;
  }
}

void loop() {......}

and an oled screen right ?

No it looks fine just as it is.

Here i am not convinced.

The WS2812 libraries are forced to use a bit-bang method on the processor you have, which requires the turning off of interrupts, but so far you have declared the object, initialized it and have done a .show() (which actually temporarily turns of interrupts) , but all before you try to start the CAN bus. So as far as i can tell there should be any interference just yet.

So in this example code, what do you need to comment out to make the CAN bus start properly ?

What if you comment out the oled screen

U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0);
 u8g2.begin();

That might fix things, but let's first investigate what is actually causing the conflict.

1 Like

Compiling your code results in

Sketch uses 14690 bytes (45%) of program storage space. Maximum is 32256 bytes.
Global variables use 1670 bytes (81%) of dynamic memory, leaving 378 bytes for local variables. Maximum is 2048 bytes.
Low memory available, stability problems may occur.

Your code uses the Adafruit NeoPixel library, the memory used for the NeoPixels (8x3 bytes) is not included in the above count. If you use the FastLED library, it will be included and your memory usage will probably show higher.

I would suggest that you post your full code so we can see if there is anything else that consumes memory.

thanks for fast respons

yes i have oled module 0.91inch i2c

i comment the oled codes and just run only Canbus and Ws2812 ::: not work

when i just use oled and canbus works perfectly

when i comment this line,Canbus works fine .
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

even i used another 5v power for Ws2812 but not work

There is nothing in the object declaration that interferes with with anything, other than a small amount of memory on the heap for the output buffer (8 leds x 3 bytes + a few other variables) No other resources are used, no timers nothing. It doesn't make sense.

here is my full code

for clarify:
i use this code for calculater RPM first and it works
but i need WaterTemp
so i change my code for wtemp and.....

#include <SPI.h>
#include <CAN.h>
#include <U8g2lib.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>

const int SPI_CS_PIN = 10;
const int INT_PIN = 2;
const int LED_PIN = 6;   // Pin where the NeoPixel is connected
const int NUM_LEDS = 8;  // Number of LEDs in the strip
unsigned int RPM = 0;
unsigned char rpmStr[20];

U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0);

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

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

  strip.begin();
  strip.show();

  while (!Serial)
    ;
  Serial.println("CAN Receiver");

  // Start the CAN bus at 500 kbps
  if (!CAN.begin(500E3)) {
    Serial.println("Starting CAN failed!");
    while (1)
      ;
  }
}

void loop() {

  int packetSize = CAN.parsePacket();
  if (packetSize) {
    unsigned long packetId = CAN.packetId();

    if (packetId == 0x1c1) {
      Serial.print("Received packet with id 0x");
      Serial.println(packetId, HEX);

      byte buf[8];
      int len = 0;
      while (CAN.available() && len < 8) {
        buf[len++] = CAN.read();
      }

      int RPM = buf[2] * 256 + buf[3];
      Serial.println(RPM);

      // Display RPM on OLED
      u8g2.clearBuffer();
      u8g2.setFont(u8g2_font_luIS14_te);
      itoa(RPM, rpmStr, 10);
      u8g2.drawStr(5, 28, rpmStr);
      u8g2.sendBuffer();

      // Update NeoPixels
        ///code for LED ----- W.I.P

      delay(1);
    }
  }
}


Sketch uses 27494 bytes (85%) of program storage space. Maximum is 32256 bytes.
Global variables use 1740 bytes (84%) of dynamic memory, leaving 308 bytes for local variables. Maximum is 2048 bytes.

I think that you're simply running out of memory and need a microcontroller with more memory; I'm not familiar with OLEDs and the lib that you use, maybe there is a more 'economic' library or this might already be the most economic one.

You will need to add to the reported usage
24 bytes for the strip
15 bytes (max) for an ISR that can happen at any time.

I'm not the specialist in memory usage but the CAN.begin() (see mcp2515.cpp) uses a local table with 24 entries of 11 bytes. You will also have to add that to the memory usage at the moment when that method is called. So you are skating on very, very thin ice as far as I can see.

You can strip that table in the library and only leave the two lines for your selected baud rate. This might give you enough memory to get through the setup() function; it might not be sufficient to run your complete code.

Note:
The library that I used and looked at is the one by Sandeep Mistry (GitHub - sandeepmistry/arduino-CAN: An Arduino library for sending and receiving data using CAN bus.), installed through library manager.
If you use another one, you must let me know which one.

1 Like

The used library is the one you mentioned and I research more about memory management.
can i add a memory to my microcontroller?

No but you can get a MCU with more RAM.

1 Like

Project Update:
I replaced the U8g2 library with Adafruit_SSD1306 and it works perfectly fine.

As you mentioned, I simply ran out of memory, and by replacing the library with a more economical one, the problem was solved.

Thanks.

Can you show your revised code?

yes sure !

This is the code for the RPM and RaceCar style Shift Light :
The Shift Light is for testing (I have a stock car).
However, I have an issue with my car's water temperature. Even though I have replaced many parts, the problem still persists.

While working on the temperature monitor, I also worked on the shift light because why not! XD

I hope this code helps car guys with their future car projects.

#include <SPI.h>
#include <CAN.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1        // Reset pin (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C  // Most common I2C address for SSD1306

const int SPI_CS_PIN = 10;
const int INT_PIN = 2;
const int LED_PIN = 6;   // Pin where the NeoPixel is connected
const int NUM_LEDS = 8;  // Number of LEDs in the strip
int MaxRpm = 5000;


char rpmStr[6];  // Smaller buffer, RPM values won't exceed 5 characters plus null terminator

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

const uint32_t RED = strip.Color(255, 0, 0);
const uint32_t colorPattern[NUM_LEDS] = {
  strip.Color(0, 0, 255),    // Blue
  strip.Color(255, 0, 255),  // Purple
  strip.Color(255, 0, 255),  // Purple
  strip.Color(255, 0, 0),    // Red
  strip.Color(255, 0, 0),    // Red
  strip.Color(255, 0, 255),  // Purple
  strip.Color(255, 0, 255),  // Purple
  strip.Color(0, 0, 255)     // Blue
};

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

  strip.begin();
  strip.show();

  while (!Serial)
    ;
  Serial.println(F("CAN Receiver"));  // Store string in flash memory

  // Start the CAN bus at 500 kbps
  if (!CAN.begin(500E3)) {
    Serial.println(F("Starting CAN failed!"));
    while (1)
      ;
  }

  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;
  }

  display.clearDisplay();
  display.setTextSize(3);               // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE);  // Draw white text
  display.setCursor(2, 10);
  display.print(F("SKYSPEC"));  // Print static string from flash
  display.display();
}

void loop() {
  static unsigned long lastFlashTime = 0;
  static bool flashState = false;
  int packetSize = CAN.parsePacket();
  if (packetSize) {
    unsigned long packetId = CAN.packetId();

    if (packetId == 0x1c1) {
      Serial.print(F("Received packet with id 0x"));
      Serial.println(packetId, HEX);

      byte buf[8];
      int len = 0;
      while (CAN.available() && len < 8) {
        buf[len++] = CAN.read();
      }

      int RPM = buf[2] * 256 + buf[3];
      Serial.println(RPM);

      // Display RPM on OLED
      itoa(RPM, rpmStr, 10);
      
      display.clearDisplay();
      display.setTextSize(2);               // Normal 1:1 pixel scale
      display.setTextColor(SSD1306_WHITE);  // Draw white text
      display.setCursor(12, 12);
      display.print(F("RPM: "));  // Print static string from flash
      display.print(rpmStr);      // Print the RPM value
      display.display();

      // Update NeoPixels
      strip.clear();
      if (RPM > MaxRpm) {
        // Flash all LEDs red
        unsigned long currentTime = millis();
        if (currentTime - lastFlashTime >= 50) {  // Change the flash interval as needed
          flashState = !flashState;
          lastFlashTime = currentTime;
        }
        uint32_t color = flashState ? RED : strip.Color(0, 0, 0);  // Toggle between red and off
        for (int i = 0; i < NUM_LEDS; i++) {
          strip.setPixelColor(i, color);
        }
      } else if (RPM >= 2300 && RPM <= MaxRpm) {
        // Calculate which LEDs to light up based on RPM
        int ledIndex = map(RPM, 2300, MaxRpm, 0, (NUM_LEDS / 2) - 1);

        // Update LEDs based on RPM
        for (int i = 0; i <= ledIndex; i++) {
          strip.setPixelColor(i, colorPattern[i]);                 // Set the color from the pattern (beginning)
          strip.setPixelColor(NUM_LEDS - 1 - i, colorPattern[i]);  // Set the color from the pattern (end)
        }
      }
      strip.show();
    }
  }
}

I suspect that this works because the Adafruit SSD1306 library uses dynamic memory allocation.
So the needed memory for the display is only allocated after the CAN is initialised.

Does it still work if you move the display.begin() section to before the CAN.begin() ? Or do you get the same issue again.

No help but …I had a similar issue with an Esp32 s3 wroom with the 2515 canbus board and tft both on SPI . ( minimal code, level shifters etc) ) Several other stories on the net .
I never resolved and went down a different route .
Maybe a library conflict , or one device not tri stating ??
( odd things such as first started would work , either way around ).

yeh i moved the display commands .
to befor CAN.begin()
and works fine.
New Sketch:

Sketch uses 20700 bytes (64%) of program storage space. Maximum is 32256 bytes.
Global variables use 996 bytes (48%) of dynamic memory, leaving 1052 bytes for local variables. Maximum is 2048 bytes.

Old Sketch:

Sketch uses 27494 bytes (85%) of program storage space. Maximum is 32256 bytes.
Global variables use 1740 bytes (84%) of dynamic memory, leaving 308 bytes for local variables. Maximum is 2048 bytes.

Thanks; I assume that the new one is using the Adafruit library and the old one is using the U2G2lib.

If you moved the display.begin() in the new sketch to before CAN.begin(), I would expect the result to be an error in the CAN.begin(); but OK, compilers are clever and might reshuffle stuff.

Glad it's sorted.

1 Like