Teensy 3.2 - Conflict between FLEXCAN and FASTLED libraries ?

Status
Not open for further replies.
I have a project that used to run on an Arduino Nano that reads CAN Bus with MCP2515+TJA1050 module using MCP2515 library, show CAN data on a 128x64 SSD1306 OLED display over hardware SPI using U8x8lib library, and drive a WS2812 RGB LED ring using FASTLED library. It works fine but drops 20% of CAN data due to its slow Arduino speed, and refreshing the SSD1306 is too slow (I want to add graphics later using U8G2 library, which requires more throughput).

I converted it to run on a Teensy 3.2
It now use the Teensy internal CAN controller by using collin80 FlexCAN library instead of MCP2515, and a simple SN65HVD230 CAN transceiver. Everything else stays the same.
It works fine (almost), and drop absolutely no CAN packets and refresh the SSD1306 display over 20X time faster than with the Nano.

However the WS2812 LED ring has now some LEDs randomly flickering all the time.
I read about FASTLED interrupt issue here, and understand it could be the cause :
https://github.com/FastLED/FastLED/wiki/Interrupt-problems

I tried to add #define FASTLED_INTERRUPT_RETRY_COUNT 1 but it doesn't make any difference (no success).
I treid to add #define FASTLED_ALLOW_INTERRUPTS 0, it fix the flickering problem (no more LED flickering), but now Teensy randomly freeze anytime between a few seconds to a few hours after start (but typically in only 2-3 minutes). I need to cycle the power to restart it.

Can anybody direct me to the right direction to fix this problem ? Thx.

Teensy pin used are:
SSD1306 (Hardware SPI)
D0 SCK 13
D1 MOSI 11
RES RST 8
DC AO 6
CS SS 7
WS8212 Leds
Pin 2
CAN Bus
TX 3
RX 4
 
Most of the WS2812B libraries disable interrupts for a long period of time in order to get the timing right. This can play havoc with doing external communications or getting the pulses correct for servos.

Assuming you have one of the serial UART pins free (1, 5, 8, 10, 20, 31) you can use WS2812Serial:

I haven't used it myself, but it may be worth a try.
 
you can try a new development flexcan library that mimics mcp_can with advanced features, and perhaps report if it works for you, it’s called IFCT, I have an update comming either today or tomorrow which will allow more special unique features
 
Last edited:
tonton81's new lib is essentially a drop in replacement for using mcp_can library. you just reference it the same way with the same library name. You may want to check out this post https://forum.pjrc.com/threads/5290...Teensy-Library?p=183467&viewfull=1#post183467 and the following post.

Really no need to do any conversions. Use the same code as you use on the nano, cs pin designator is really a dummy just for consistency, delete any mcp interrupt code and just use checkAvailable instead of testing on the interrupt. Correct me if I am wrong tonton
 
As fas as WS2812Serial goes, I really taught it would have solved my problem by reading this;
https://www.pjrc.com/non-blocking-ws2812-led-library/
Unfortunately it didn't succeed. It works fine without flickering, but still randomly freeze.

And for IFCT, I couldn't get it to compile without errors, I could only create new errors as I try to solved previous ones...
I'll need a bit more guidance to make it work, there's no wiki/tutorial/instructions on github, and few minimal examples on the forum thread you refered me to.
I guess don't have enough programming knowledge to make it through... Some help would be welcome. Thx.
 
Very kind from you to offer.
Here's the nano code;

Code:
/*
IO PinOut
SSD1306 OLED Display SPI PinOut for Arduino Nano
D0	SCK		13
D1	MOSI	11
RES	RST		9
DC	AO		8
CS	SS		7
Bouton TTP223	6
WS2812 RGB LED Ring = 3
MCP2515 CAN Ctrl SPI Module PinOut
INT			2
SCK	SCK		13
SI	MOSI	11
SO	MISO	12
CS	SS		10
120ohm jumper must be IN
*/
const int buttonPin = 6; 
bool buttonState, prevButtonState, timeToParty;
int page = 0;
long scanTime, lastUpdate, maxUpdate, pressTime, PBduration;
int nbCAN_Totalmsg, nbCAN_Tachmsg, nbCAN_Gearmsg, nbCAN_Wheelmsg;

