Arduino Uno 3, CANBUS (MCP2515) & 2 x 12864 OLED Screens

Hello,

I’m putting together a car (Mk1 Audi TT) water-methanol controller which also displays the RPM, Speed, Boost Pressure, IAT and records the maximum for display later (there is also as set of LED’s to indicate when to change gear). The RPM & Speed come from the CANBUS but the Boost Pressure & IAT have to come from a Bosch sensor as these are not transmitted over the CANBUS.

I’m using a MCP2515 CANBUS module and 2 x 12864 OLED Screens to display the data gathered, if I use a potentiometer to simulate the RPM & Speed and exclude MCP2515 CANBUS code it all works as expected but once I use the MCP2515 CANBUS code (obviously removing the potentiometer code) the RPM & Speed are not displayed nor do the gear change lights work. If I use the MCP2515 CANBUS code on it’s own displaying RPM & Speed to serial and lighting the required gear change lights it works fine.

I’ve done some debugging and found that it appears to be something to do with the buffering (over writing or running out), so my queries are,

  • By using MCP2515 & 2 x 12864 OLED Screens am I just getting to the limit of the Arduino Uno 3 hardware\buffer capabilities?
  • At present I’m receiving all the CANBUS messages, if I filter these down to the 2 ID’s I require for the RPM & Speed could it solve my problem? (I’ve tried filtering the CANBUS messages before but couldn’t get it to work)
  • I’m using the Adafruit_GFX.h & Adafruit_SSD1306.h libraries for the OLED Screens, is there another way to code these screens which might help?
  • By rearranging the current code could the problem be resolved as I have the CANBUS code at the start of the loop & the display code at the bottom of the loop.

Any ideas or experience most appreciated.

Thanks

PS

I know the code is not the greatest piece of work and there are probably economies that can be made, so don’t be too hard on me (it’s a hobby not my profession).

//CanBus
#include <mcp_can.h>
#include <SPI.h>


int aRPM,bRPM,RPM,Max_RPM = 0;

long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
bool init_state = true;

#define CAN0_INT 2                              // Set INT to pin 2
MCP_CAN CAN0(10);                               // Set CS to pin 10

//OLED
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
Adafruit_SSD1306 display1(OLED_RESET);
Adafruit_SSD1306 display2(OLED_RESET);

char message[] = "297M LstWyPt, 345M StPt, rec#89";
char printBuffer[128];
int x, minX;

// Boost Pressure and IAT
// int offset = 125; // low pressure calibration
// int span = 895; // high pressure calibration
int offset = 111; // low pressure calibration
int span = 912; // high pressure calibration
float Boost_pressure_Pa;
float Boost_pressure_kPa;
float Boost_pressure_PSI;
int boost_pressure_pin = A3;
int Max_PSI = 0;

int IAT_voltage = 0;
float IAT_Vin = 4.6; // 4.6 volts - USB powered
float IAT_Vout = 0;
float R1 = 2000; // Temperature resistor value
float IAT_buffer = 0;
float logR2;
float R2;
float IAT;
int IAT_pin = A1;
float IAT_C = 0;
float Max_IAT = 0;

// float A = 1.297373711e-03, B = 2.587931666e-04, C = 1.639343214e-07;  // Steinhart-Hart and Hart Coefficients Max
// float A = 1.306496435e-03, B = 2.580557547e-04, C = 1.752303942e-07;  // Steinhart-Hart and Hart Coefficients Nominal
float A = 1.316012871e-03, B = 2.572431200e-04, C = 1.877359037e-07;  // Steinhart-Hart and Hart Coefficients Min




float Voltage = 1.00;



//Change lights
int numLEDSLit = 1;
int latchPin = 8;      // (8) ST_CP [RCK] on 74HC595
int clockPin = 5;      // (5) SH_CP [SCK] on 74HC595
int dataPin = 3;     // (3) DS [S1] on 74HC595
int leds = 0;

//Speed
int Speed = 0;
int Max_Speed = 0;

