CANBUS & 1.3" OLEDs - slow running

Hi,
I have an Uno receiving data from an Engine ECU via a CANBUS shield and I now want to output that data onto some OLEDs. I am using the excellent u8glib library for the displays and have put together a sketch which gets me the data that I want, however, something about it is slowing the CANBUS comms down! This means that the displays are sometimes taking half a minute or more to update to newer information.

I have done some investigating using the serial monitor and have established that it's not the displays that are at fault - as soon as they get the new info, they display it. It's just that the new info is slow to arrive from the CANBUS. Also, by running just the CANBUS code, the RX led on the shield blinks quickly and I get new data in straight away at the serial monitor, indicating that it's working correctly. When trying to run the CANBUS & OLED codes combined, the RX LED is on solid (I suspect flashing too fast for my eyes to detect) and the serial monitor is slow to scroll, with the data taking a long time to update.

A snippet of the code:

void ct() {
  u8g.setFont(u8g_font_helvB24n);
  u8g.setScale2x2();
  u8g.setPrintPos( 0, 24);
  u8g.print(CT);
  u8g.undoScale();
  u8g.setFont(u8g_font_helvB10);
  u8g.setPrintPos( 0, 61);
  u8g.print("Coolant");
  u8g.setPrintPos( 112, 61);
  u8g.print((char)176);
  u8g.setPrintPos( 118, 61);
  u8g.print("C");
}

void at() {
  u8g.setFont(u8g_font_helvB24n);
  u8g.setScale2x2();
  u8g.setPrintPos( 0, 24);
  u8g.print(AT);
  u8g.undoScale();
  u8g.setFont(u8g_font_helvB10);
  u8g.setPrintPos( 0, 61);
  u8g.print("Intake");
  u8g.setPrintPos( 112, 61);
  u8g.print((char)176);
  u8g.setPrintPos( 118, 61);
  u8g.print("C");
}
void draw_gauge1(void) {
  switch(gauge1counter) {
    case 0: boost(); break;
    case 1: tps(); break;
    case 2: batt(); break;
    case 3: op(); break;
    case 4: ct(); break;
    case 5: at(); break;
    case 6: ot(); break;
}
}void picture1()  
{  
  u8g.firstPage();  
  do {
    draw_gauge1();
  } while( u8g.nextPage() );
}void counters(void) {
  int reading1 = digitalRead(19);
  if (reading1 != lastgauge1ButtonState) {
    lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading1 != gauge1ButtonState) {
      gauge1ButtonState = reading1;
      if (gauge1ButtonState == HIGH) {
      gauge1counter++;
      if (gauge1counter >=7)
      gauge1counter = 0;
    }
  }
  }
  lastgauge1ButtonState = reading1;
  }

void setup(void) {
  pinMode(18, INPUT);
  pinMode(19, INPUT);
  CAN0.begin(CAN_1000KBPS);
  Serial.begin(115200);
  pinMode(2, INPUT);
  // flip screen, if required
  // u8g.setRot180();
}

void loop(void) {
    if(!digitalRead(2))
    {
      CAN0.readMsgBuf(&len, rxBuf);
      rxId = CAN0.getCanId();
     }
     if (rxId == 4096) {
   RPM1 = (rxBuf[0] * 100);
   RPM2 = (rxBuf[1]);
   MAP1 = (rxBuf[2] * 256);
   MAP2 = (rxBuf[3]);
   OP1 = (rxBuf[4] * 100);
   OP2 = (rxBuf[5]);
   TPS = (rxBuf[6]);
   }
   if (rxId == 4097) {
   AFR1 = (rxBuf[4]);
   AFR2 = (rxBuf[5]);
   }
   if (rxId == 4099) {
   AT = (rxBuf[0] - 40);
   CT = (rxBuf[1] - 40);
   OT = (rxBuf[2] - 40);
   BAT = (rxBuf[7] / 11.0);
   }
   RPM = RPM1 + RPM2;
   MAP = (MAP1 + MAP2) - 1013;
   Boost = MAP * 0.0145;
   Vac = (MAP * 0.0295);// - 29.92;  
 
  {
   picture1();
    counters();
  } 
}