// --------------- Chrono Instances
#include <Chrono.h>              // https://github.com/SofaPirate/Chrono
Chrono ChronoFlashLent;             // pour flasher quand moteur éteint ou pas de CAN BUS
Chrono ChronoUpdateLCD;           // pour refresh du LCD
Chrono ChronoUpdateScroll;          // pour refresh du texte scrollant
Chrono ChronoUpdateFastLed;         // pour refresh du Tach avec des leds RGB
Chrono ChronoCANStatRefresh;        // pour refresh du calcul de statistique de frames/secondes
// --------------- SSD1306 Display variables 
#include <Arduino.h>
#include <U8x8lib.h>      //  https://github.com/olikraus/u8g2
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE);  // en I2c
// U8X8_SSD1306_128X64_NONAME_4W_HW_SPI u8x8(7/*CS*/, 8/*DC*/, 9/*RES*/ );  // En SPI
String Title = "  BRZ Tuning 2018/06/25  ";
String espace = "    ";
String temp(4);
byte span = Title.length() -16;  // nb de caractere qui dépasse le display
byte x = 0, step = 1;
// --------------- CAN BUS variables 
#include <Canbus.h>     //  https://github.com/sparkfun/SparkFun_CAN-Bus_Arduino_Library
#include <defaults.h>
#include <global.h>
#include <mcp2515.h>
#include <mcp2515_defs.h>
bool CANstatus, prevCANstatus;
long CANupdate, CANmsg_TimeStamp;
// --------------- FAST LED variables 
#include "FastLED.h"    //  https://github.com/FastLED/FastLED
#define NUM_LEDS 16
#define DATA_PIN 3
CRGB leds[NUM_LEDS];
CRGB Rleds[NUM_LEDS];     // Reverse leds array
bool FlasherRapide, FlasherLent;
// --------------- BRZ speed variables  
int EngSpd;
byte Gear, Throttle, AccPedal;
String GearStr(4);  // String de 4 caracteres
int AvGa, AvDr, ArGa, ArDr, Steering;
int wheelSpd [4], deltaSpd, maxSpd, minSpd;
bool driftStatus, prevDriftStatus;
long driftStartTime, driftDuration;
//--------------------------------------------------------------------
void sort(int a[], int size) {
  for(int i=0; i<(size-1); i++) {
    for(int o=0; o<(size-(i+1)); o++) {
      if(a[o] > a[o+1]) {
        int t = a[o];
        a[o] = a[o+1];
        a[o+1] = t;
      }
    }
  }
}
//---------------------------------------------------------------
void fadeall() { for(int i = 0; i < NUM_LEDS; i++) { leds[i].nscale8(250); } }
//--------------------------------------------------------------------
void cylon() {
  static uint8_t hue = 0;
  for(int i = 0; i < NUM_LEDS; i++) {
    Rleds[i] = CHSV(hue, 255, 96);
    hue = hue + 7;
    FastLED.show(); 
    fadeall();
    delay(10);
  }
  for(int i = (NUM_LEDS)-1; i >= 0; i--) {
    Rleds[i] = CHSV(hue, 255, 96);
    hue = hue + 7;
    FastLED.show();
    fadeall();
    delay(10);
  }
}
//--------------------------------------------------------------------
String int_to_str (int x) {  // conversion int a string de 4 caracteres
  String inStr;
  inStr = String(x);    
  while (inStr.length() < 4)  
  {
    inStr = " " + inStr;
  }
  return inStr;
}
//--------------------------------------------------------------------
String int_to_str3D1D (int x) {  // conversion int a string de 3 chiffres + 1 décimale
  String inStr;
  inStr = String(x);    
  while (inStr.length() < 4)  
  {
    inStr = " " + inStr;
  }
  String outStr = inStr.substring(0,3) + "." + inStr.substring(3,4); 
  return outStr;
}
//--------------------------------------------------------------------
void reverseLeds() {   // Reverse order of led array for clockwise tach effect
  uint8_t left = 0;
  uint8_t right = NUM_LEDS-1;
  while (left < NUM_LEDS) {
    Rleds [left++] = leds[right--];
  }
}
void defaultDisplay(){      // draw default display (page 0)
  u8x8.clearDisplay();
  u8x8.drawString(0,1,"  CANBUS TACH  ");
  u8x8.drawString(0,2,"Gear");
  u8x8.drawString(0,3,"Tach(rpm)");
  u8x8.drawString(0,4,"Av Gauche");
  u8x8.drawString(0,5,"Av Droite");
  u8x8.drawString(0,6,"Ar Gauche");
  u8x8.drawString(0,7,"Ar Droite");
}
void CANBusAnalyserDisplay(){     // draw alternate display (page 1)
  u8x8.clearDisplay();
  u8x8.drawString(0,1,"CANBUS  ANALYSER");
  //u8x8.drawString(0,2,"Update ms");
  u8x8.drawString(0,3,"CAN BUS   fr/sec");
  u8x8.drawString(0,4,"Total frame");
  u8x8.drawString(0,5,"Tach");
  u8x8.drawString(0,6,"Gear");
  u8x8.drawString(0,7,"Wheel Speed");
}
void DriftDisplay1(){         // draw drift display (page 2)
  u8x8.clearDisplay();
  u8x8.drawString(0,1,"  Drift  Timer  ");
  //u8x8.drawString(0,2,"");
  u8x8.drawString(0,3,"Time");
  u8x8.drawString(0,4,"Speed");
  u8x8.drawString(0,5,"Tach");
  u8x8.drawString(0,6,"Gear");
  u8x8.drawString(0,7,"Steering");
}
void DriftDisplay2(){         // draw drift display (page 3)
  u8x8.clearDisplay();
  u8x8.drawString(0,1,"Drift Statistics");
  u8x8.drawString(0,2,"Delta Speed");
  u8x8.drawString(0,3,"Max Speed");
  u8x8.drawString(0,4,"Min Speed");
  u8x8.drawString(0,5,"Drift Time");
  u8x8.drawString(0,6,"Throttle");
  u8x8.drawString(0,7,"AccPedal");
}
//--------------------------------------------------------------------
void setup() { 
  pinMode(buttonPin, INPUT);  // Touch button input
  LEDS.addLeds<WS2812,DATA_PIN,GRB>(Rleds,NUM_LEDS);
  LEDS.setBrightness(84);
  u8x8.begin();
  u8x8.setPowerSave(0);
  u8x8.setFont(u8x8_font_amstrad_cpc_extended_r);
  defaultDisplay();
  
  Serial.begin(115200); // For debug use
  Serial.println("CAN Read - Testing receival of CAN Bus message");  

  if(Canbus.init(CANSPEED_500))  //Initialise MCP2515 CAN controller at the specified speed
  Serial.println("CAN Init ok");
  else
  Serial.println("Can't init CAN"); 
}

