Arduino SeeedStudio CAN-BUS shield

SeeedStudio CAN-BUS shield | Troubleshooting | CAN Bus Code Example #1 | CAN Bus Code Example #2 |

 

The SeeddStudio CAN bus shield v1.0 is based on the MCP2515 CAN Bus controller with SPI interface and MCP2551 CAN transceiver.   The V1.0 shield uses pins 11, 12, and 13 to access SPI on the Uno.   Supports CAN V2.0B up to 1. MB/s and both standard (11 bit) and extended (29 bit) data and remote frames.  

CAN-BUS Shield V1.0 product

How to adapt the shield to different pins for SPI

Shield tutorial

CAN-BUS Shield V1.2

 

The SeeedStudio CAN bus shield v2.0 is based on the MCP2515 controller & MCP2551 transceiver.   Supports CAN V2.0B up to 1. MB/s and both standard (11 bit) and extended (29 bit) data and remote frames.  

Product details

Seeed_Arduino_CAN library

 

Troubleshooting

Do not attempt to use the built-in LED on pin #13 because it is used as a SPI pin for the shield.   Do not use pin #7 because it is used by the shield for the status LED.  

The Seeed Arduino CAN library (mcp2515_can.h) can send an extended 19 bit CAN frame, but it cannot detect it as a receiver, nor can it decode the CAN ID correctly.  

Four LEDs on the shield provide useful information.   If you see the TX, RX, and INT red LEDs constantly on, then your termination resistance is incorrect (should be 120 ohms at either end of the CAN bus).   When termination resistance is correct, you will see a green PWR LED, and then when the unit sending the Tx red LED will briefly illuminate, and when the unit receiving the Rx and INT LED will briefly illuminate.  

 

CAN Bus Code Example

CAN bus is great when you have a situation where you need to get power to a Arduino with sensors, and you want to communicate that sensor information back to a central source such as a Arduino with a shield that transmits the data to the internet.   I found that CAN bus literature online was very misleading and confusing.   The examples below are intended to provide the typical Arduino user with sufficient information to get a CAN bus running with minimal pain and maximum connectivity and robustness.  

Note the use of the function updateCanMsgFromFloat() to build the CAN message that is sent.   It is critical that you carefully select values for the 'offset' and 'factor' for the floating point values you are sending.   The correct offset and factor will preserve the required number of significant digits in the values you are transmitting.   The table below provides some guidelines.  

Float Offset Factor # Significant Digits
0 .. 999 0 1.0 1 .. 3
0.1 .. 409.4 0 0.01 1 .. 4
0.001 .. 4.009 0 0.001 1 .. 4
0.001 .. 0.4009 0 0.0001 3 .. 4
0 .. 1 0 0.001 .. 1.0 1
0 .. 99 0 0.1 .. 1.0 2
0 .. 999 0 1.0 3
900.1 .. 999.1 900 0.1 4
9900.1 .. 9999.1 9900 0.1 5

 

When you have a CAN bus with a lot of traffic, then you need to setup masks and filters for the receiving CAN bus shield.   If you don't set these up properly, you won't get any messages.   Furthermore, you can use them to limit the scope of messages you need to deal with in your code.  

CANbus_seeduino_SendMsgE.ino

		 
/*
 
  Seeduino CAN bus shield - send message
  
  Modified by Mark Kiehl
  Original source:  various

  Resources:
	DIO7  can LED
	DO10  SPI (SS)
	DO11  SPI (MOSI)
	DO12  SPI (MISO)
	DO13  SPI (SCK)

  Don't mess with the CAN LED on DIO7 (seeduino).

  See also:  CANbus_seeduino_ReceiveMsgD.ino
 
*/

// Show serial messages when DEBUG = true, otherwise minimize them.
#define DEBUG true


// ****************************************************/
// CAN bus data structure and hardware vars

// CAN message address
// 0xPPPXXXXSS
//  P = priority;  low value = higher priority; 
//    0x00=0
//    0x0F=15
//    0x10=16
//    0x1C=20
//    0x20=32
//    0x90=144
//    0xFF=255; 
//  XXXX = PNG, parameter group number, 4 chars / 8 bytes long
//  SS = source address, 
unsigned long CANmsgId = 0x0F100120;

// The CAN message
unsigned char CANmsg[8];

// CAN library source:
//  Shenzhen SeeedStudio Co.,LTD.
//  http://www.seeedstudio.com
#include "mcp_can.h"
#include "SPI.h"

// CAN bus @ 250 kbps is limited to a sample rate of 100 Hz
// 1000 ms = 1 sec = 1 Hz
// 100 ms = 0.1 sec = 10 Hz
// 10 ms = 0.01 sec = 100 Hz
int TxInterval = 1000;
unsigned long lastTx = 0;

// ****************************************************/

void setup() {
  
  Serial.begin(9600);
  delay(5000);
  while (!Serial) {
	; // wait for serial port to connect. Needed for Leonardo only
	delay(1);
  }
  
  // ****************************************************/
  // CAN_5KBPS, CAN_10KBPS, CAN_20KBPS, CAN_40KBPS, CAN_50KBPS, CAN_80KBPS, CAN_100KBPS, CAN_125KBPS, CAN_200KBPS, CAN_250KBPS and CAN_500KBPS. 
  if(CAN.begin(CAN_250KBPS) == CAN_OK) 
  {
	Serial.println ("can init ok");
  } else {
	Serial.println("Can init fail!!");
	pinMode(13, OUTPUT);
	while (1) {
	  // flash LED on DIO13 continuously
	  for(int i = 20; i > 0; i--){
		digitalWrite(13, HIGH);
		delay(25);
		digitalWrite(13, LOW);
		delay(25);
	  }
	}
  }
  randomSeed(millis());
  // ****************************************************/

  Serial.println("Setup complete");
  Serial.println("  ");
}