//W-M
int Max_WM = 0;
int WM_value_percent = 1;
int WM_pin = 6;
int WM_value_T;
int WM_value_P;
int WM_value;
//int WM_Level_pin = A2;
int WM_Level = 101;

//History
int HistoryValue = 0;
int Max_Counter = 1;
int Max_Scroll = 4;
int Max_Reset = 7;



void setup() {
  Serial.begin(115200);
  display1.begin(SSD1306_SWITCHCAPVCC, 0x3D);
  display1.setTextSize(2);
  display1.setTextColor(WHITE);
  display1.setTextWrap(false);
  x = display1.width();
  minX = -12 * strlen(message);  // 12 = 6 pixels/character * text size 2
  display2.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display2.setTextSize(2);
  display2.setTextColor(WHITE);
  display2.setTextWrap(false);
  x = display2.width();
  minX = -12 * strlen(message);  // 12 = 6 pixels/character * text size 2
  pinMode(WM_pin, OUTPUT);


  // Initialize MCP2515 running at 8MHz with a baudrate of 500kb/s and the masks and filters disabled.
  if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) == CAN_OK) Serial.println("MCP2515 Initialized Successfully!");
  else Serial.println("Error Initializing MCP2515...");
  if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) == CAN_OK)
    Serial.println("MCP2515 Initialized Successfully!");
  else
    Serial.println("Error Initializing MCP2515...");

  CAN0.setMode(MCP_NORMAL);                     // Set operation mode to normal so the MCP2515 sends acks to received data.

  pinMode(2, INPUT);                            // Setting pin 2 for /INT input
  pinMode(CAN0_INT, INPUT);                            // Configuring pin for /INT input

  //Change lights
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);  
  pinMode(clockPin, OUTPUT);
 //Reset & Scroll
  pinMode(Max_Reset, INPUT_PULLUP); 
  pinMode(Max_Scroll, INPUT_PULLUP);  
}
  //Change lights
void updateShiftRegister()
{
   digitalWrite(latchPin, LOW);
   shiftOut(dataPin, clockPin, MSBFIRST, leds);
   digitalWrite(latchPin, HIGH);
}


void loop() {


    if (digitalRead(Max_Scroll)== LOW){
      delay (150);
    
   if (digitalRead(Max_Scroll)== LOW){
   Max_Counter = Max_Counter +1;
}}

    if(Max_Counter >5) Max_Counter = 1;
    
  if (digitalRead(Max_Reset) == LOW)
  {
    Max_Speed = 0;
    Max_RPM = 0;
    Max_IAT = 0;
    Max_WM = 0;
    Max_PSI = 0;
  }

if(!digitalRead(2))                    // If pin 2 is low, read receive buffer

{

      CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s)

      Serial.print("ID: ");
      Serial.print(rxId, HEX);
      Serial.print(" Data: ");
      for(int i = 0; i<len; i++)           // Print each byte of the data
      {
        if(rxBuf[i] < 0x10)                // If data byte is less than 0x10, add a leading zero
        {
          Serial.print("0");
        }
        Serial.print(rxBuf[i], HEX);
        Serial.print(" ");

      }

    }
    


          if(rxId == 0x280){




            aRPM = rxBuf[3] * 256;
            bRPM = aRPM + rxBuf[2];
            RPM = bRPM * 0.25;  

          if (RPM > Max_RPM) Max_RPM = RPM;
 
        
        Serial.println();
                    Serial.print("RPM: ");
          Serial.println(RPM, DEC);

                    Serial.print("Max_RPM: ");
          Serial.println(Max_RPM, DEC);
}
          if(rxId == 0x288){
            
          Speed = (rxBuf[3]*0.759);
//          Serial.print("vehicle Speed (MPH): ");
          Serial.println(Speed);
//          Serial.print("   ");
//          Serial.println(Speed, DEC);
          if (Speed > Max_Speed) Max_Speed = Speed;
//          Serial.print("Max_Speed: ");
          Serial.println(Max_Speed);
          }