//---------------------------------------------------------------------
void loop()
{
  scanTime = millis() - lastUpdate;
  lastUpdate = millis();
  if (scanTime > maxUpdate) maxUpdate = scanTime;
  
  buttonState = digitalRead(buttonPin);  // toggle page a chaque fois que le bouton est activé
  if ((buttonState == HIGH) & (prevButtonState == 0)) { // one scan only - detection de front montant
    pressTime = millis();
    page++;
    
    if ((page == 0) | (page == 4)) {    // go to regular CAN Data page
      page = 0;
      defaultDisplay();
    }
    
    else if (page == 1) {           // go to CAN frame rate analysis page
      CANBusAnalyserDisplay();
      nbCAN_Totalmsg = 0;
      nbCAN_Tachmsg = 0;
      nbCAN_Gearmsg = 0;
      nbCAN_Wheelmsg = 0;
    }
    
    else if (page == 2) {   // go to regular CAN Data page
      DriftDisplay1();    
    }
    
    else if (page == 3) {   // go to regular CAN Data page
      DriftDisplay2();    
    }
  }
  prevButtonState = buttonState;
  
  if (buttonState == HIGH) {
    PBduration = millis() - pressTime;
    if (PBduration > 2500) {
      cylon(); 
      timeToParty = 1;
    }
  }
  else {timeToParty = 0;}

  if (ChronoUpdateScroll.hasPassed(500) == 1) {    // scroll aller-retour du titre au 500ms
    ChronoUpdateScroll.restart();
    u8x8.setCursor(0, 0);
    u8x8.print(Title.substring(x, x+16));
    x = x + step;
    if (x == span || x == 0) {
      step = step * -1 ;
    }
  }
  
  // ---------------------------- Reception CAN BUS
  tCAN message;
  if (mcp2515_check_message()) 
  {
    if (mcp2515_get_message(&message)) 
    {
      nbCAN_Totalmsg++;
      CANmsg_TimeStamp = millis();
      if(message.id == 0x140)  // filtre sur la vitesse moteur
      {             
        nbCAN_Tachmsg++;
        AccPedal = message.data[0] * 0.392157;    // Acceleration Pedal 0-100%
        //Serial.println(AccPedal);
        EngSpd = ((message.data[3] & 63) * 256) + message.data[2];
        //Serial.println(EngSpd);             // pour Arduino IDE Serial Plotter
        Throttle = message.data[6] * 0.392157;    // Throttle 0-100%
      }

      else if(message.id == 0x141)  // filtre sur la transmission
      {             
        nbCAN_Gearmsg++;
        Gear = (message.data[6] & 15);
        if ((Gear > 0) & (Gear < 7)) {
          GearStr = int_to_str(Gear); 
        }
        else {GearStr = "   N"; }
      }  
      
      else if(message.id == 0x0D0)  // filtre sur la position du volant en degree
      {
        Steering = ((message.data[1] * 256 + message.data[0])*0.1);  // Steering position en degrés *100 
      }
      
      else if(message.id == 0x0D4)  // filtre sur les vitesses des roues
      {
        nbCAN_Wheelmsg++;
        AvGa = ((message.data[1] * 256 + message.data[0])*0.5748);// vitesse en km/h X 10
        AvDr = ((message.data[3] * 256 + message.data[2])*0.5748);// vitesse en km/h X 10
        ArGa = ((message.data[5] * 256 + message.data[4])*0.5748);// vitesse en km/h X 10
        ArDr = ((message.data[7] * 256 + message.data[6])*0.5748);// vitesse en km/h X 10

        if ((page == 3) | (page == 2)) {
          wheelSpd [0] = AvGa;
          wheelSpd [1] = AvDr;
          wheelSpd [2] = ArGa;
          wheelSpd [3] = AvDr;
          sort(wheelSpd,4);   //Trier les vitesses des roues par ordre croissant
          maxSpd = wheelSpd [3];
          minSpd = wheelSpd [0];
          deltaSpd = maxSpd-minSpd;
          
          if (deltaSpd >= 30 ) {
            driftStatus = 1;
            driftDuration = millis() - driftStartTime;
          }
          else driftStatus = 0;

          if ((driftStatus == 1) & (prevDriftStatus == 0)) {
            driftStartTime = millis();
          }
          prevDriftStatus = driftStatus;
        }
      }
    }
  }
  
  // CAN bus live status detection; 0 = No CAN Data present, 1 = CAN Data incoming
  CANupdate = millis() - CANmsg_TimeStamp;
  if ( CANupdate < 100 ) { 
    CANstatus = 1; 
    if (prevCANstatus == 0) u8x8.drawString(0,1,"Live CANBUS Data") ;
  } 
  else { 
    CANstatus = 0; 
    u8x8.drawString(0,1,"No CAN BUS Data ") ;
  } 
  prevCANstatus = CANstatus;  
  
  // --------------------------  SD1306 128x64 OLED Display page 1 when CANstatus == 1 
  if ((CANstatus == 1) & (page == 1) & (ChronoCANStatRefresh.hasPassed(1000) == 1)) { 
    ChronoCANStatRefresh.restart();
    
    temp = int_to_str(scanTime);  // nd de scan en 1 seconde = rate
    u8x8.setCursor(12, 2);
    //u8x8.print(temp); 
    maxUpdate = 0;
    
    temp = int_to_str(nbCAN_Totalmsg);
    nbCAN_Totalmsg = 0, 
    u8x8.setCursor(12, 4);
    u8x8.print(temp); 
    
    temp = int_to_str(nbCAN_Tachmsg);
    nbCAN_Tachmsg = 0,
    u8x8.setCursor(12, 5);
    u8x8.print(temp); 
    
    temp = int_to_str(nbCAN_Gearmsg);
    nbCAN_Gearmsg = 0,
    u8x8.setCursor(12, 6);
    u8x8.print(temp);     
    
    temp = int_to_str(nbCAN_Wheelmsg);
    nbCAN_Wheelmsg = 0;
    u8x8.setCursor(12, 7);
    u8x8.print(temp); 
  }
  
  // ---------------------------- Refresh SD1306 128x64 OLED Display page 0 when CANstatus == 1 
  if (ChronoUpdateLCD.hasPassed(250) == 1) {    // Update LCD au 250ms
    ChronoUpdateLCD.restart();
    
    if ((CANstatus == 1) & (page == 0)) {   // refresh page au 100msec
      long LCDrefreshTime = millis();
      u8x8.setCursor(12, 2);
      u8x8.print(GearStr);      // Gear
      
      u8x8.setCursor(12, 3);
      temp = int_to_str(EngSpd);  // Revolution
      u8x8.print(temp);     
      
      u8x8.setCursor(11, 4);
      temp = int_to_str3D1D(AvGa);  // Roue Avant Gauche
      u8x8.print(temp);     
      u8x8.setCursor(11, 5);  
      temp = int_to_str3D1D(AvDr);  // Roue Avant Droite  
      u8x8.print(temp);
      
      u8x8.setCursor(11, 6);
      temp = int_to_str3D1D(ArGa);  // Roue Arriere Gauche
      u8x8.print(temp);
      
      u8x8.setCursor(11, 7);
      temp = int_to_str3D1D(ArDr);  // Roue Arriere Droite
      u8x8.print(temp);
      LCDrefreshTime = millis() - LCDrefreshTime;
      //Serial.println(LCDrefreshTime); // mesure du temps de refresh (I2C Vs SPI)
                                      // égal 23msec en I2C, égal 5msec en SPI
    } 

    if ((CANstatus == 1) & (page == 2)) {   // refresh page au 100msec
            
      u8x8.setCursor(12, 3);
      temp = int_to_str(driftDuration);  // 
      u8x8.print(temp);     
      
      u8x8.setCursor(11, 4);
      temp = int_to_str3D1D(minSpd);  // 
      u8x8.print(temp);       // 
      
      u8x8.setCursor(12, 5);  
      temp = int_to_str(EngSpd);  //   
      u8x8.print(temp);
      
      u8x8.setCursor(12, 6); 
      u8x8.print(GearStr);

      if (Steering < -20 ) {
        u8x8.setCursor(9, 7);
        u8x8.print("Ga");
      }
        else if (Steering > 20 ) {
        u8x8.setCursor(9, 7);
        u8x8.print("Dr");
      }
        else {
        u8x8.setCursor(9, 7);
        u8x8.print("  ");
      }
      u8x8.setCursor(11, 7);
      temp = int_to_str3D1D(Steering);  // 
      u8x8.print(temp);
    }   
    
    if ((CANstatus == 1) & (page == 3)) {   // refresh page au 100msec
      
      u8x8.setCursor(11, 2);
      temp = int_to_str3D1D(deltaSpd);  // 
      u8x8.print(temp);       // 
      
      u8x8.setCursor(11, 3);
      temp = int_to_str3D1D(maxSpd);  // 
      u8x8.print(temp);     
      
      u8x8.setCursor(11, 4);
      temp = int_to_str3D1D(minSpd);  // 
      u8x8.print(temp);
      
      u8x8.setCursor(12, 5);  
      //temp = int_to_str(driftDuration);  //   
      u8x8.print(driftDuration);
      
      u8x8.setCursor(12, 6);
      temp = int_to_str(Throttle);  // 
      u8x8.print(temp);
      
      u8x8.setCursor(12, 7);
      temp = int_to_str(AccPedal);  // 
      u8x8.print(temp);
    }   
    
    // ---------------------------- Refresh SD1306 128x64 OLED Display page 0 when CANstatus == 0
    else if (CANstatus == 0) {        //  erase display when no CAN data available
      u8x8.setCursor(12, 2);
      u8x8.print(espace); 
      u8x8.setCursor(12, 3);
      u8x8.print(espace); 
      u8x8.setCursor(12, 4);
      u8x8.print(espace);
      u8x8.setCursor(12, 5);
      u8x8.print(espace);
      u8x8.setCursor(12, 6);
      u8x8.print(espace);
      u8x8.setCursor(12, 7);
      u8x8.print(espace);
    }
  }
  //---------------------------------------------------------------
  // WS2812 LEDs update
  if (ChronoFlashLent.hasPassed(800) == 1) {    // toggle au 800msec
    ChronoFlashLent.restart();
    if (FlasherLent == HIGH)  FlasherLent = LOW;
    else FlasherLent = HIGH;
  }
  
  if (CANstatus == 1) {
    
    if ((ChronoUpdateFastLed.hasPassed(200) == 1) & (timeToParty == 0)){    // refresh au 200msec
      ChronoUpdateFastLed.restart();
      if (FlasherRapide == HIGH)  FlasherRapide = LOW;
      else FlasherRapide = HIGH;
      
      int numLedsToLight = map(EngSpd, 0, 8001, 0, NUM_LEDS);
      long WS2812RefreshTime = micros();
      for(int i = 0; i < NUM_LEDS; i++) { 
        if (i < numLedsToLight) { leds[i] = CHSV(160, 255, 128); }                    // Blue 1/2 brightness
        if ((i == numLedsToLight) & (FlasherRapide == 1)) { leds[i] = CRGB(48, 48, 48); }     // White 3/8 brightness
        else if ((i == numLedsToLight) & (FlasherRapide == 0)) { leds[i] = CRGB(0, 0, 0); }   // Black
        if ((i >= numLedsToLight) & (EngSpd >= 7400)) { leds[i] = CHSV(0, 255, 128); }    // Red 1/2 brightness    
        if (i > numLedsToLight) { leds[i] = CHSV(160, 255, 0); }                      // Black 
        // First LED flash Green when engine in idle
        if ((i == 0) & (EngSpd < 500) & (FlasherLent == 1)) { leds[i] = CHSV(96, 255, 128);}       // Green 1/2 brightness
        else if ((i == 0) & (EngSpd < 500) & (FlasherLent == 0)) { leds[i] = CHSV(96, 255, 0);}     // Black 
        if ((i > 0) & (EngSpd < 500)) { leds[i] = CRGB(0, 0, 0); }                 // Black   
      }
    reverseLeds();
    FastLED.show();
    WS2812RefreshTime = micros() - WS2812RefreshTime;
    //Serial.println(WS2812RefreshTime);
    } 
  }
  else {
    // First LED flash Red when no CAN BUS communication
    for(int i = 0; i < NUM_LEDS; i++) { 
      if ((i == 0) & (FlasherLent == 1)) { leds[i] = CHSV(0, 255, 128);}  // Red 1/2 brightness
      if ((i == 0) & (FlasherLent == 0)) { leds[i] = CHSV(0, 255, 0);}    // Black  
      if (i > 0 ) { leds[i] = CRGB(0, 0, 0); }                // Black    
    }
  reverseLeds();
  FastLED.show(); 
  }
}
//--------------------------------------------------------------
 