void loop() {

  // if millis() or timer wraps around, we'll just reset it
  if (lastTx > millis())  lastTx = millis();

  if ((millis() - lastTx) > TxInterval) {   
	
	// clear out CANmsg[]
	for (int i=0; i<8; i++) {
	  CANmsg[i] = 0;
	}

	int msgOffset;
	float msgFactor;
	float myFloatVal;
		
	// cycle through CANmsgId
	switch (CANmsgId) {
	  case 0x0F100120:
		CANmsgId = 0x0F200120;
		// temp in deg C
		msgOffset = -125;
		msgFactor = 0.03125;
		myFloatVal = 23.4;
		break;
	  case 0x0F200120:
		CANmsgId = 0x0F300120;
		// percentage
		msgOffset = -125;
		msgFactor = 1.0;
		myFloatVal = 98.7;
		break;
	  case 0x0F300120:
		CANmsgId = 0x0F100120;
		// gps coordinate
		msgOffset = 4000;
		msgFactor = 0.0001;
		myFloatVal = 40.4479;
		break;
	}

	#if DEBUG
	  Serial.print("float value = ");
	  Serial.println(myFloatVal);
	#endif
	// updateCanMsgFromFloat(float floatVal, int startBit, int Length, int offset, float factor) 
	updateCanMsgFromFloat(myFloatVal, 0, 16, msgOffset, msgFactor);
	CAN.sendMsgBuf(CANmsgId, 1, 8, CANmsg);
	
	lastTx = millis();
	#if DEBUG
	  Serial.print("Sending CAN msg ");
	  Serial.print(CANmsgId, HEX);
	  Serial.print("  ");
	  for (int i=0; i<7; i++) {
		Serial.print(CANmsg[i]);
		Serial.print(",");
	  }
	  Serial.println(CANmsg[7]);
	  Serial.println("  ");
	#endif
  } 

}


//****************************************************************

// These functions are written around the concept of using at
// least two bytes to represent a float or integer value.  
// This allows you to send up to four separate float or integer
// values within a CAN message.  

void updateCanMsgFromFloat(float floatVal, int startBit, int length, int offset, float factor) {
  // Update msg[8] within CANmsg[] with the passed parameters
  // Assumes length = 16;
  // unsigned int floatToIntCANbus(float myFloat, int offset, float factor) {
  unsigned int myInt = floatToIntCANbus(floatVal, offset, factor);
  byte msb = getMsbFromInt(myInt);
  byte lsb = getLsbFromInt(myInt);
  switch (startBit) {
	case 0:
	  CANmsg[0] = msb;
	  CANmsg[1] = lsb;      
	  break;
	case 16:
	  CANmsg[2] = msb;
	  CANmsg[3] = lsb;      
	  break;
	case 32:
	  CANmsg[4] = msb;
	  CANmsg[5] = lsb;      
	  break;
	case 48:
	  CANmsg[6] = msb;
	  CANmsg[7] = lsb;      
	  break;
  }
}


unsigned int floatToIntCANbus(float myFloat, int offset, float factor) {
  // float myFloat = 2128.5;
  // unsigned int myInt = floatToIntCANbus(myFloat, 0, 0.125);
  //
  // Beginning with float of 2128.5, convert to CAN signal
  // values.
  // (int val) = ((float val) - offset) / factor;
  // (int val) = ((2128.5) - 0.0) / 0.125;
  // (int val) = 17028
   
  myFloat = myFloat - (float)offset;
  //Serial.println(myFloat);
  myFloat = myFloat / factor;
  //Serial.println(myFloat);
  //unsigned long myLong = (unsigned long) myFloat;
  //Serial.println(myLong);
  unsigned int myInt = (unsigned int) myFloat;
  return myInt;
}

byte getMsbFromInt(int myInt) {
  // int myInt = 17028;
  // byte msb = getMsbFromInt(myInt); 
  byte msb = myInt & 0xff;
  return msb;
}

byte getLsbFromInt(int myInt) {
  // int myInt = 17028;
  // byte lsb = getLsbFromInt(myInt);
  byte lsb = (myInt >>8) & 0xff;
  return lsb;
}


//****************************************************************

float fGetLgUnSignedRandFloat() {
  long unsigned myLong = random(100000000, 999999999);
  long divisor;
  int expnt = random(2,6);
  switch (expnt) {
	case 2:
	  divisor = 100;
	  break;
	case 3:
	  divisor = 1000;
	  break;
	case 4:
	  divisor = 10000;
	  break;
	case 5:
	  divisor = 100000;
	default:
	  divisor = 10;
  }
  float f = (float)myLong / divisor;
  /*
  int sign = random(10);
  if (sign <= 5) {
	f = f * (-1.0);
  }
  */
  return f;
}

CANbus_seeduino_ReceiveMsgD.ino

/*
  Seeduino CAN bus shield - receive message
  
  This Arduino receives messages via the attached 
  seeduino CAN bus shield.  
  
  Modified by Mark Kiehl
  Original source:  various

  Resources:
	DIO7  can LED
	DO10  SPI (SS)
	DO11  SPI (MOSI)
	DO12  SPI (MISO)
	DO13  SPI (SCK)

  CAN bus @ 250 kbps is limited to a sample rate of 100 Hz
  1000 ms = 1 sec = 1 Hz
  100 ms = 0.1 sec = 10 Hz
  10 ms = 0.01 sec = 100 Hz

  Don't mess with the CAN LED on DIO7 (seeduino).

  See also:  CANbus_seeduino_SendMsgE.ino

*/


// Show serial messages when DEBUG = true, otherwise minimize them.
#define DEBUG true