// Shift lights not sure how it works

  numLEDSLit = RPM / 1000;
//  Serial.println(numLEDSLit);
  if (numLEDSLit >= 6) numLEDSLit = 8;
  if (numLEDSLit < 1) numLEDSLit = 1;
  leds = 0;   // no LEDs lit to start
  for (int i = 0; i < numLEDSLit; i++)
  {
    leds = leds + (1 << i);  // sets the i'th bit
  }
  updateShiftRegister();
  


// Boost Pressure
  Boost_pressure_Pa = map(analogRead(boost_pressure_pin), offset, span, 500, 4000);
  Boost_pressure_kPa = Boost_pressure_Pa /10, 1;


//  Serial.println("  ");
  if (Boost_pressure_kPa < 20) Boost_pressure_kPa = 20;
  Boost_pressure_PSI = Boost_pressure_kPa * 0.145038;
  Boost_pressure_PSI = Boost_pressure_PSI - 14;
  if (Boost_pressure_PSI < 1) Boost_pressure_PSI = 0;
  if (Boost_pressure_PSI > Max_PSI) Max_PSI = Boost_pressure_PSI;



  if (WM_value_percent > Max_WM) Max_WM = WM_value_percent;

//IAT

  IAT_voltage = analogRead(IAT_pin);
  if (IAT_voltage) {
    IAT_buffer = IAT_voltage * IAT_Vin;
    IAT_Vout = (IAT_buffer) / 1024.0;
    IAT_buffer = (IAT_Vin / IAT_Vout) - 1;
    R2 = R1 / IAT_buffer;

  }

  logR2 = log(R2);
  IAT = (1.0 / (A + B * logR2 + C * logR2 * logR2 * logR2)); // Steinhart and Hart Equation. T  = 1 / {A + B[ln(R)] + C[ln(R)]^3}
  IAT_C =  IAT - 273.15;



// need to edit for sensor pressure
  if (IAT_C >= 27) {
    WM_value_T = map (IAT_C, 27, 40, 0, 255);
    WM_value_P = map(Boost_pressure_PSI, 8, 25, 0, 255);
    if (WM_value_T >= WM_value_P) WM_value = WM_value_T;
    if (WM_value_P >= WM_value_T) WM_value = WM_value_P;
    if (WM_value <= 0) WM_value = 0;
    WM_value_percent = WM_value / 2.5;
    if (Boost_pressure_PSI >= 31) WM_value_percent = 100;
    if (WM_value_percent >= 100) WM_value_percent = 100;
    WM_value = WM_value_percent * 2.55;
    analogWrite(WM_pin, WM_value);
  }
  else
  {
    WM_value_P = map(Boost_pressure_PSI, 8, 25, 0, 255);
    WM_value_T = map (IAT_C, 28, 40, 0, 255);
    if (WM_value_P >= WM_value_T) WM_value = WM_value_P;
    if (WM_value_T >= WM_value_P) WM_value = WM_value_T;
    if (WM_value <= 0) WM_value = 0;
    WM_value_percent = WM_value / 2.5;
    if (Boost_pressure_PSI >= 25) WM_value_percent = 100;
    if (WM_value_percent >= 100) WM_value_percent = 100;
    WM_value = WM_value_percent * 2.55;
    analogWrite(WM_pin, WM_value);
  }
  display1.clearDisplay();
  display1.setTextSize(1); // Draw 2X-scale text
  display1.setTextColor(SSD1306_WHITE);
  display1.setCursor(0, 0);
  // sensor voltage code
  //display1.println(F("SV"));
  display1.println(F("IAT"));
  display1.setCursor(28, 0);
  // sensor voltage code
  //display1.println(Voltage);
  display1.println(IAT_C);

  display1.setCursor(75, 0);
  display1.print(F("PSI"));
  display1.setCursor(99, 0);
  display1.println(Boost_pressure_PSI);
  display1.setCursor(112, 0);
  display1.setTextSize(3); // Draw 2X-scale text
  display1.setTextColor(SSD1306_WHITE);