OK, I see 2 issues here. I based my code off mcp_can class which belongs to Seeedstudio's repo

Your library uses Sparkfun's mcp2515 class which has AVR specific registers and will never work on teensy, at all. (and its a blocking type of canbus library as well!).

Can I see your teensy speicific version?
 
Last edited:
Of course.
Can stuff is based on example Rx-SingleBus-WithObjects from the FlexCAN library.
Code:
/*
IO PinOut
SSD1306 OLED Display SPI PinOut for Teensy 3.2
D0	SCK		13
D1	MOSI	11
RES	RST		8
DC	AO		6
CS	SS		7 
Bouton TTP223	12
WS2812 RGB LED Ring = 5
CAN Controller is Internal to Teensy
CAN TX 3
CAN RX 4
CAN transceiver is Texas Instr. SN65HVD230
*/
const int buttonPin = 12; 
bool buttonState, prevButtonState, timeToParty;
int page = 0;
long scanTime, lastUpdate, maxUpdate, pressTime, PBduration;
int nbCAN_Totalmsg, nbCAN_Tachmsg, nbCAN_Gearmsg, nbCAN_Wheelmsg;

// --------------- Chrono Instances
#include <Chrono.h>              // https://github.com/SofaPirate/Chrono
Chrono ChronoFlashLent;             // pour flasher quand moteur éteint, ou pas de CAN BUS
Chrono ChronoUpdateLCD;           // pour refresh du LCD
Chrono ChronoUpdateScroll;          // pour refresh du texte scrollant
Chrono ChronoUpdateFastLed;         // pour refresh du Tach avec des leds RGB
Chrono ChronoCANStatRefresh;        // pour refresh du calcul de statistique de frames/secondes
// --------------- SSD1306 Display variables 
#include <Arduino.h>
#include <U8x8lib.h>      //  https://github.com/olikraus/u8g2
// U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE);  // en I2c
U8X8_SSD1306_128X64_NONAME_4W_HW_SPI u8x8(7/*CS*/, 6/*DC*/, 8/*RES*/ );  // En SPI
String Title = "  BRZ Tuning 2018/07/16  ";
String espace = "    ";
String temp(4);
byte span = Title.length() -16;  // nb de caractere qui dépasse le display
byte x = 0, step = 1;
// --------------- CAN BUS variables 
#include <FlexCAN.h>     // https://github.com/collin80/FlexCAN_Library
bool CANstatus, prevCANstatus;
long CANupdate, CANmsg_TimeStamp, elapseTime;
// --------------- FAST LED variables 
#include <WS2812Serial.h>  // https://github.com/PaulStoffregen/WS2812Serial Non blocking LED library
#define USE_WS2812SERIAL
#include "FastLED.h"    //  https://github.com/FastLED/FastLED
#define NUM_LEDS 16
#define DATA_PIN 5
CRGB leds[NUM_LEDS];
CRGB Rleds[NUM_LEDS];     // Reverse leds array
bool FlasherRapide, FlasherLent;
// --------------- BRZ speed variables  
int EngSpd;
byte Gear, Throttle, AccPedal;
String GearStr(4);  // String de 4 caracteres
int AvGa, AvDr, ArGa, ArDr, Steering;
int wheelSpd [4], deltaSpd, maxSpd, minSpd;
bool driftStatus, prevDriftStatus;
long driftStartTime, driftDuration;
//--------------------------------------------------------------------
void sort(int a[], int size) {
	for(int i=0; i<(size-1); i++) {
		for(int o=0; o<(size-(i+1)); o++) {
			if(a[o] > a[o+1]) {
				int t = a[o];
				a[o] = a[o+1];
				a[o+1] = t;
			}
		}
	}
}
//---------------------------------------------------------------
void fadeall() { for(int i = 0; i < NUM_LEDS; i++) { leds[i].nscale8(250); } }
//--------------------------------------------------------------------
void cylon() {
	static uint8_t hue = 0;
	for(int i = 0; i < NUM_LEDS; i++) {
		Rleds[i] = CHSV(hue, 255, 96);
		hue = hue + 7;
		FastLED.show(); 
		fadeall();
		delay(10);
	}
	for(int i = (NUM_LEDS)-1; i >= 0; i--) {
		Rleds[i] = CHSV(hue, 255, 96);
		hue = hue + 7;
		FastLED.show();
		fadeall();
		delay(10);
	}
}
//--------------------------------------------------------------------
String int_to_str (int x) {  // conversion int a string de 4 caracteres
	String inStr;
	inStr = String(x);    
	while (inStr.length() < 4)  
	{
		inStr = " " + inStr;
	}
	return inStr;
}
//--------------------------------------------------------------------
String int_to_str3D1D (int x) {  // conversion int a string de 3 chiffres + 1 décimale
	String inStr;
	inStr = String(x);    
	while (inStr.length() < 4)  
	{
		inStr = " " + inStr;
	}
	String outStr = inStr.substring(0,3) + "." + inStr.substring(3,4); 
	return outStr;
}
//--------------------------------------------------------------------
void reverseLeds() {   // Reverse order of led array for clockwise tach effect
	uint8_t left = 0;
	uint8_t right = NUM_LEDS-1;
	while (left < NUM_LEDS) {
		Rleds [left++] = leds[right--];
	}
}
void defaultDisplay(){      // draw default display (page 0)
	u8x8.clearDisplay();
	u8x8.drawString(0,1,"  CANBUS TACH  ");
	u8x8.drawString(0,2,"Gear");
	u8x8.drawString(0,3,"Tach(rpm)");
	u8x8.drawString(0,4,"Av Gauche");
	u8x8.drawString(0,5,"Av Droite");
	u8x8.drawString(0,6,"Ar Gauche");
	u8x8.drawString(0,7,"Ar Droite");
}
void CANBusAnalyserDisplay(){     // draw alternate display (page 1)
	u8x8.clearDisplay();
	u8x8.drawString(0,1,"CANBUS  ANALYSER");
	//u8x8.drawString(0,2,"Update ms");
	u8x8.drawString(0,3,"CAN BUS   fr/sec");
	u8x8.drawString(0,4,"Total frame");
	u8x8.drawString(0,5,"Tach");
	u8x8.drawString(0,6,"Gear");
	u8x8.drawString(0,7,"Wheel Speed");
}
void DriftDisplay1(){         // draw drift display (page 2)
	u8x8.clearDisplay();
	u8x8.drawString(0,1,"  Drift  Timer  ");
	//u8x8.drawString(0,2,"");
	u8x8.drawString(0,3,"Time");
	u8x8.drawString(0,4,"Speed");
	u8x8.drawString(0,5,"Tach");
	u8x8.drawString(0,6,"Gear");
	u8x8.drawString(0,7,"Steering");
}
void DriftDisplay2(){         // draw drift display (page 3)
	u8x8.clearDisplay();
	u8x8.drawString(0,1,"Drift Statistics");
	u8x8.drawString(0,2,"Delta Speed");
	u8x8.drawString(0,3,"Max Speed");
	u8x8.drawString(0,4,"Min Speed");
	u8x8.drawString(0,5,"Drift Time");
	u8x8.drawString(0,6,"Throttle");
	u8x8.drawString(0,7,"AccPedal");
}