//////////////////////////////////////////////////////////////////////
// Seeduino CAN bus shield.  SPI on UNO DIO 10-13
// CAN library source:
//  Shenzhen SeeedStudio Co.,LTD.
//  http://www.seeedstudio.com
#include "mcp_can.h"
#include "SPI.h"
#define INT8U unsigned char
volatile INT8U Flag_Recv = 0;
unsigned long msgId;
INT8U len = 0;
INT8U buf[8];
// CAN message address
unsigned long CANmsgId;
// CAN message values
unsigned char CANmsg[8];
//////////////////////////////////////////////////////////////////////

void setup(){
  
  Serial.begin(9600);
  delay(5000);
  
  randomSeed(millis());

  //////////////////////////////////////////////////////////////////////
  // CAN_5KBPS, CAN_10KBPS, CAN_20KBPS, CAN_40KBPS, CAN_50KBPS, CAN_80KBPS, CAN_100KBPS, CAN_125KBPS, CAN_200KBPS, CAN_250KBPS and CAN_500KBPS. 
  if(CAN.begin(CAN_250KBPS) == CAN_OK) 
  {
	Serial.println ("can init ok");
  } else {
	Serial.println("Can init fail!!");
	while (1) {
	  for(int i = 20; i > 0; i--){
		digitalWrite(13, HIGH);
		delay(25);
		digitalWrite(13, LOW);
		delay(25);
	  }
	}
  }
  
  //Generally, set the mask to 0xFFFFFFF and then apply filters
  // init_Mask(unsigned char num, unsigned char ext, unsigned char ulData); 
  //CAN.init_Mask(0, 1, 0xFFFFFFF);     
  //CAN.init_Mask(1, 1, 0xFFFFFFF); 
  CAN.init_Mask(0, 1, 0x0);
  CAN.init_Mask(1, 1, 0x0);
  
  // init_Filt(unsigned char num, unsigned char ext, unsigned char ulData); 
  // filter (block) all messages using filter 0
  //CAN.init_Filt(0, 1, 0xFFFFFFF); 
  //CAN.init_Mask(1, 1, 0xFFFFFFF); 
  CAN.init_Filt(0, 1, 0x0);   
  //CAN.init_Filt(2, 1, 0xCF00400);
  //CAN.init_Filt(1, 1, 0x18FEEF00);
  //CAN.init_Filt(1, 1, 0x18FEF700);
  //CAN.init_Filt(1, 1, 0xCFF1351);
  
  //Generally, set the mask to 0xFFFFFFF and then apply filters
  //to each of the messages you want to allow to pass to the 
  //CAN bus shield.
  //
  //Mask 0xFFFFFFF & filter 0xFFFFFFF disables all messages
  //Mask 0xFFFFFFF & filter 0x0 disables all messages (mask disables filter)
  //Mask 0x0 & filter 0x0 allows all messages to pass
  //Mask 0x0 & filter 0xFFFFFFF allows msg 0xCF00400 to be received
  //Mask 0xFFFFFFF & filter 0xCF00400 allows msg 0xCF00400 to be received
  
  attachInterrupt(0, MCP2515_ISR, FALLING); // digital pin 2
  //////////////////////////////////////////////////////////////////////
  
  Serial.println("Setup complete");
  Serial.println("  ");
}


void MCP2515_ISR() {
	//  Interrupt Service Routine
	//  Do not use delay or millis here.
	//  Serial data received while here may be lost.
	//  Declare as volatile any variables that you modify
	//  within this function.
	Flag_Recv = 1;
	// stop interrupts so you can process the message
	noInterrupts(); 
}

void loop(){
  
  if(Flag_Recv) {
	 
	  Flag_Recv = 0;
			  
	  //  CAN.readMsgBuf(unsigned char msgId, unsigned char buf); 
	  //  "len" represents the data length.
	  //  "buf" is where you store the data.
	  CAN.readMsgBuf(&len, buf);    
	  
	  //CAN ID of the "send" node. 
	  //*** Must be called AFTER CAN.readMsgBuff, otherwise
	  //    you will get the last CAN ID, not the current value.  
	  CANmsgId=CAN.getCanId();

	  #if DEBUG
		Serial.print(CANmsgId, HEX);
		Serial.print("  ");         
		for (int i=0;i<8;i++) {
		  CANmsg[i] = buf[i];
		  Serial.print(CANmsg[i]);
		  Serial.print(",");
		}
	  Serial.println("  ");
	  #endif
	  
	  // do NOT use a delay.  Causes problems.
	  // Minimize activity and processing time, otherwise
	  // it may stop to work.
	
	  if (CANmsgId == 0) {
		// ignore.  Always receives msg with CANmsgID = 0 the first time.
		#if DEBUG
		  Serial.println("  ");
		#endif
	  } else if (CANmsgId == 0x0F100120) {
		float fReturn;
		//fReturn = getFloatFromCanMsg(msgStartBit, msgLength, msgOffset, msgFactor);
		fReturn = getFloatFromCanMsg(0, 16, 4000, 0.0001);
		#if DEBUG
		  Serial.print("CAN msg first two bytes converted back to float; f = ");
		  printFloat(fReturn, 4);
		  Serial.println("  ");
		  Serial.println("  ");
		#endif
	  } else if (CANmsgId == 0x0F200120) {
		float fReturn;
		//fReturn = getFloatFromCanMsg(msgStartBit, msgLength, msgOffset, msgFactor);
		fReturn = getFloatFromCanMsg(0, 16, -125, 0.03125);
		#if DEBUG
		  Serial.print("CAN msg first two bytes converted back to float; f = ");
		  printFloat(fReturn, 4);
		  Serial.println("  ");
		  Serial.println("  ");
		#endif
	  } else if (CANmsgId == 0x0F300120) {
		float fReturn;
		//fReturn = getFloatFromCanMsg(msgStartBit, msgLength, msgOffset, msgFactor);
		fReturn = getFloatFromCanMsg(0, 16, -125, 1.0);
		#if DEBUG
		  Serial.print("CAN msg first two bytes converted back to float; f = ");
		  printFloat(fReturn, 4);
		  Serial.println("  ");
		  Serial.println("  ");
		#endif
	  }
	  
	  // restart interrupts
	  interrupts();
 }
}

