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.
How to adapt the shield to different pins for SPI
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.
Seeed_Arduino_CAN library
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 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.
/* 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; }
/* 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; }
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.
/*
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()
/*
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.