//---------------------------------------------------------------

class ExampleClass : public CANListener 
{
public:
	void printFrame(CAN_message_t &frame, int mailbox);
	bool frameHandler(CAN_message_t &frame, int mailbox, uint8_t controller); //overrides the parent version so we can actually do something
};

void ExampleClass::printFrame(CAN_message_t &frame, int mailbox)
{
	nbCAN_Totalmsg++;
	CANmsg_TimeStamp = millis();

	switch (frame.id) {
	case 0x140:    // filtre sur la vitesse moteur
		nbCAN_Tachmsg++;
		AccPedal = frame.buf[0] * 0.392157;    // Acceleration Pedal 0-100%
		//Serial.println(AccPedal);
		EngSpd = ((frame.buf[3] & 63) * 256) + frame.buf[2];
		//Serial.println(EngSpd);             // pour Arduino IDE Serial Plotter
		Throttle = frame.buf[6] * 0.392157;    // Throttle 0-100%
		break;
		
	case 0x141:    // filtre sur la transmission
		nbCAN_Gearmsg++;
		Gear = (frame.buf[6] & 15);
		if ((Gear > 0) & (Gear < 7)) {
			GearStr = int_to_str(Gear); 
		}
		else {GearStr = "   N"; }
		break;
	case 0x0D0:    // filtre sur la position du volant en degree
		Steering = ((frame.buf[1] * 256 + frame.buf[0])*0.1);  // Steering position en degrés *100 
		break;
		
	case 0x0D4:    // filtre sur les vitesses des roues
		nbCAN_Wheelmsg++;
		AvGa = ((frame.buf[1] * 256 + frame.buf[0])*0.5748);// vitesse en km/h X 10
		AvDr = ((frame.buf[3] * 256 + frame.buf[2])*0.5748);// vitesse en km/h X 10
		ArGa = ((frame.buf[5] * 256 + frame.buf[4])*0.5748);// vitesse en km/h X 10
		ArDr = ((frame.buf[7] * 256 + frame.buf[6])*0.5748);// vitesse en km/h X 10

		if ((page == 3) | (page == 2)) {
			wheelSpd [0] = AvGa;
			wheelSpd [1] = AvDr;
			wheelSpd [2] = ArGa;
			wheelSpd [3] = AvDr;
			sort(wheelSpd,4);   //Trier les vitesses des roues par ordre croissant
			maxSpd = wheelSpd [3];
			minSpd = wheelSpd [0];
			deltaSpd = maxSpd-minSpd;
			
			if (deltaSpd >= 30 ) {
				driftStatus = 1;
				driftDuration = millis() - driftStartTime;
			}
			else driftStatus = 0;

			if ((driftStatus == 1) & (prevDriftStatus == 0)) {
				driftStartTime = millis();
			}
			prevDriftStatus = driftStatus;
		}
		break;
	}
}