float getFloatFromCanMsg(int startBit, int msgLen, int offset, float factor) {
  // Read the data from msg[8] within CANmsg[] 
  // convert it with the passed parameters, and return a float value.
  // Assumes msgLen = 16
  byte msb;
  byte lsb;
  switch (startBit) {
	case 0:
	  msb = CANmsg[0];
	  lsb = CANmsg[1];
	  break;
	case 16:
	  msb = CANmsg[2];
	  lsb = CANmsg[3];
	  break;
	case 32:
	  msb = CANmsg[4];
	  lsb = CANmsg[5];
	  break;
	case 48:
	  msb = CANmsg[6];
	  lsb = CANmsg[7];
	  break;
  }
  int myInt = (lsb << 8) | msb;
  // float CANbusIntToFloat(unsigned int myInt, int offset, float factor) {
  float myFloat = CANbusIntToFloat(myInt, offset, factor);
  return myFloat;
}

float CANbusIntToFloat(unsigned int myInt, int offset, float factor) {
  // value in decimal = (CAN DEC value) * factor + offset
  // 17500 * 0.125 + 0 = 2187.5 rpm
  
  float myFloat = (float) myInt * factor + (float) offset;
  return myFloat;
}

void printFloat(float value, int places) {
  // printFloat prints out the float 'value' rounded to 'places' places after the decimal point
  // Follow with println as needed.
  
  // this is used to cast digits
  int digit;
  float tens = 0.1;
  int tenscount = 0;
  int i;
  float tempfloat = value;

  // make sure we round properly. this could use pow from math.h, but doesn't seem worth the import
  // if this rounding step isn't here, the value  54.321 prints as 54.3209

  // calculate rounding term d:   0.5/pow(10,places)  
  float d = 0.5;
  if (value < 0)
	d *= -1.0;
  // divide by ten for each decimal place
  for (i = 0; i < places; i++)
	d/= 10.0;    
  // this small addition, combined with truncation will round our values properly
  tempfloat +=  d;

  // first get value tens to be the large power of ten less than value
  // tenscount isn't necessary but it would be useful if you wanted to know after this how many chars the number will take

  if (value < 0)
	tempfloat *= -1.0;
  while ((tens * 10.0) <= tempfloat) {
	tens *= 10.0;
	tenscount += 1;
  }


  // write out the negative if needed
  if (value < 0)
	Serial.print('-');

  if (tenscount == 0)
	Serial.print(0, DEC);

  for (i=0; i< tenscount; i++) {
	digit = (int) (tempfloat/tens);
	Serial.print(digit, DEC);
	tempfloat = tempfloat - ((float)digit * tens);
	tens /= 10.0;
  }

  // if no places after decimal, stop now and return
  if (places <= 0)
	return;

  // otherwise, write the point and continue on
  Serial.print('.');  

  // now write out each decimal place by shifting digits one by one into the ones place and writing the truncated value
  for (i = 0; i < places; i++) {
	tempfloat *= 10.0;
	digit = (int) tempfloat;
	Serial.print(digit,DEC);  
	// once written, subtract off that digit
	tempfloat = tempfloat - (float) digit;
  }
  
}							

byte countDigits(int num){
  byte count=0;
  while(num){
  num=num/10;
  count++;
  }
  return count;
}

int getDigitRightToLeft(unsigned int number, int digit) {
  // gets the digit specified, counting from the left of the
  // decimal point and moving to the left.
  for (int i=0; i < digit-1; i++) {
  number /= 10;
  }
  return number % 10;
}

int getDigitLeftToRight(const int n, const int k) {
  //Get K-th Digit from a Number (zero-based index)
  // beginning from the left of the integer part of a number.
	switch(k)
	{
		case 0:return n%10;
		case 1:return n/10%10;
		case 2:return n/100%10;
		case 3:return n/1000%10;
		case 4:return n/10000%10;
		case 5:return n/100000%10;
		case 6:return n/1000000%10;
		case 7:return n/10000000%10;
		case 8:return n/100000000%10;
		case 9:return n/1000000000%10;
	}
	return 0;
}

 

CAN Bus Code Example #2

This example for sending and receiving CAN bus devices demonstrates sending a message with a standard frame and scaling the floating point value to send with a offset and factor.   All but one message is sent with the start bit of 0.   The one message with ID of 0x13 sends four floating point values scaled with the same offset and factor, with each value located in the CAN message at a different start bit.   The receiving CAN bus node then decodes each received message all the way to the original floating point value.   CAN ID masking is not demonstrated.  

Sending CAN bus Node


/*

  Arduino Uno + Arduino SeeedStudio CAN-BUS shield
  MCP2551 CAN bus controller

  Converts floating point value to a CAN message and then transmits it.
  Most messages use standard 11-bit frames, but one example sends an
  extended 29-bit frame. 
  Most messages use a start bit of 0, but one uses a start bit of 0, 16, 32, 48
  to send 4x encoded floating point values. 

  NOTE: The V1.0 shield uses pins 11, 12, and 13 to access 
  SPI on the Uno.  DO NOT USE THE LED ON PIN 13. 

  Uno SPI:
    13  SCK
    12  MISO
    11  MOSI
    10  SS

  D2 is for receive interrupt

  NOTE: The Seeed Arduino CAN library (mcp2515_can.h) can send an
        extended 19 bit CAN frame, but it cannot detect it as a receiver,
        nor can it decode the CAN ID correctly.  
  
  In Arduino IDE:
    Set board to 'Arduino Uno'
    Set programmer to 'AVRISP mkii'
    Set COM port
  
*/