If I comment out picture1(); near the bottom then the CANBUS code alone runs fine. I actually have 7 data sets that I want to display and am also using 2 OLEDs. They control independently of each other OK and toggle through the 7 different options instantaneously when the appropriate button is pressed. I didn't include every last bit of my sketch as I thought it would just make an unnecessary mess of duplicated code!

So to sum up, the CANBUS part is fine on it's own and the OLEDs display correctly on their own, but my attempt to amalgamate the two results in the CANBUS running slow - can anyone see where the problem lies? Many thanks!

Hmmm... I've got it working, to some degree. I've change the bottom of the sketch to:

{
     counters();
   }
  
    if(BAT != lastBAT || MAP != lastMAP || TPS != lastTPS || /*OP != lastOP ||*/ OT != lastOT || CT != lastCT || AT != lastAT || 
       gauge1counter != lastgauge1counter || gauge2counter != lastgauge2counter) {
    picture1();
    picture2();
  }
   lastBAT = BAT;
   lastMAP = MAP;
   lastTPS = TPS;
   //lastOP = OP;
   lastOT = OT;
   lastCT = CT;
   lastAT = AT;
   lastgauge1counter = gauge1counter;
   lastgauge2counter = gauge2counter;
}

So it now only re-draws the displays when there has been a change to any of the values or if I've pressed one of the buttons to scroll through the different options. At the moment, the only value that is changing is BAT (battery voltage) and it's updating fine. BUT, something tells me that this isn't the most elegant or proper solution. When the engine is actually running and lots of values are changing I can see me running back into the same problem!

If there's a better way of doing it please let me know :slight_smile:

Hi

My suggestion is to create an independent function, which reads the CAN bus and updates the variables. The idea is, that this function can be called any time from anywhere in the program:

void readCAN(void) {
  // 1. read buf from can
  // 2. store value if required
}

This function can be mixed up with the picture draw function:

void picture1()  
{  
  u8g.firstPage();  
  do {
    draw_gauge1();
    readCAN();
  } while( u8g.nextPage() );

However, variables should not get changed during the draw_gauge1(). So, readCAN() should read to some buffer variables first and you need another function, which copies these buffer variables to the variables, which are used for the display:

void copyToDisplayVariables(void) {
  // ...
}

So, finally the picture1() procedure will look like this:

void picture1()  
{  
  copyToDisplayVariables();
  u8g.firstPage();  
  do {
    draw_gauge1();
    readCAN();
  } while( u8g.nextPage() );

Oliver

That's more like it! Thank you for your reply Oliver (and of course for the library!) Both displays now work beautifully and the RX led on the CAN shield has a nice steady blink like it did when running on it's own. My attempt at a fix would make the led "hang" briefly as values changed then continue blinking again once they were steady.

One quick question though, in this bit:

void copyToDisplayVariables(void) {
  // ...
}

Was I supposed to use it exactly as-is, or replace the comment with my variables such as BAT, MAP, TPS etc? The reason I ask is that I loaded it as-is just to see what happened and it worked! Which has confused me slightly (easily done!) so I wondered if that was your intention or not?

Thanks again for your help :slight_smile:

Hi

My idea was to have every variable twice. For example with BAT: Instead of BAT, i suggest to have BAT_from_CAN and BAT_for_OLED.
So, in the three functions:

  1. store the value from the CAN in BAT_from_CAN
  2. copy value BAT_from_CAN in BAT_for_OLED
  3. display the value in BAT_for_OLED

so, the copy function will look like this:

void copyToDisplayVariables(void) {
  BAT_for_OLED = BAT_from_CAN;
  //... similar for other variables
}

Reason is, the values should not get changed during the u8glib while loop.
However, without this double buffering of your variable, the only impact might be artefacts on the screen.

Oliver