bool ExampleClass::frameHandler(CAN_message_t &frame, int mailbox, uint8_t controller)
{
	printFrame(frame, mailbox);
	return true;
}

ExampleClass exampleClass;

//--------------------------------------------------------------------
void setup() { 
	pinMode(buttonPin, INPUT);  // Touch button input
	LEDS.addLeds<WS2812SERIAL,DATA_PIN,BRG>(Rleds,NUM_LEDS);
	LEDS.setBrightness(84);
	u8x8.begin();
	u8x8.setPowerSave(0);
	u8x8.setFont(u8x8_font_amstrad_cpc_extended_r);
	defaultDisplay();
	Serial.begin(115200); // For debug use
	Can0.begin(500000);  
	Can0.attachObj(&exampleClass);
	exampleClass.attachGeneralHandler();
}

//---------------------------------------------------------------------
void loop()
{
	scanTime = millis() - lastUpdate;
	lastUpdate = millis();
	if (scanTime > maxUpdate) maxUpdate = scanTime;

	buttonState = digitalRead(buttonPin);  // toggle page a chaque fois que le bouton est activé
	if ((buttonState == HIGH) & (prevButtonState == 0)) { // one scan only - detection de front montant
		pressTime = millis();
		page++;
		
		if ((page == 0) | (page == 4)) {    // go to regular CAN Data page
			page = 0;
			defaultDisplay();
		}
		
		else if (page == 1) {           // go to CAN frame rate analysis page
			CANBusAnalyserDisplay();
			nbCAN_Totalmsg = 0;
			nbCAN_Tachmsg = 0;
			nbCAN_Gearmsg = 0;
			nbCAN_Wheelmsg = 0;
		}
		
		else if (page == 2) {   // go to regular CAN Data page
			DriftDisplay1();    
		}
		
		else if (page == 3) {   // go to 2nd CAN Data page
			DriftDisplay2();    
		}
	}
	prevButtonState = buttonState;

	if (buttonState == HIGH) {
		PBduration = millis() - pressTime;
		if (PBduration > 2500) {
			cylon(); 
			timeToParty = 1;
		}
	}
	else {timeToParty = 0;}

	if (ChronoUpdateScroll.hasPassed(500) == 1) {    // scroll aller-retour du titre au 500ms
		ChronoUpdateScroll.restart();
		u8x8.setCursor(0, 0);
		u8x8.print(Title.substring(x, x+16));
		x = x + step;
		if (x == span || x == 0) {
			step = step * -1 ;
		}
	}

	// CAN bus live status detection; 0 = No CAN Data present, 1 = CAN Data incoming
	CANupdate = millis() - CANmsg_TimeStamp;
	if ( CANupdate < 100 ) { 
		CANstatus = 1; 
		if (prevCANstatus == 0) u8x8.drawString(0,1,"Live CANBUS Data") ;
	} 
	else { 
		CANstatus = 0; 
		u8x8.drawString(0,1,"No CAN BUS Data ") ;
	} 
	prevCANstatus = CANstatus;  

	// --------------------------  SD1306 128x64 OLED Display page 1 when CANstatus == 1 
	if ((CANstatus == 1) & (page == 1) & (ChronoCANStatRefresh.hasPassed(1000) == 1)) { 
		elapseTime = ChronoCANStatRefresh.elapsed();
		ChronoCANStatRefresh.restart();
		
		temp = int_to_str(scanTime);  // nd de scan en 1 seconde = rate
		u8x8.setCursor(12, 2);
		//u8x8.print(temp); 
		maxUpdate = 0;
		
		nbCAN_Totalmsg = nbCAN_Totalmsg * (1000.0 / elapseTime);
		temp = int_to_str(nbCAN_Totalmsg);
		nbCAN_Totalmsg = 0, 
		u8x8.setCursor(12, 4);
		u8x8.print(temp); 

    nbCAN_Tachmsg = nbCAN_Tachmsg * (1000.0 / elapseTime);
		temp = int_to_str(nbCAN_Tachmsg);
		nbCAN_Tachmsg = 0,
		u8x8.setCursor(12, 5);
		u8x8.print(temp); 

    nbCAN_Gearmsg = nbCAN_Gearmsg * (1000.0 / elapseTime);    
		temp = int_to_str(nbCAN_Gearmsg);
		nbCAN_Gearmsg = 0,
		u8x8.setCursor(12, 6);
		u8x8.print(temp);     

    nbCAN_Wheelmsg = nbCAN_Wheelmsg * (1000.0 / elapseTime);  
		temp = int_to_str(nbCAN_Wheelmsg);
		nbCAN_Wheelmsg = 0;
		u8x8.setCursor(12, 7);
		u8x8.print(temp); 
	}

	// ---------------------------- Refresh SD1306 128x64 OLED Display page 0 when CANstatus == 1 
	if (ChronoUpdateLCD.hasPassed(250) == 1) {    // Update LCD au 250ms
		ChronoUpdateLCD.restart();
		
		if ((CANstatus == 1) & (page == 0)) {   // refresh page au 100msec
			long LCDrefreshTime = millis();
			u8x8.setCursor(12, 2);
			u8x8.print(GearStr);      // Gear
			
			u8x8.setCursor(12, 3);
			temp = int_to_str(EngSpd);  // Revolution
			u8x8.print(temp);     
			
			u8x8.setCursor(11, 4);
			temp = int_to_str3D1D(AvGa);  // Roue Avant Gauche
			u8x8.print(temp);     
			u8x8.setCursor(11, 5);  
			temp = int_to_str3D1D(AvDr);  // Roue Avant Droite  
			u8x8.print(temp);
			
			u8x8.setCursor(11, 6);
			temp = int_to_str3D1D(ArGa);  // Roue Arriere Gauche
			u8x8.print(temp);
			
			u8x8.setCursor(11, 7);
			temp = int_to_str3D1D(ArDr);  // Roue Arriere Droite
			u8x8.print(temp);
			LCDrefreshTime = millis() - LCDrefreshTime;
			// Serial.println(LCDrefreshTime); // mesure du temps de refresh (I2C Vs SPI)
			// égal 23msec en I2C, égal 5msec en SPI
		} 

		if ((CANstatus == 1) & (page == 2)) {   // refresh page au 100msec
			
			u8x8.setCursor(12, 3);
			temp = int_to_str(driftDuration);  // 
			u8x8.print(temp);     
			
			u8x8.setCursor(11, 4);
			temp = int_to_str3D1D(minSpd);  // 
			u8x8.print(temp);       // 
			
			u8x8.setCursor(12, 5);  
			temp = int_to_str(EngSpd);  //   
			u8x8.print(temp);
			
			u8x8.setCursor(12, 6); 
			u8x8.print(GearStr);

			if (Steering < -20 ) {
				u8x8.setCursor(9, 7);
				u8x8.print("Ga");
			}
			else if (Steering > 20 ) {
				u8x8.setCursor(9, 7);
				u8x8.print("Dr");
			}
			else {
				u8x8.setCursor(9, 7);
				u8x8.print("  ");
			}
			u8x8.setCursor(11, 7);
			temp = int_to_str3D1D(Steering);  // 
			u8x8.print(temp);
		}   
		
		if ((CANstatus == 1) & (page == 3)) {   // refresh page au 100msec
			
			u8x8.setCursor(11, 2);
			temp = int_to_str3D1D(deltaSpd);  // 
			u8x8.print(temp);       // 
			
			u8x8.setCursor(11, 3);
			temp = int_to_str3D1D(maxSpd);  // 
			u8x8.print(temp);     
			
			u8x8.setCursor(11, 4);
			temp = int_to_str3D1D(minSpd);  // 
			u8x8.print(temp);
			
			u8x8.setCursor(12, 5);  
			//temp = int_to_str(driftDuration);  //   
			u8x8.print(driftDuration);
			
			u8x8.setCursor(12, 6);
			temp = int_to_str(Throttle);  // 
			u8x8.print(temp);
			
			u8x8.setCursor(12, 7);
			temp = int_to_str(AccPedal);  // 
			u8x8.print(temp);
		}   
		
		// ---------------------------- Refresh SD1306 128x64 OLED Display page 0 when CANstatus == 0
		else if (CANstatus == 0) {        //  erase display when no CAN data available
			u8x8.setCursor(12, 2);
			u8x8.print(espace); 
			u8x8.setCursor(12, 3);
			u8x8.print(espace); 
			u8x8.setCursor(12, 4);
			u8x8.print(espace);
			u8x8.setCursor(12, 5);
			u8x8.print(espace);
			u8x8.setCursor(12, 6);
			u8x8.print(espace);
			u8x8.setCursor(12, 7);
			u8x8.print(espace);
		}
	}
	//---------------------------------------------------------------
	// WS2812 LEDs update
	if (ChronoFlashLent.hasPassed(800) == 1) {    // toggle au 800msec
		ChronoFlashLent.restart();
		if (FlasherLent == HIGH)  FlasherLent = LOW;
		else FlasherLent = HIGH;
	}

	if (CANstatus == 1) {
		
		if ((ChronoUpdateFastLed.hasPassed(200) == 1) & (timeToParty == 0)){    // refresh au 200msec
			ChronoUpdateFastLed.restart();
			if (FlasherRapide == HIGH)  FlasherRapide = LOW;
			else FlasherRapide = HIGH;
			
			int numLedsToLight = map(EngSpd, 0, 8001, 0, NUM_LEDS);
                        long WS2812RefreshTime = micros();
			for(int i = 0; i < NUM_LEDS; i++) { 
				if (i < numLedsToLight) { leds[i] = CHSV(160, 255, 128); }                    // Blue 1/2 brightness
				if ((i == numLedsToLight) & (FlasherRapide == 1)) { leds[i] = CRGB(48, 48, 48); }     // White 3/8 brightness
				else if ((i == numLedsToLight) & (FlasherRapide == 0)) { leds[i] = CRGB(0, 0, 0); }   // Black
				if ((i >= numLedsToLight) & (EngSpd >= 7400)) { leds[i] = CHSV(0, 255, 128); }    // Red 1/2 brightness    
				if (i > numLedsToLight) { leds[i] = CHSV(160, 255, 0); }                      // Black 
				// First LED flash Green when engine in idle
				if ((i == 0) & (EngSpd < 500) & (FlasherLent == 1)) { leds[i] = CHSV(96, 255, 128);}       // Green 1/2 brightness
				else if ((i == 0) & (EngSpd < 500) & (FlasherLent == 0)) { leds[i] = CHSV(96, 255, 0);}     // Black 
				if ((i > 0) & (EngSpd < 500)) { leds[i] = CRGB(0, 0, 0); }                 // Black  
			}
     reverseLeds();
     FastLED.show();
     WS2812RefreshTime = micros() - WS2812RefreshTime;
     //Serial.println(WS2812RefreshTime);
		} 
	}
	else {
		// First LED flash Red when no CAN BUS communication
		for(int i = 0; i < NUM_LEDS; i++) { 
			if ((i == 0) & (FlasherLent == 1)) { leds[i] = CHSV(0, 255, 128);}  // Red 1/2 brightness
			if ((i == 0) & (FlasherLent == 0)) { leds[i] = CHSV(0, 255, 0);}    // Black  
			if (i > 0 ) { leds[i] = CRGB(0, 0, 0); }                // Black    
		}
        reverseLeds();
        FastLED.show(); 
	}
}
//--------------------------------------------------------------
 
Yeah, OK it looks like you have exampleClass loaded from Object oriented can, unfortunately I don't mimic the Flexcan_Library class(s) at all, unless theres enough interest to do that then ill think about it, but the calls are different
 
I have sequentially remove different part of the code till it doesn't freeze.
The source of the problem doesn't seem to be a conflict (interrupt issue) between FlexCAN and WS2812Serial, but only with the FlexCAN library.
The only way for this code not to crash is to comment out everything in the Switch Case structure where it services the CAN frames.
 
Hi Charlez450, I have updated IFCT to emulate collin80's library which your using, including the object oriented calls. It compiles fine as is and your exampleClass is indeed receiving the specific frames, I can't test your other code as I don't have the hardware.

Just download latest update and change your sketch above to use IFCT.h include instead of FlexCAN.h and recompile, nothing else needs to be changed

Tony
 
Status
Not open for further replies.
Back
Top