//////////////////////////////////////////////////////////////////////////////
// TimerA
const unsigned long timerA = 2000;  
unsigned long timerAlap = millis();  // timer
/////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////
// CAN bus 

// Seeed_Arduino_CAN
// https://github.com/Seeed-Studio/Seeed_Arduino_CAN
#include <SPI.h>
#define CAN_2515

// For Arduino MCP2515 Hat:
// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
//const int SPI_CS_PIN = 9;
const int SPI_CS_PIN = 10;

#include "mcp2515_can.h"
mcp2515_can CAN(SPI_CS_PIN); // Set CS pin

unsigned long CANmsgId = 0xF30150;
unsigned char canMsg[8] = {0, 0, 0, 0, 0, 0, 0, 0};
int msgOffset = 0;
float msgFactor = 1.0;
float msgValue = 0.0;
int startBit = 0;


// These functions are written around the concept of using at
// least two bytes to represent a float or integer value.  
// This allows you to send up to four separate float or integer
// values within a CAN message.  

void updateCanMsgFromFloat(unsigned char *CANmsg, float floatVal, int startBit, int length, int msgOffset, float factor) {
  // Update msg[8] within CANmsg[] with the passed parameters
  // Assumes length = 16;
  // unsigned int floatToIntCANbus(float myFloat, int msgOffset, float factor) {
  unsigned int myInt = floatToIntCANbus(floatVal, msgOffset, factor);
  byte msb = getMsbFromInt(myInt);
  byte lsb = getLsbFromInt(myInt);
  switch (startBit) {
  case 0:
    CANmsg[0] = msb;
    CANmsg[1] = lsb;      
    break;
  case 16:
    CANmsg[2] = msb;
    CANmsg[3] = lsb;      
    break;
  case 32:
    CANmsg[4] = msb;
    CANmsg[5] = lsb;      
    break;
  case 48:
    CANmsg[6] = msb;
    CANmsg[7] = lsb;      
    break;
  }
}


unsigned int floatToIntCANbus(float myFloat, int msgOffset, float factor) {
  // float myFloat = 2128.5;
  // unsigned int myInt = floatToIntCANbus(myFloat, 0, 0.125);
  //
  // Beginning with float of 2128.5, convert to CAN signal
  // values.
  // (int val) = ((float val) - msgOffset) / factor;
  // (int val) = ((2128.5) - 0.0) / 0.125;
  // (int val) = 17028
   
  myFloat = myFloat - (float)msgOffset;
  //Serial.println(myFloat);
  myFloat = myFloat / factor;
  //Serial.println(myFloat);
  //unsigned long myLong = (unsigned long) myFloat;
  //Serial.println(myLong);
  unsigned int myInt = (unsigned int) myFloat;
  return myInt;
}

byte getMsbFromInt(int myInt) {
  // int myInt = 17028;
  // byte msb = getMsbFromInt(myInt); 
  byte msb = myInt & 0xff;
  return msb;
}

byte getLsbFromInt(int myInt) {
  // int myInt = 17028;
  // byte lsb = getLsbFromInt(myInt);
  byte lsb = (myInt >>8) & 0xff;
  return lsb;
}

////////////////////////////////////////////////////////////////


void setup () {

  SERIAL_PORT_MONITOR.begin(115200);
  while(!Serial){};

  // Initialize CAN and set baud rate
  //  .. CAN_250KBPS, CAN_500KBPS, CAN_666KBPS, CAN_1000KBPS
  while (CAN_OK != CAN.begin(CAN_250KBPS)) {             // init can bus : baudrate = 500k
      SERIAL_PORT_MONITOR.println("CAN init fail, retry...");
      delay(100);
  }
  SERIAL_PORT_MONITOR.println("CAN init ok!");

  timerAlap = millis(); // reset the timer
} // setup()