// WM level low code
//  if (WM_Level <= 460) {
//    display1.setTextSize(2); // Draw 2X-scale text
//    display1.setCursor(0, 17);
//    display1.println(F("W-M"));
//    display1.setTextSize(3); // Draw 2X-scale text
//    display1.setCursor(41, 10);
//    display1.println(F("EMPTY"));
//  }
//  else {
//    display1.setTextSize(2); // Draw 2X-scale text
//    display1.setCursor(0, 17);
//    //display1.print(F("PSI"));
//    display1.println(F("W-M"));
//    display1.setTextSize(3); // Draw 2X-scale text
//    display1.setCursor(50, 10);
//    //display1.println(Boost_pressure_PSI);
//    display1.println(WM_value_percent);
//    display1.setCursor(113, 10);
//    display1.println(F("%"));
//  }
    display1.setTextSize(2); // Draw 2X-scale text
    display1.setCursor(0, 17);
    //display1.print(F("PSI"));
    display1.println(F("W-M"));
    display1.setTextSize(3); // Draw 2X-scale text
    display1.setCursor(50, 10);
    //display1.println(Boost_pressure_PSI);
    display1.println(WM_value_percent);
    display1.setCursor(113, 10);
    display1.println(F("%"));

  display2.clearDisplay();
  // Here as it reads 0 when with the rest of the max readings??
  if (IAT_C > Max_IAT) Max_IAT = IAT_C;
  
  if (Max_Counter == 1) {
  display2.clearDisplay();
  display2.setTextSize(3); // Draw 2X-scale text
  display2.setTextColor(SSD1306_WHITE);
  display2.setCursor(50, 11);
  display2.println(Max_Speed);
  display2.setTextSize(1); // Draw 2X-scale text
  display2.setCursor(15, 12);
  display2.println(F("MAX"));
  display2.setCursor(15, 25);
  display2.println(F("MPH"));
  }
  if  (Max_Counter == 2) {
  display2.clearDisplay();
  display2.setTextSize(3); // Draw 2X-scale text
  display2.setTextColor(SSD1306_WHITE);
  display2.setCursor(50, 11);
  display2.println(Max_RPM);
  display2.setTextSize(1); // Draw 2X-scale text
  display2.setCursor(15, 12);
  display2.println(F("MAX"));
  display2.setCursor(15, 25);
  display2.println(F("RPM"));
  }
  if (Max_Counter == 3) {
  display2.clearDisplay();
  display2.setTextSize(3); // Draw 2X-scale text
  display2.setTextColor(SSD1306_WHITE);
  display2.setCursor(40, 11);
  display2.println(Max_IAT);
  display2.setTextSize(1); // Draw 2X-scale text
  display2.setCursor(15, 12);
  display2.println(F("MAX"));
  display2.setCursor(15, 25);
  display2.println(F("IAT"));
  }
  if (Max_Counter == 4) {
  display2.clearDisplay();
  display2.setTextSize(3); // Draw 2X-scale text
  display2.setTextColor(SSD1306_WHITE);
  display2.setCursor(50, 11);
  display2.println(Max_PSI);
  display2.setTextSize(1); // Draw 2X-scale text
  display2.setCursor(15, 12);
  display2.println(F("MAX"));
  display2.setCursor(15, 25);
  display2.println(F("PSI"));
  }
  if (Max_Counter == 5) {
  display2.clearDisplay();
  display2.setTextSize(3); // Draw 2X-scale text
  display2.setTextColor(SSD1306_WHITE);
  display2.setCursor(50, 11);
  display2.println(Max_WM);
  display2.setTextSize(1); // Draw 2X-scale text
  display2.setCursor(15, 12);
  display2.println(F("MAX"));
  display2.setCursor(15, 25);
  display2.println(F("W-M"));
  }
  

