Add CAN BUS Serial monitor example

This commit is contained in:
Baozhu Zuo
2020-12-10 17:10:14 +08:00
parent 62f484a4b4
commit a899ff745b
4 changed files with 930 additions and 0 deletions
+556
View File
@@ -0,0 +1,556 @@
/*****************************************************************************************
* This is implementation of CAN BUS ASCII protocol based on LAWICEL v1.3 serial protocol
* of CanSerial/CANUSB device (http://www.CanSerial.com/docs/CanSerial_v3.pdf)
*
* Made for Arduino with Seeduino/ElecFreaks CAN BUS Shield based on MCP2515
*
* Copyright (C) 2015 Anton Viktorov <latonita@yandex.ru>
* https://github.com/latonita/can-ascii
*
* This library is free software. You may use/redistribute it under The MIT License terms.
*
*****************************************************************************************/
#include <SPI.h>
#include "mcp_can.h"
#include "can-serial.h"
#define LOGGING_ENABLED
#ifdef LOGGING_ENABLED
#define dbg_begin(x) debug.begin(x)
#define dbg0(x) debug.print(x)
#define dbg1(x) debug.println(x)
#define dbg2(x,y) debug.print(x); debug.println(y)
#define dbgH(x) debug.print(x,HEX)
#define DEBUG_RX_PIN 8
#define DEBUG_TX_PIN 9
#else
#define dbg_begin(x)
#define dbg0(x)
#define dbg1(x)
#define dbg2(x,y)
#define dbgH(x)
#endif
#ifdef LOGGING_ENABLED
// software serial #2: TX = digital pin 8, RX = digital pin 9
// on the Mega, use other pins instead, since 8 and 9 don't work on the Mega
SoftwareSerial debug(DEBUG_RX_PIN, DEBUG_TX_PIN);
//#define debug Serial
#endif
CanSerial* CanSerial::_instance = 0;
CanSerial* CanSerial::instance() {
if (_instance == 0)
_instance = new CanSerial();
return _instance;
}
void CanSerial::init(INT8U defaultCanSpeed, const INT8U clock) {
dbg_begin(LWUART_DEFAULT_BAUD_RATE); // logging through software serial
dbg1("CAN ASCII. Welcome to debug");
instance()->LWUARTCanSpeedSelection = defaultCanSpeed;
instance()->LWUARTMcpModuleClock = clock;
instance()->initFunc();
}
void CanSerial::setFilter(INT8U (*userFunc)(INT32U)) {
instance()->setFilterFunc(userFunc);
}
void CanSerial::loop() {
instance()->loopFunc();
}
void CanSerial::serialEvent() {
instance()->serialEventFunc();
}
void CanSerial::initFunc() {
if (!inputString.reserve(LWUART_INPUT_STRING_BUFFER_SIZE)) {
dbg0("inputString.reserve failed in initFunc. less optimal String work is expected");
}
// LWUARTAutoStart = true; //todo: read from eeprom
// LWUARTAutoPoll = false; //todo: read from eeprom
// LWUARTTimeStamp = //read from eeprom
// LWUARTMessage[0] = 'Z'; LWUARTMessage[1] = '1'; exec();
//if (LWUARTAutoStart) {
inputString = "O\0x0D";
stringComplete = true;
loopFunc();
//}
}
void CanSerial::setFilterFunc(INT8U (*userFunc)(INT32U)) {
instance()->userAddressFilterFunc = userFunc;
}
void CanSerial::loopFunc() {
if (stringComplete) {
int len = inputString.length();
if (len > 0 && len < LWUART_FRAME_MAX_SIZE) {
strcpy((char*)LWUARTMessage, inputString.c_str());
exec();
}
// clear the string:
inputString = "";
stringComplete = false;
}
if (LWUARTCanChannelMode != LWUART_STATUS_CAN_CLOSED) {
int recv = 0;
while (CAN_MSGAVAIL == checkReceive() && recv++<5) {
dbg0('+');
if (CAN_OK == receiveSingleFrame()) {
Serial.write(LWUART_CR);
}
}
Serial.flush();
}
}
void CanSerial::serialEventFunc() {
while (Serial.available()) {
char inChar = (char)Serial.read();
inputString += inChar;
if (inChar == LWUART_CR) {
stringComplete = true;
}
}
}
INT8U CanSerial::exec() {
dbg2("Command received:", inputString);
LWUARTLastErr = parseAndRunCommand();
switch (LWUARTLastErr) {
case LWUART_OK:
Serial.write(LWUART_RET_ASCII_OK);
break;
case LWUART_OK_SMALL:
Serial.write(LWUART_RET_ASCII_OK_SMALL);
Serial.write(LWUART_RET_ASCII_OK);
break;
case LWUART_OK_BIG:
Serial.write(LWUART_RET_ASCII_OK_BIG);
Serial.write(LWUART_RET_ASCII_OK);
break;
case LWUART_ERR_NOT_IMPLEMENTED:
// Choose behavior: will it fail or not when not implemented command comes in. Some can monitors might be affected by this selection.
Serial.write(LWUART_RET_ASCII_ERROR);
//Serial.write(LWUART_RET_ASCII_OK);
break;
default:
Serial.write(LWUART_RET_ASCII_ERROR);
}
return 0;
}
INT8U CanSerial::parseAndRunCommand() {
INT8U ret = LWUART_OK;
INT8U idx = 0;
INT8U err = 0;
LWUARTLastErr = LWUART_OK;
switch (LWUARTMessage[0]) {
case LWUART_CMD_SETUP:
// Sn[CR] Setup with standard CAN bit-rates where n is 0-9.
if (LWUARTCanChannelMode == LWUART_STATUS_CAN_CLOSED) {
idx = HexHelper::parseNibbleWithLimit(LWUARTMessage[1], LWUART_CAN_BAUD_NUM);
LWUARTCanSpeedSelection = LWUARTCanBaudRates[idx];
}
else {
ret = LWUART_ERR;
}
break;
case LWUART_CMD_SETUP_BTR:
// sxxyy[CR] Setup with BTR0/BTR1 CAN bit-rates where xx and yy is a hex value.
ret = LWUART_ERR; break;
case LWUART_CMD_OPEN:
// O[CR] Open the CAN channel in normal mode (sending & receiving).
if (LWUARTCanChannelMode == LWUART_STATUS_CAN_CLOSED) {
ret = openCanBus();
if (ret == LWUART_OK) {
LWUARTCanChannelMode = LWUART_STATUS_CAN_OPEN_NORMAL;
}
}
else {
ret = LWUART_ERR;
}
break;
case LWUART_CMD_LISTEN:
// L[CR] Open the CAN channel in listen only mode (receiving).
if (LWUARTCanChannelMode == LWUART_STATUS_CAN_CLOSED) {
ret = openCanBus();
if (ret == LWUART_OK) {
LWUARTCanChannelMode = LWUART_STATUS_CAN_OPEN_LISTEN;
}
}
else {
ret = LWUART_ERR;
}
break;
case LWUART_CMD_CLOSE:
// C[CR] Close the CAN channel.
if (LWUARTCanChannelMode != LWUART_STATUS_CAN_CLOSED) {
LWUARTCanChannelMode = LWUART_STATUS_CAN_CLOSED;
}
else {
ret = LWUART_ERR;
}
break;
case LWUART_CMD_TX11:
// tiiildd...[CR] Transmit a standard (11bit) CAN frame.
if (LWUARTCanChannelMode == LWUART_STATUS_CAN_OPEN_NORMAL) {
parseCanStdId();
LWUARTPacketLen = HexHelper::parseNibbleWithLimit(LWUARTMessage[LWUART_OFFSET_STD_PKT_LEN], LWUART_FRAME_MAX_LENGTH + 1);
for (; idx < LWUARTPacketLen; idx++) {
LWUARTBuffer[idx] = HexHelper::parseFullByte(LWUARTMessage[LWUART_OFFSET_STD_PKT_DATA + idx * 2], LWUARTMessage[LWUART_OFFSET_STD_PKT_DATA + idx * 2 + 1]);
}
INT8U mcpErr = sendMsgBuf(LWUARTCanId, 0, 0, LWUARTPacketLen, LWUARTBuffer);
if (mcpErr != CAN_OK) {
ret = LWUART_ERR;
} else if (LWUARTAutoPoll) {
ret = LWUART_OK_SMALL;
}
}
else {
ret = LWUART_ERR;
}
break;
case LWUART_CMD_TX29:
// Tiiiiiiiildd...[CR] Transmit an extended (29bit) CAN frame
if (LWUARTCanChannelMode == LWUART_STATUS_CAN_OPEN_NORMAL) {
parseCanExtId();
LWUARTPacketLen = HexHelper::parseNibbleWithLimit(LWUARTMessage[LWUART_OFFSET_EXT_PKT_LEN], LWUART_FRAME_MAX_LENGTH + 1);
for (; idx < LWUARTPacketLen; idx++) {
LWUARTBuffer[idx] = HexHelper::parseFullByte(LWUARTMessage[LWUART_OFFSET_EXT_PKT_DATA + idx * 2], LWUARTMessage[LWUART_OFFSET_EXT_PKT_DATA + idx * 2 + 1]);
}
if (CAN_OK != sendMsgBuf(LWUARTCanId, 1, 0, LWUARTPacketLen, LWUARTBuffer)) {
ret = LWUART_ERR;
} else if (LWUARTAutoPoll) {
ret = LWUART_OK_BIG;
} else {
ret = LWUART_OK;
}
}
break;
case LWUART_CMD_RTR11:
// riiil[CR] Transmit an standard RTR (11bit) CAN frame.
if (LWUARTCanChannelMode == LWUART_STATUS_CAN_OPEN_NORMAL) {
parseCanStdId();
LWUARTPacketLen = HexHelper::parseNibbleWithLimit(LWUARTMessage[LWUART_OFFSET_STD_PKT_LEN], LWUART_FRAME_MAX_LENGTH + 1);
if (CAN_OK != sendMsgBuf(LWUARTCanId, 0, 1, LWUARTPacketLen, LWUARTBuffer)) {
ret = LWUART_ERR;
}
else if (LWUARTAutoPoll) {
ret = LWUART_OK_SMALL;
}
}
else {
ret = LWUART_ERR;
}
break;
case LWUART_CMD_RTR29:
// Riiiiiiiil[CR] Transmit an extended RTR (29bit) CAN frame.
if (LWUARTCanChannelMode == LWUART_STATUS_CAN_OPEN_NORMAL) {
parseCanExtId();
LWUARTPacketLen = HexHelper::parseNibbleWithLimit(LWUARTMessage[LWUART_OFFSET_EXT_PKT_LEN], LWUART_FRAME_MAX_LENGTH + 1);
if (CAN_OK != sendMsgBuf(LWUARTCanId, 1, 1, LWUARTPacketLen, LWUARTBuffer)) {
ret = LWUART_ERR;
}
else if (LWUARTAutoPoll) {
ret = LWUART_OK_SMALL; // not a typo. strangely CanSerial_v3.pdf tells to return "z[CR]", not "Z[CR]" as in 29bit. todo: check if it is error in pdf???
}
} else {
ret = LWUART_ERR;
}
break;
case LWUART_CMD_POLL_ONE:
// P[CR] Poll incomming FIFO for CAN frames (single poll)
if (LWUARTCanChannelMode != LWUART_STATUS_CAN_CLOSED && LWUARTAutoPoll == LWUART_AUTOPOLL_OFF) {
if (CAN_MSGAVAIL == checkReceive()) {
ret = receiveSingleFrame();
}
} else {
ret = LWUART_ERR;
}
break;
case LWUART_CMD_POLL_MANY:
// A[CR] Polls incomming FIFO for CAN frames (all pending frames)
if (LWUARTCanChannelMode != LWUART_STATUS_CAN_CLOSED && LWUARTAutoPoll == LWUART_AUTOPOLL_OFF) {
while (CAN_MSGAVAIL == checkReceive()) {
ret = ret ^ receiveSingleFrame();
if (ret != CAN_OK)
break;
Serial.write(LWUART_CR);
}
if (ret == CAN_OK)
Serial.print(LWUART_ALL);
} else {
ret = LWUART_ERR;
}
break;
case LWUART_CMD_FLAGS:
// F[CR] Read Status Flags.
// LAWICEL CanSerial and CANUSB have some specific errors which differ from MCP2515/MCP2551 errors. We just return MCP2515 error.
Serial.print(LWUART_FLAG);
if (LWUARTCAN.checkError(&err) == CAN_OK)
err = 0;
HexHelper::printFullByte(err & MCP_EFLG_ERRORMASK);
break;
case LWUART_CMD_AUTOPOLL:
// Xn[CR] Sets Auto Poll/Send ON/OFF for received frames.
if (LWUARTCanChannelMode == LWUART_STATUS_CAN_CLOSED) {
LWUARTAutoPoll = (LWUARTMessage[1] == LWUART_ON_ONE) ? LWUART_AUTOPOLL_ON : LWUART_AUTOPOLL_OFF;
//todo: save to eeprom
} else {
ret = LWUART_ERR;
}
break;
case LWUART_CMD_FILTER:
// Wn[CR] Filter mode setting. By default CanSerial works in dual filter mode (0) and is backwards compatible with previous CanSerial versions.
ret = LWUART_ERR_NOT_IMPLEMENTED; break;
case LWUART_CMD_ACC_CODE:
// Mxxxxxxxx[CR] Sets Acceptance Code Register (ACn Register of SJA1000). // we use MCP2515,
ret = LWUART_ERR_NOT_IMPLEMENTED; break;
case LWUART_CMD_ACC_MASK:
// mxxxxxxxx[CR] Sets Acceptance Mask Register (AMn Register of SJA1000).
ret = LWUART_ERR_NOT_IMPLEMENTED; break;
case LWUART_CMD_UART:
// Un[CR] Setup UART with a new baud rate where n is 0-6.
idx = HexHelper::parseNibbleWithLimit(LWUARTMessage[1], LWUART_UART_BAUD_NUM);
Serial.begin(LWUARTSerialBaudRates[idx]);
break;
case LWUART_CMD_VERSION1:
case LWUART_CMD_VERSION2:
// V[CR] Get Version number of both CanSerial hardware and software
Serial.print(LWUART_LAWICEL_VERSION_STR);
break;
case LWUART_CMD_SERIAL:
// N[CR] Get Serial number of the CanSerial.
Serial.print(LWUART_LAWICEL_SERIAL_NUM);
break;
case LWUART_CMD_TIMESTAMP:
// Zn[CR] Sets Time Stamp ON/OFF for received frames only. Z0 - OFF, Z1 - Lawicel's timestamp 2 bytes, Z2 - arduino timestamp 4 bytes.
if (LWUARTCanChannelMode == LWUART_STATUS_CAN_CLOSED) {
// LWUARTTimeStamp = (LWUARTMessage[1] == LWUART_ON_ONE);
if (LWUARTMessage[1] == LWUART_ON_ONE) {
LWUARTTimeStamp = LWUART_TIMESTAMP_ON_NORMAL;
}
else if (LWUARTMessage[1] == LWUART_ON_TWO) {
LWUARTTimeStamp = LWUART_TIMESTAMP_ON_EXTENDED;
}
else {
LWUARTTimeStamp = LWUART_TIMESTAMP_OFF;
}
}
else {
ret = LWUART_ERR;
}
break;
case LWUART_CMD_AUTOSTART:
// Qn[CR] Auto Startup feature (from power on).
if (LWUARTCanChannelMode != LWUART_STATUS_CAN_CLOSED) {
if (LWUARTMessage[1] == LWUART_ON_ONE) {
LWUARTAutoStart = LWUART_AUTOSTART_ON_NORMAL;
}
else if (LWUARTMessage[1] == LWUART_ON_TWO) {
LWUARTAutoStart = LWUART_AUTOSTART_ON_LISTEN;
}
else {
LWUARTAutoStart = LWUART_AUTOSTART_OFF;
}
//todo: save to eeprom
}
else {
ret = LWUART_ERR;
}
break;
default:
ret = LWUART_ERR_UNKNOWN_CMD;
}
return ret;
}
INT8U CanSerial::checkReceive() {
#ifndef _MCP_FAKE_MODE_
return LWUARTCAN.checkReceive();
#else
return CAN_MSGAVAIL;
#endif
}
INT8U CanSerial::readMsgBufID(INT32U *ID, INT8U *len, INT8U buf[]) {
#ifndef _MCP_FAKE_MODE_
return LWUARTCAN.readMsgBufID(ID, len, buf);
#else
*ID = random(0x100, 0x110);
*len = 4;
buf[0] = random(0x01, 0x10);
buf[1] = random(0xa1, 0xf0);
buf[2] = 0x00;
buf[3] = 0x00;
return CAN_OK;
#endif
}
INT8U CanSerial::receiveSingleFrame() {
INT8U ret = LWUART_OK;
INT8U idx = 0;
if (CAN_OK == readMsgBufID(&LWUARTCanId, &LWUARTPacketLen, LWUARTBuffer)) {
if (LWUARTCanId > 0x1FFFFFFF) {
ret = LWUART_ERR; // address if totally wrong
}
else if (checkPassFilter(LWUARTCanId)) {// do we want to skip some addresses?
if (isExtendedFrame()) {
Serial.print(LWUART_TR29);
HexHelper::printFullByte(HIGH_BYTE(HIGH_WORD(LWUARTCanId)));
HexHelper::printFullByte(LOW_BYTE(HIGH_WORD(LWUARTCanId)));
HexHelper::printFullByte(HIGH_BYTE(LOW_WORD(LWUARTCanId)));
HexHelper::printFullByte(LOW_BYTE(LOW_WORD(LWUARTCanId)));
}
else {
Serial.print(LWUART_TR11);
HexHelper::printNibble(HIGH_BYTE(LOW_WORD(LWUARTCanId)));
HexHelper::printFullByte(LOW_BYTE(LOW_WORD(LWUARTCanId)));
}
//write data len
HexHelper::printNibble(LWUARTPacketLen);
//write data
for (idx = 0; idx < LWUARTPacketLen; idx++) {
HexHelper::printFullByte(LWUARTBuffer[idx]);
}
//write timestamp if needed
if (LWUARTTimeStamp != LWUART_TIMESTAMP_OFF) {
INT32U time = millis();
if (LWUARTTimeStamp == LWUART_TIMESTAMP_ON_NORMAL) {
// standard LAWICEL protocol. two bytes.
time %= 60000;
} else {
// non standard protocol - 4 bytes timestamp
HexHelper::printFullByte(HIGH_BYTE(HIGH_WORD(time)));
HexHelper::printFullByte(LOW_BYTE(HIGH_WORD(time)));
}
HexHelper::printFullByte(HIGH_BYTE(LOW_WORD(time)));
HexHelper::printFullByte(LOW_BYTE(LOW_WORD(time)));
}
}
}
else {
ret = LWUART_ERR;
}
return ret;
}
INT8U CanSerial::isExtendedFrame() {
#ifndef _MCP_FAKE_MODE_
return LWUARTCAN.isExtendedFrame();
#else
return LWUARTCanId > 0x7FF ? 1 : 0; //simple hack for fake mode
#endif
}
INT8U CanSerial::checkPassFilter(INT32U addr) {
if (userAddressFilterFunc == 0)
return LWUART_FILTER_PROCESS;
return (*userAddressFilterFunc)(addr);
}
INT8U CanSerial::openCanBus() {
INT8U ret = LWUART_OK;
#ifndef _MCP_FAKE_MODE_
if (CAN_OK != LWUARTCAN.begin(LWUARTCanSpeedSelection, LWUARTMcpModuleClock))
ret = LWUART_ERR;
#endif
return ret;
}
INT8U CanSerial::sendMsgBuf(INT32U id, INT8U ext, INT8U rtr, INT8U len, INT8U *buf) {
#ifndef _MCP_FAKE_MODE_
return LWUARTCAN.sendMsgBuf(id, ext, rtr, len, buf);
#else
Serial.print("<sending:");
Serial.print(id, HEX);
Serial.print(',');
if (ext) Serial.print('+');
else Serial.print('-');
if (rtr) Serial.print('+');
else Serial.print('-');
Serial.print(',');
Serial.print(len, DEC);
Serial.print(',');
int i;
for (i = 0; i < len; i++) printFullByte(buf[i]);
return CAN_OK;
#endif
}
void CanSerial::parseCanStdId() {
LWUARTCanId = (((INT32U)HexHelper::parseNibble(LWUARTMessage[1])) << 8)
+ (((INT32U)HexHelper::parseNibble(LWUARTMessage[2])) << 4)
+ (((INT32U)HexHelper::parseNibble(LWUARTMessage[3])));
LWUARTCanId &= 0x7FF;
}
void CanSerial::parseCanExtId() {
LWUARTCanId = (((INT32U)HexHelper::parseNibble(LWUARTMessage[1])) << 28)
+ (((INT32U)HexHelper::parseNibble(LWUARTMessage[2])) << 24)
+ (((INT32U)HexHelper::parseNibble(LWUARTMessage[3])) << 20)
+ (((INT32U)HexHelper::parseNibble(LWUARTMessage[4])) << 16)
+ (((INT32U)HexHelper::parseNibble(LWUARTMessage[5])) << 12)
+ (((INT32U)HexHelper::parseNibble(LWUARTMessage[6])) << 8)
+ (((INT32U)HexHelper::parseNibble(LWUARTMessage[7])) << 4)
+ (((INT32U)HexHelper::parseNibble(LWUARTMessage[8])));
LWUARTCanId &= 0x1FFFFFFF;
}
void HexHelper::printFullByte(INT8U b) {
if (b < 0x10) {
Serial.print('0');
// dbg0('0');
}
Serial.print(b, HEX);
//dbgH(b);
}
void HexHelper::printNibble(INT8U b) {
Serial.print(b & 0x0F, HEX);
//dbgH(b & 0x0F);
}
INT8U HexHelper::parseNibble(INT8U hex) {
INT8U ret = 0;
if (hex >= '0' && hex <= '9') {
ret = hex - '0';
} else if (hex >= 'a' && hex <= 'f') {
ret = hex - 'a' + 10;
} else if (hex >= 'A' && hex <= 'F') {
ret = hex - 'A' + 10;
} // else error, return 0
return ret;
}
INT8U HexHelper::parseFullByte(INT8U H, INT8U L) {
return (parseNibble(H) << 4) + parseNibble(L);
}
INT8U HexHelper::parseNibbleWithLimit(INT8U hex, INT8U limit) {
INT8U ret = parseNibble(hex);
if (ret < limit)
return ret;
else
return 0;
}
+244
View File
@@ -0,0 +1,244 @@
/*****************************************************************************************
* This is implementation of CAN BUS ASCII protocol based on LAWICEL v1.3 serial protocol
* of CanSerial/CANUSB device (http://www.CanSerial.com/docs/CanSerial_v3.pdf)
*
* Made for Arduino with Seeduino/ElecFreaks CAN BUS Shield based on MCP2515
*
* Copyright (C) 2015 Anton Viktorov <latonita@yandex.ru>
* https://github.com/latonita/can-ascii
*
* This library is free software. You may use/redistribute it under The MIT License terms.
*
*****************************************************************************************/
#ifndef _CAN_SERIAL_H_
#define _CAN_SERIAL_H_
#include "mcp_can.h"
#define LOGGING_ENABLED
#ifdef LOGGING_ENABLED
#include "SoftwareSerial.h"
#define dbg_begin(x) debug.begin(x)
#define dbg0(x) debug.print(x)
#define dbg1(x) debug.println(x)
#define dbg2(x,y) debug.print(x); debug.println(y)
#define dbgH(x) debug.print(x,HEX)
#define DEBUG_RX_PIN 8
#define DEBUG_TX_PIN 9
#else
#define dbg_begin(x)
#define dbg0(x)
#define dbg1(x)
#define dbg2(x,y)
#define dbgH(x)
#endif
#define LWUART_LAWICEL_VERSION_STR "V1013"
#define LWUART_LAWICEL_SERIAL_NUM "NA123"
#define LWUART_CAN_BUS_SHIELD_CS_PIN 10
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Commands not supported/not implemented:
// s, W, M, m, U
//
// Commands modified:
// S - supports not declared 83.3 rate straight away (S9)
// refer to https://github.com/latonita/CAN_BUS_Shield fork with 83.3 support, easy to add.
// F - returns MCP2515 error flags
// Z - extra Z2 option enables 4 byte timestamp vs standard 2 byte (60000ms max)
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// CODE SUPPORTED SYNTAX DESCRIPTION
//
#define LWUART_CMD_SETUP 'S' // YES+ Sn[CR] Setup with standard CAN bit-rates where n is 0-8.
// S0 10Kbit S4 125Kbit S8 1Mbit
// S1 20Kbit S5 250Kbit S9 83.3Kbit
// S2 50Kbit S6 500Kbit
// S3 100Kbit S7 800Kbit
#define LWUART_CMD_SETUP_BTR 's' // - sxxyy[CR] Setup with BTR0/BTR1 CAN bit-rates where xx and yy is a hex value.
#define LWUART_CMD_OPEN 'O' // YES O[CR] Open the CAN channel in normal mode (sending & receiving).
#define LWUART_CMD_LISTEN 'L' // YES L[CR] Open the CAN channel in listen only mode (receiving).
#define LWUART_CMD_CLOSE 'C' // YES C[CR] Close the CAN channel.
#define LWUART_CMD_TX11 't' // YES tiiildd...[CR] Transmit a standard (11bit) CAN frame.
#define LWUART_CMD_TX29 'T' // YES Tiiiiiiiildd...[CR] Transmit an extended (29bit) CAN frame
#define LWUART_CMD_RTR11 'r' // YES riiil[CR] Transmit an standard RTR (11bit) CAN frame.
#define LWUART_CMD_RTR29 'R' // YES Riiiiiiiil[CR] Transmit an extended RTR (29bit) CAN frame.
#define LWUART_CMD_POLL_ONE 'P' // YES P[CR] Poll incomming FIFO for CAN frames (single poll)
#define LWUART_CMD_POLL_MANY 'A' // YES A[CR] Polls incomming FIFO for CAN frames (all pending frames)
#define LWUART_CMD_FLAGS 'F' // YES+ F[CR] Read Status Flags.
#define LWUART_CMD_AUTOPOLL 'X' // YES Xn[CR] Sets Auto Poll/Send ON/OFF for received frames.
#define LWUART_CMD_FILTER 'W' // - Wn[CR] Filter mode setting. By default CanSerial works in dual filter mode (0) and is backwards compatible with previous CanSerial versions.
#define LWUART_CMD_ACC_CODE 'M' // - Mxxxxxxxx[CR] Sets Acceptance Code Register (ACn Register of SJA1000). // we use MCP2515, not supported
#define LWUART_CMD_ACC_MASK 'm' // - mxxxxxxxx[CR] Sets Acceptance Mask Register (AMn Register of SJA1000). // we use MCP2515, not supported
#define LWUART_CMD_UART 'U' // YES Un[CR] Setup UART with a new baud rate where n is 0-6.
#define LWUART_CMD_VERSION1 'V' // YES v[CR] Get Version number of both CanSerial hardware and software
#define LWUART_CMD_VERSION2 'v' // YES V[CR] Get Version number of both CanSerial hardware and software
#define LWUART_CMD_SERIAL 'N' // YES N[CR] Get Serial number of the CanSerial.
#define LWUART_CMD_TIMESTAMP 'Z' // YES+ Zn[CR] Sets Time Stamp ON/OFF for received frames only. EXTENSION to LAWICEL: Z2 - millis() timestamp w/o standard 60000ms cycle
#define LWUART_CMD_AUTOSTART 'Q' // YES todo Qn[CR] Auto Startup feature (from power on).
#define LOW_BYTE(x) ((unsigned char)((x)&0xFF))
#define HIGH_BYTE(x) ((unsigned char)(((x)>>8)&0xFF))
#define LOW_WORD(x) ((unsigned short)((x)&0xFFFF))
#define HIGH_WORD(x) ((unsigned short)(((x)>>16)&0xFFFF))
#ifndef INT32U
#define INT32U unsigned long
#endif
#ifndef INT16U
#define INT16U word
#endif
#ifndef INT8U
#define INT8U byte
#endif
#define LWUART_OK 0x00
#define LWUART_OK_SMALL 0x01
#define LWUART_OK_BIG 0x02
#define LWUART_ERR 0x03
#define LWUART_ERR_NOT_IMPLEMENTED 0x04
#define LWUART_ERR_UNKNOWN_CMD 0x05
#define LWUART_FILTER_SKIP 0x00
#define LWUART_FILTER_PROCESS 0x01
//#define LWUART_IS_OK(x) ((x)==LWUART_OK ||(x)==LWUART_OK_NEW ? TRUE : FALSE)
#define LWUART_CR '\r'
#define LWUART_ALL 'A'
#define LWUART_FLAG 'F'
#define LWUART_TR11 't'
#define LWUART_TR29 'T'
#define LWUART_RET_ASCII_OK 0x0D
#define LWUART_RET_ASCII_ERROR 0x07
#define LWUART_RET_ASCII_OK_SMALL 'z'
#define LWUART_RET_ASCII_OK_BIG 'Z'
#define LWUART_STATUS_CAN_CLOSED 0x00
#define LWUART_STATUS_CAN_OPEN_NORMAL 0x01
#define LWUART_STATUS_CAN_OPEN_LISTEN 0x01
#define LWUART_FRAME_MAX_LENGTH 0x08
#define LWUART_FRAME_MAX_SIZE (sizeof("Tiiiiiiiildddddddddddddddd\r")+1)
#define LWUART_INPUT_STRING_BUFFER_SIZE 200
#define LWUART_OFF '0'
#define LWUART_ON_ONE '1'
#define LWUART_ON_TWO '2'
#define LWUART_AUTOPOLL_OFF 0x00
#define LWUART_AUTOPOLL_ON 0x01
#define LWUART_AUTOSTART_OFF 0x00
#define LWUART_AUTOSTART_ON_NORMAL 0x01
#define LWUART_AUTOSTART_ON_LISTEN 0x02
#define LWUART_TIMESTAMP_OFF 0x00
#define LWUART_TIMESTAMP_ON_NORMAL 0x01
#define LWUART_TIMESTAMP_ON_EXTENDED 0x02
#define LWUART_OFFSET_STD_PKT_LEN 0x04
#define LWUART_OFFSET_STD_PKT_DATA 0x05
#define LWUART_OFFSET_EXT_PKT_LEN 0x09
#define LWUART_OFFSET_EXT_PKT_DATA 0x0A
#define LWUART_DEFAULT_BAUD_RATE 115200
#define LWUART_DEFAULT_CAN_RATE CAN_500KBPS
#define LWUART_DEFAULT_CLOCK_FREQ MCP_16MHz
#define LWUART_CAN_BAUD_NUM 0x0a
#define LWUART_UART_BAUD_NUM 0x07
const INT32U LWUARTSerialBaudRates[] //PROGMEM
= { 230400, 115200, 57600, 38400, 19200, 9600, 2400 };
const INT32U LWUARTCanBaudRates[] //PROGMEM
= { CAN_10KBPS, CAN_20KBPS, CAN_50KBPS, CAN_100KBPS, CAN_125KBPS, CAN_250KBPS, CAN_500KBPS, CAN_500KBPS /*CAN_800KBPS*/, CAN_1000KBPS, CAN_83K3BPS };
class CanSerial
{
public:
static void init(INT8U defaultCanSpeed = LWUART_DEFAULT_CAN_RATE, const INT8U clock = LWUART_DEFAULT_CLOCK_FREQ);
static void setFilter(INT8U (*userFunc)(INT32U));
static void loop();
static void serialEvent();
private:
static CanSerial* _instance;
static CanSerial* instance();
void initFunc();
void setFilterFunc(INT8U (*userFunc)(INT32U));
void loopFunc();
void serialEventFunc();
INT8U (*userAddressFilterFunc)(INT32U addr) = 0;
MCP_CAN LWUARTCAN = MCP_CAN(LWUART_CAN_BUS_SHIELD_CS_PIN);
INT8U LWUARTCanSpeedSelection = CAN_83K3BPS;
INT8U LWUARTMcpModuleClock = MCP_16MHz;
INT8U LWUARTCanChannelMode = LWUART_STATUS_CAN_CLOSED;
INT8U LWUARTLastErr = LWUART_OK;
INT8U LWUARTAutoStart = LWUART_AUTOSTART_OFF;
INT8U LWUARTAutoPoll = LWUART_AUTOPOLL_OFF;
INT8U LWUARTTimeStamp = LWUART_TIMESTAMP_OFF;
INT32U LWUARTCanId = 0;
INT8U LWUARTBuffer[8];
INT8U LWUARTPacketLen = 0;
INT8U LWUARTMessage[LWUART_FRAME_MAX_SIZE];
String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
INT8U parseAndRunCommand();
INT8U exec();
INT8U checkReceive();
INT8U readMsgBufID(INT32U *ID, INT8U *len, INT8U buf[]);
INT8U receiveSingleFrame();
INT8U isExtendedFrame();
INT8U checkPassFilter(INT32U addr);
INT8U openCanBus();
INT8U sendMsgBuf(INT32U id, INT8U ext, INT8U rtr, INT8U len, INT8U *buf);
void parseCanStdId();
void parseCanExtId();
};
class HexHelper {
public:
static void printFullByte(INT8U b);
static void printNibble(INT8U b);
static INT8U parseNibble(INT8U hex);
static INT8U parseFullByte(INT8U H, INT8U L);
static INT8U parseNibbleWithLimit(INT8U hex, INT8U limit);
};
class CanSerialFake : CanSerial {
};
#endif
+62
View File
@@ -0,0 +1,62 @@
# arduino-canbus-monitor [![Build Status](https://api.travis-ci.org/latonita/arduino-canbus-monitor.svg?branch=master)](https://travis-ci.org/latonita/arduino-canbus-monitor) [![Coverity Scan](https://scan.coverity.com/projects/11684/badge.svg)](https://scan.coverity.com/projects/latonita-arduino-canbus-monitor) [![Analytics](https://ga-beacon.appspot.com/UA-99380399-1/welcome-page)](https://github.com/igrigorik/ga-beacon)
CAN BUS monitoring software based on Arduino with Seeduino/ElecFreaks CAN BUS shield based on MCP2515 (Numerous other MCP2515 based CAN BUS modules from ebay and aliexpress work well to).
This software implements CAN ASCII / Serial CAN / SLCAN protocol compatible with Lawicel CAN232/CANUSB.
As for PC counterpart software I personally used and can recommend two tools:
1) [Windows] CANHacker tool v.2.00.01 (by fuchs) to sniff and visualize data on the bus. You can download CANHacker tool from this forum page: http://www.canhack.net/viewforum.php?f=25&sid=ac01d465f19e088cb160cab630561607
2) [Windows] CAN-COOL (by MHS Elektronik), open source, but unfortunaly available only in German. Download link: http://www.mhs-elektronik.de/index.php?module=content&action=show&page=can_cool (Make sure you select RS232 and SL-CAN protocol and then click hardware bus reset icon on a toolbar)
3) [Linux] Please dig into direction of SLCAN/SocketCAN, but start from https://github.com/linux-can/can-utils
This monitor uses CAN BUS library forked from https://github.com/Seeed-Studio/CAN_BUS_Shield.
Copyright (C) 2015,2016 Anton Viktorov <latonita@yandex.ru>
You can buy me a beer if you like the tool :o) [![Donate](https://www.paypal.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4JPDVHYWUY3LW)
See protocol definition here http://www.can232.com/docs/can232_v3.pdf and here http://www.can232.com/docs/canusb_manual.pdf
Commands not supported/not implemented:
- s, W, M, m, U.
Commands modified:
- S - supports not declared 83.3 rate
- F - returns MCP2515 error flags
- Z - extra Z2 option enables 4 byte timestamp vs standard 2 byte (60000ms max)
```
CMD | IMPLEMENTED | SYNTAX | DESCRIPTION
------------------------------------------------------------------------------------------------------------
'S' | YES+ | Sn[CR] Setup with standard CAN bit-rates where n is 0-8.
| | S0 10Kbit S4 125Kbit S8 1Mbit
| | S1 20Kbit S5 250Kbit S9 83.3Kbit
| | S2 50Kbit S6 500Kbit
| | S3 100Kbit S7 800Kbit
's' | - | sxxyy[CR] Setup with BTR0/BTR1 CAN bit-rates where xx and yy is a hex value.
'O' | YES | O[CR] Open the CAN channel in normal mode (sending & receiving).
'L' | YES | L[CR] Open the CAN channel in listen only mode (receiving).
'C' | YES | C[CR] Close the CAN channel.
't' | YES | tiiildd...[CR] Transmit a standard (11bit) CAN frame.
'T' | YES | Tiiiiiiiildd...[CR] Transmit an extended (29bit) CAN frame
'r' | YES | riiil[CR] Transmit an standard RTR (11bit) CAN frame.
'R' | YES | Riiiiiiiil[CR] Transmit an extended RTR (29bit) CAN frame.
'P' | YES | P[CR] Poll incomming FIFO for CAN frames (single poll)
'A' | YES | A[CR] Polls incomming FIFO for CAN frames (all pending frames)
'F' | YES+ | F[CR] Read Status Flags.
'X' | YES | Xn[CR] Sets Auto Poll/Send ON/OFF for received frames.
'W' | - | Wn[CR] Filter mode setting. By default CAN232 works in dual filter mode (0) and is backwards compatible with previous CAN232 versions.
'M' | - | Mxxxxxxxx[CR] Sets Acceptance Code Register (ACn Register of SJA1000). // we use MCP2515, not supported
'm' | - | mxxxxxxxx[CR] Sets Acceptance Mask Register (AMn Register of SJA1000). // we use MCP2515, not supported
'U' | YES | Un[CR] Setup UART with a new baud rate where n is 0-6.
'V' | YES | v[CR] Get Version number of both CAN232 hardware and software
'v' | YES | V[CR] Get Version number of both CAN232 hardware and software
'N' | YES | N[CR] Get Serial number of the CAN232.
'Z' | YES+ | Zn[CR] Sets Time Stamp ON/OFF for received frames only. EXTENSION to LAWICEL: Z2 - millis() timestamp w/o standard 60000ms cycle
'Q' | YES todo | Qn[CR] Auto Startup feature (from power on).
```
@@ -0,0 +1,68 @@
/*****************************************************************************************
* This is implementation of CAN BUS ASCII protocol based on LAWICEL v1.3 serial protocol
* of CAN232/CANUSB device (http://www.can232.com/docs/can232_v3.pdf)
*
* Made for Arduino with Seeduino/ElecFreaks CAN BUS Shield based on MCP2515
*
* Copyright (C) 2015 Anton Viktorov <latonita@yandex.ru>
* https://github.com/latonita/arduino-canbus-monitor
*
* This library is free software. You may use/redistribute it under The MIT License terms.
*
*****************************************************************************************/
#include <SPI.h>
#include "mcp_can.h"
#include "can-serial.h"
void setup() {
Serial.begin(LWUART_DEFAULT_BAUD_RATE); // default COM baud rate is 115200.
// Can232::init (RATE, CLOCK)
// Rates: CAN_10KBPS, CAN_20KBPS, CAN_50KBPS, CAN_100KBPS, CAN_125KBPS, CAN_250KBPS, CAN_500KBPS, CAN_500KBPS, CAN_1000KBPS, CAN_83K3BPS
// Default is CAN_83K3BPS ;)))))))))
// Clock: MCP_16MHz or MCP_8MHz.
// Default is MCP_16MHz. Please note, not all CAN speeds supported. check big switch in mcp_can.cpp
// defaults can be changed in mcp_can.h
// Can232::init(); // rate and clock = LW232_DEFAULT_CAN_RATE and LW232_DEFAULT_CLOCK_FREQ
// Can232::init(CAN_125KBPS); // rate = 125, clock = LW232_DEFAULT_CLOCK_FREQ
CanSerial::init(CAN_125KBPS, MCP_16MHz); // set default rate you need here and clock frequency of CAN shield. Typically it is 16MHz, but on some MCP2515 + TJA1050 it is 8Mhz
// optional custom packet filter to reduce number of messages comingh through to canhacker
// Can232::setFilter(myCustomAddressFilter);
}
INT8U myCustomAddressFilter(INT32U addr) {
INT8U ret = LWUART_FILTER_SKIP; //LW232_FILTER_PROCESS or LW232_FILTER_SKIP
switch(addr) {
// case 0x01b: //VIN
// case 0x1C8: //lights
// case 0x2C0: // pedals
case 0x3d0: // sound vol, treb..
ret = LWUART_FILTER_PROCESS;
// case 0x000: // ?
// case 0x003: //shifter
// case 0x015: // dor open close affects this as well
// case 0x02c: // ???
// ret = 0;
// break;
// case 0x002:
// case 0x1a7: //fuel cons or some other
// ret = 1;
// break;
// default:
// ret = 0;
}
return ret;
}
void loop() {
CanSerial::loop();
}
void serialEvent() {
CanSerial::serialEvent();
}