void loop() {
 
  //  Timer A
  if (timerAlap > millis())  timerAlap = millis();
  if (millis() - timerAlap > timerA) { 
    
    // clear out CANmsg[]
    for (int i=0; i<8; i++) {
      canMsg[i] = 0;
    }

    Serial.print("CANmsgId: 0x"); Serial.println(CANmsgId,HEX);

    // cycle through CANmsgId
    switch (CANmsgId) {
      case 0x0F:
        CANmsgId = 0x10;  // 0x10 = 16
        // temp in deg C
        // updateCanMsgFromFloat(float floatVal, int startBit, int Length, int msgOffset, float factor) 
        updateCanMsgFromFloat(canMsg, 23.4, 0, 16, -125, 0.03125);
        //CAN.sendMsgBuf(CANmsgId, frameType, length, CANmsg);    
        // 0x00: standard data frame
        // 0x01: extended data frame  (example says 0x02 is extended, but wrong!)
        // 0x30: standard remote frame ???
        // 0x32: extended remote frame ???
        CAN.MCP_CAN::sendMsgBuf(CANmsgId, 0x00, 8, canMsg);
        break;
      case 0x10:
        CANmsgId = 0x11;  // 0x11 = 17
        // speed 0 to 5000 rpm
        // updateCanMsgFromFloat(float floatVal, int startBit, int Length, int msgOffset, float factor) 
        updateCanMsgFromFloat(canMsg, 4567.8, 0, 16, 0, 0.125);
        //CAN.sendMsgBuf(CANmsgId, frameType, length, CANmsg);    
        CAN.MCP_CAN::sendMsgBuf(CANmsgId, 0x00, 8, canMsg);
        break;
      case 0x11:
        CANmsgId = 0x12;  // 0x12 = 18
        // mass flow kg/h
        // updateCanMsgFromFloat(float floatVal, int startBit, int Length, int msgOffset, float factor) 
        updateCanMsgFromFloat(canMsg, 0.23456, 0, 16, 0, 0.2);
        //CAN.sendMsgBuf(CANmsgId, frameType, length, CANmsg);    
        CAN.MCP_CAN::sendMsgBuf(CANmsgId, 0x00, 8, canMsg);
        break;
      case 0x12:
        CANmsgId = 0x13;  // 0x13 = 19
        // Define a message that sends 4x values by using all 4x byte
        // positions for startBit of 0, 16, 32, and 48.
        // Same offset and factor for each value
        // gps speed, angle, altitude, satellites
        // updateCanMsgFromFloat(float floatVal, int startBit, int Length, int msgOffset, float factor) 
        updateCanMsgFromFloat(canMsg, 2.0, 0, 16, 0, 1.0);
        updateCanMsgFromFloat(canMsg, 225.0, 16, 16, 0, 1.0);
        updateCanMsgFromFloat(canMsg, 1111.1, 32, 16, 0, 1.0);
        updateCanMsgFromFloat(canMsg, 11.0, 48, 16, 0, 1.0);
        //CAN.sendMsgBuf(CANmsgId, frameType, length, CANmsg);    
        CAN.MCP_CAN::sendMsgBuf(CANmsgId, 0x00, 8, canMsg);
        break;
      case 0x13:
        CANmsgId = 0x1C;  // 0x1C = 20
        // percentage 0 to 100%
        // updateCanMsgFromFloat(float floatVal, int startBit, int Length, int msgOffset, float factor) 
        updateCanMsgFromFloat(canMsg, 98.7, 0, 16, -125, 1.0);
        //CAN.sendMsgBuf(CANmsgId, frameType, length, CANmsg);    
        CAN.MCP_CAN::sendMsgBuf(CANmsgId, 0x00, 8, canMsg);
        break;
      case 0x1C:
        CANmsgId = 0x20;  // 0x20 = 32
        // gps coordinate
        // updateCanMsgFromFloat(float floatVal, int startBit, int Length, int msgOffset, float factor) 
        updateCanMsgFromFloat(canMsg, -76.5432, 0, 16, -90, 0.001);
        //CAN.sendMsgBuf(CANmsgId, frameType, length, CANmsg);    
        CAN.MCP_CAN::sendMsgBuf(CANmsgId, 0x00, 8, canMsg);
        break;
      case 0x20:
        CANmsgId = 0x90;  // 0x90 = 144
        // boolean
        // updateCanMsgFromFloat(float floatVal, int startBit, int Length, int msgOffset, float factor) 
        updateCanMsgFromFloat(canMsg, 1.0, 0, 16, 0, 1.0);
        //CAN.sendMsgBuf(CANmsgId, frameType, length, CANmsg);    
        CAN.MCP_CAN::sendMsgBuf(CANmsgId, 0x00, 8, canMsg);
        break;
      case 0x90:
        // Send an extended 29 bit frame (frameType = 0x01)
        // with longer hexadecimal CAN ID. 
        CANmsgId = 0xF30150;  // 0xF30150 = 
        // F-301-50, outside wind speed (kN) 
        // updateCanMsgFromFloat(float floatVal, int startBit, int Length, int msgOffset, float factor) 
        updateCanMsgFromFloat(canMsg, 17.4, 0, 16, -125, 1.0);
        // 0x01: extended data frame (NOT 0x02 as example suggests)
        //CAN.sendMsgBuf(CANmsgId, frameType, length, CANmsg);    
        CAN.MCP_CAN::sendMsgBuf(CANmsgId, 0x01, 8, canMsg);
        break;
      case 0xF30150:
        // barometric pressure (hPa) or altitude (m) on order of 1000
        CANmsgId = 0x0F;  // 0x0F = 15
        // updateCanMsgFromFloat(float floatVal, int startBit, int Length, int msgOffset, float factor) 
        updateCanMsgFromFloat(canMsg, 1234.5, 0, 16, 0, 0.125);
        //CAN.sendMsgBuf(CANmsgId, frameType, length, CANmsg);    
        CAN.MCP_CAN::sendMsgBuf(CANmsgId, 0x00, 8, canMsg);
        break;
      default:
        CANmsgId = 0x0F;  // 0x0F = 15
        // barometric pressure (hPa) or altitude (m) on order of 1000
        // updateCanMsgFromFloat(float floatVal, int startBit, int Length, int msgOffset, float factor) 
        updateCanMsgFromFloat(canMsg, 1234.5, 0, 16, 0, 0.125);
        //CAN.sendMsgBuf(CANmsgId, frameType, length, CANmsg);    
        CAN.MCP_CAN::sendMsgBuf(CANmsgId, 0x00, 8, canMsg);
        break;
    } // switch

    Serial.print("Sending to ID 0x");
    Serial.print(CANmsgId, HEX);
    Serial.print("\t");
    for (int i = 0; i < 8; i++) {
      Serial.print(canMsg[i], HEX);
      Serial.print("\t");
    }
    Serial.println("\n");      
    
    timerAlap = millis(); // reset the timer
  }

} // loop()

 

Receiving CAN bus Node


/*

  Arduino SeeedStudio CAN-BUS shield
  Arduino Uno + Arduino SeeedStudio CAN-BUS shield
  MCP2551 CAN bus controller

  Reads incoming standard 11-bit CAN bus messages and decodes them
  when the CAN ID is recognized.  No mask or filtering employed. 

  NOTE: The V1.0 shield uses pins 11, 12, and 13 to access 
  SPI on the Uno.  DO NOT USE THE LED ON PIN 13. 

  Uno SPI:
    13  SCK
    12  MISO
    11  MOSI
    10  SS

  D2 is for receive interrupt

  NOTE: The Seeed Arduino CAN library (mcp2515_can.h) can send an
        extended 19 bit CAN frame, but it cannot detect it as a receiver,
        nor can it decode the CAN ID correctly.  
  
  In Arduino IDE:
    Set board to 'Arduino Uno'
    Set programmer to 'AVRISP mkii'
    Set COM port
  
*/