if (Max_Counter == 1) {
     drawProgressbar(0,0,108,10, Speed / 1.5  );
    display2.setCursor(110,2);
    display2.setTextSize(1);
    display2.setTextColor(WHITE);
    display2.print("MPH");
    }
if (Max_Counter == 2) {
     drawProgressbar(0,0,108,10, RPM / 75  );
    display2.setCursor(110,2);
    display2.setTextSize(1);
    display2.setTextColor(WHITE);
    display2.print("RPM");
    }
if (Max_Counter == 3) {
     drawProgressbar(0,0,108,10, IAT_C / 0.5  );
    display2.setCursor(110,2);
    display2.setTextSize(1);
    display2.setTextColor(WHITE);
    display2.print("IAT");
    }
if (Max_Counter == 4) {
     drawProgressbar(0,0,108,10, Boost_pressure_PSI / 0.32  );   
    display2.setCursor(110,2);
    display2.setTextSize(1);
    display2.setTextColor(WHITE);
    display2.print("PSI");
    }
if (Max_Counter == 5) {   
   drawProgressbar(0,0,108,10, WM_value_percent);
       display2.setCursor(110,2);
    display2.setTextSize(1);
    display2.setTextColor(WHITE);
    display2.print("W-M");
}

  
  display2.display();      // Show initial text
  display1.display();      // Show initial text
  //delay(100);
}

void drawProgressbar(int x,int y, int width,int height, int progress)
{

   progress = progress > 100 ? 100 : progress;
   progress = progress < 0 ? 0 :progress;

   float bar = ((float)(width-1) / 100) * progress;

   display2.drawRect(x, y, width, height, WHITE);
   display2.fillRect(x+2, y+2, bar , height-4, WHITE);

//     if( progress <50){
//    display2.setCursor(64,0);
//    display2.setTextSize(1);
//    display2.setTextColor(WHITE);
//    display2.print(RPM);
//    display2.print("RPM");
//     }
// else{
//      display2.setCursor(64,0);
//     display2.setTextColor(BLACK, WHITE); // 'inverted' text
// 
//     display2.print(RPM);
//     display2.print("RPM");
// }
  
  // Scroll in various directions, pausing in-between:
  //  display1.startscrollright(0x00, 0x0F);
  //  delay(2000);
  //  display1.stopscroll();
  //  delay(1000);
  //  display1.startscrollleft(0x00, 0x0F);
  //  delay(10500);
  //  display.stopscroll();
  //  delay(1000);
  //  display.startscrolldiagright(0x00, 0x07);
  //  delay(2000);
  //  display.startscrolldiagleft(0x00, 0x07);
  //  delay(2000);
  //  display.stopscroll();
  //  delay(1000);

// Shift lights not sure how it works
//  int numLEDSLit = RPM / 937;  //1023 / 9 / 2
//  if (numLEDSLit >= 6) numLEDSLit = 8;
//  if (numLEDSLit < 1) numLEDSLit = 1;
//  leds = 0;   // no LEDs lit to start
//  for (int i = 0; i < numLEDSLit; i++)
//  {
//    leds = leds + (1 << i);  // sets the i'th bit
//  }
//  updateShiftRegister();
//Serial.println(Max_Counter);
//Serial.println(MaxIAT);
//Serial.println(IAT_C);
}

I suspect you are hitting the limits. With that library I think it will reserve 1K of RAM per display. Uno has only 2K in total, and some of that is reserved.

You might look at U8G2 library as an alternative. It uses significantly less but the penalty is that updates can be slower, and your code will need some restructuring, because each screen has be re-drawn several times to update the entire screen.

Thanks,

Humm,

That's a pain, I'll give the CANBUS filter another go as I can see some of the CANBUS messages displayed on the serial and the RPM registers on the OLED every 30 seconds or so. Failing that I've got a DWIN 4.3 Inch HMI touch screen in the box sitting on my desk which I can use instead of the OLED's, as it only requires an RX & TX it should resolve the Arduino hardware buffering\resource issues. Just need to work out how to implement it.