/////////////////////////////////////////////////////////////////////////
// CAN bus 

// Seeed_Arduino_CAN
// https://github.com/Seeed-Studio/Seeed_Arduino_CAN
#include <SPI.h>
#define CAN_2515

// Set SPI CS Pin according to your hardware
const int SPI_CS_PIN = 10;
const int CAN_INT_PIN = 2;  // for RX interrupt only

#include "mcp2515_can.h"
mcp2515_can CAN(SPI_CS_PIN); // Set CS pin

uint8_t  msgType; // bit0: ext, bit1: rtr
int msgOffset = 0;
float msgFactor = 1.0;
float msgValue = 0.0;
int startBit = 0;


float getFloatFromCanMsg(unsigned char *CANmsg, int startBit, int msgLen, int msgOffset, float factor) {
  // Read the data from msg[8] within CANmsg[] 
  // convert it with the passed parameters, and return a float value.
  // Assumes msgLen = 16
  byte msb;
  byte lsb;
  switch (startBit) {
  case 0:
    msb = CANmsg[0];
    lsb = CANmsg[1];
    break;
  case 16:
    msb = CANmsg[2];
    lsb = CANmsg[3];
    break;
  case 32:
    msb = CANmsg[4];
    lsb = CANmsg[5];
    break;
  case 48:
    msb = CANmsg[6];
    lsb = CANmsg[7];
    break;
  }
  int myInt = (lsb << 8) | msb;
  // float CANbusIntToFloat(unsigned int myInt, int msgOffset, float factor) {
  float myFloat = CANbusIntToFloat(myInt, msgOffset, factor);
  return myFloat;
}

float CANbusIntToFloat(unsigned int myInt, int msgOffset, float factor) {
  // value in decimal = (CAN DEC value) * factor + offset
  // 17500 * 0.125 + 0 = 2187.5 rpm
  
  float myFloat = (float) myInt * factor + (float) msgOffset;
  return myFloat;
}

/////////////////////////////////////////////////////////////////////////


void setup() {

    SERIAL_PORT_MONITOR.begin(115200);

    // Initialize CAN and set baud rate
    //  .. CAN_250KBPS, CAN_500KBPS, CAN_666KBPS, CAN_1000KBPS
    while (CAN_OK != CAN.begin(CAN_250KBPS)) {             // init can bus : baudrate = 500k
        SERIAL_PORT_MONITOR.println("CAN init fail, retry...");
        delay(100);
    }
    SERIAL_PORT_MONITOR.println("CAN init ok!");
}


void loop() {
  unsigned char len = 0;
  unsigned char buf[8];

  // check if data coming
  if (CAN_MSGAVAIL == CAN.checkReceive()) {          

    CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf

    unsigned long canId = CAN.getCanId();

    /*
    //NOTE: The Seeed Arduino CAN library (mcp2515_can.h) can send an
    //      extended 19 bit CAN frame, but it cannot detect it as a receiver,
    //      nor can it decode the CAN ID correctly.  
    //      All of the following code is commented out because it is useless.
    msgType = (CAN.isExtendedFrame() << 0) |
           (CAN.isRemoteRequest() << 1);
    static const byte type2[] = {0x00, 0x02, 0x30, 0x32};
    // 0x00: standard data frame (11 bit)
    // 0x02: extended data frame (29 bit)
    // 0x30: standard remote frame RR (remote request)
    // 0x32: extended remote frame
    // determine the data frame type 
    switch (type2[msgType]){
      case 0x00:
        SERIAL_PORT_MONITOR.println("\tstd data frame");
        break;
      case 0x02:
        SERIAL_PORT_MONITOR.println("\text data frame");
        break;
      case 30:
        SERIAL_PORT_MONITOR.println("\tstd remote data frame");
        break;
      case 32:
        SERIAL_PORT_MONITOR.println("\text remote data frame");
        break;
    } // switch
    */

    float val1 = 0.0; float val2 = 0.0; float val3=0.0; float val4=0.0;
    
    switch (canId) {
      case 0x0F:
        // barometric pressure (hPa) or altitude (m) on order of 1000
        //msgValue = getFloatFromCanMsg(msg, msgStartBit, msgLength, msgOffset, msgFactor);
        val1 = getFloatFromCanMsg(buf, 0, 16, 0, 0.125);
        SERIAL_PORT_MONITOR.print("CAN ID 0x"); SERIAL_PORT_MONITOR.print(canId,HEX);
        SERIAL_PORT_MONITOR.print("\t barometric pressure [hPa]: "); SERIAL_PORT_MONITOR.println(val1, 3);
        break;
      case 0x10:
        // temp in deg C
        //msgValue = getFloatFromCanMsg(msg, msgStartBit, msgLength, msgOffset, msgFactor);
        val1 = getFloatFromCanMsg(buf, 0, 16, -125, 0.03125);
        SERIAL_PORT_MONITOR.print("CAN ID 0x"); SERIAL_PORT_MONITOR.print(canId,HEX);
        SERIAL_PORT_MONITOR.print("\t Temp [C]: "); SERIAL_PORT_MONITOR.println(val1, 1);
        break;
      case 0x11:
        // speed 0 to 5000 rpm
        //msgValue = getFloatFromCanMsg(msg, msgStartBit, msgLength, msgOffset, msgFactor);
        val1 = getFloatFromCanMsg(buf, 0, 16, 0, 0.125);
        SERIAL_PORT_MONITOR.print("CAN ID 0x"); SERIAL_PORT_MONITOR.print(canId,HEX);
        SERIAL_PORT_MONITOR.print("\t Engine speed [rpm]: "); SERIAL_PORT_MONITOR.println(val1, 3);
        break;
      case 0x12:
        // mass flow kg/h
        //msgValue = getFloatFromCanMsg(msg, msgStartBit, msgLength, msgOffset, msgFactor);
        val1 = getFloatFromCanMsg(buf, 0, 16, 0, 0.2);
        SERIAL_PORT_MONITOR.print("CAN ID 0x"); SERIAL_PORT_MONITOR.print(canId,HEX);
        SERIAL_PORT_MONITOR.print("\t mass flow [kg/h]: "); SERIAL_PORT_MONITOR.println(val1, 3);
        break;
      case 0x13:
        // gps speed, angle, altitude, satellites
        // raw:  0x2  0x0  0xE1  0x0 0x57  0x4 0xB 0x0
        SERIAL_PORT_MONITOR.print("CAN ID 0x"); SERIAL_PORT_MONITOR.println(canId,HEX);
        
        // GPS speed [mph]
        //msgValue = getFloatFromCanMsg(msg, msgStartBit, msgLength, msgOffset, msgFactor);
        val1 = getFloatFromCanMsg(buf, 0, 16, 0, 1.0);
        SERIAL_PORT_MONITOR.print("\t GPS speed [mph]: "); SERIAL_PORT_MONITOR.println(val1, 1);
        
        // GPS angle [deg]
        startBit = 16;
        //msgValue = getFloatFromCanMsg(msg, msgStartBit, msgLength, msgOffset, msgFactor);
        val2 = getFloatFromCanMsg(buf, 16, 16, 0, 1.0);
        SERIAL_PORT_MONITOR.print("\t GPS angle [deg]: "); SERIAL_PORT_MONITOR.println(val2,1);
        
        // GPS altitude [m]
        //msgValue = getFloatFromCanMsg(msg, msgStartBit, msgLength, msgOffset, msgFactor);
        val3 = getFloatFromCanMsg(buf, 32, 16, 0, 1.0);
        SERIAL_PORT_MONITOR.print("\t GPS altitude [m]: "); SERIAL_PORT_MONITOR.println(val3,1);
        
        // GPS satellites [count]
        //msgValue = getFloatFromCanMsg(msg, msgStartBit, msgLength, msgOffset, msgFactor);
        val4 = getFloatFromCanMsg(buf, 48, 16, 0, 1.0);
        SERIAL_PORT_MONITOR.print("\t Satellites: "); SERIAL_PORT_MONITOR.println(val4);
        break;
      case 0x1C:
        // percentage
        //msgValue = getFloatFromCanMsg(msg, msgStartBit, msgLength, msgOffset, msgFactor);
        val1 = getFloatFromCanMsg(buf, 0, 16, -125, 1.0);
        SERIAL_PORT_MONITOR.print("CAN ID 0x"); SERIAL_PORT_MONITOR.print(canId,HEX);
        SERIAL_PORT_MONITOR.print("\t Percentage [%]: "); SERIAL_PORT_MONITOR.println(val1, 3);
        break;
      case 0x20:
        // gps coordinate
        //msgValue = getFloatFromCanMsg(msg, msgStartBit, msgLength, msgOffset, msgFactor);
        val1 = getFloatFromCanMsg(buf, 0, 16, -90, 0.001);
        SERIAL_PORT_MONITOR.print("CAN ID 0x"); SERIAL_PORT_MONITOR.print(canId,HEX);
        SERIAL_PORT_MONITOR.print("\t Longitude [decimal degrees]: "); SERIAL_PORT_MONITOR.println(val1, 3);
        break;
      case 0x90:
        // boolean
        //msgValue = getFloatFromCanMsg(msg, msgStartBit, msgLength, msgOffset, msgFactor);
        val1 = getFloatFromCanMsg(buf, 0, 16, 0, 1.0);
        SERIAL_PORT_MONITOR.print("CAN ID 0x"); SERIAL_PORT_MONITOR.print(canId,HEX);
        SERIAL_PORT_MONITOR.print("\t boolean: "); SERIAL_PORT_MONITOR.println(val1, 3);
        break;
      default:
        // Unknown CAN ID
        SERIAL_PORT_MONITOR.print("Rx raw data from ID: 0x");
        SERIAL_PORT_MONITOR.print(canId, HEX);
        // Show the raw CAN data..
        for (int i = 0; i < len; i++) { 
            SERIAL_PORT_MONITOR.print("0x");
            SERIAL_PORT_MONITOR.print(buf[i], HEX);
            SERIAL_PORT_MONITOR.print("\t");
        }
        SERIAL_PORT_MONITOR.println();
        break;
    } // switch
    SERIAL_PORT_MONITOR.println();    
  }
} // loop()

/*
  Serial output: 
  
  CAN ID 0x20   Longitude [decimal degrees]: -76.544
  
  CAN ID 0x90  boolean: 1.000
  
  CAN ID 0xF   barometric pressure [hPa]: 1234.500
  
  CAN ID 0x10  Temp [C]: 23.4
  
  CAN ID 0x11  Engine speed [rpm]: 4567.750
  
  CAN ID 0x12  mass flow [kg/h]: 0.200
  
  CAN ID 0x13
     GPS speed [mph]: 2.0
     GPS angle [deg]: 225.0
     GPS altitude [m]: 1111.0
     Satellites: 11.00
  
  CAN ID 0x1C  Percentage [%]: 98.000

*/

 

 


Do you need help developing or customizing a IoT product for your needs?   Send me an email requesting a free one hour phone / web share consultation.  

 

The information presented on this website is for the author's use only.   Use of this information by anyone other than the author is offered as guidelines and non-professional advice only.   No liability is assumed by the author or this web site.