diff --git a/can-232.cpp b/can-232.cpp new file mode 100644 index 0000000..f9557fd --- /dev/null +++ b/can-232.cpp @@ -0,0 +1,542 @@ +/***************************************************************************************** +* 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 +* https://github.com/latonita/can-ascii +* +* This library is free software. You may use/redistribute it under The MIT License terms. +* +*****************************************************************************************/ + +#include +#include +#include "can-232.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 + +//#define LOGGING_ENABLED +//#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); +//#endif + +Can232* Can232::_instance = 0; + +Can232* const & Can232::instance() { + if (_instance == 0) + _instance = new Can232(); + return _instance; +} + +void Can232::init() { + dbg_begin(LW232_DEFAULT_BAUD_RATE); // logging through software serial + dbg1("CAN ASCII. Welcome to debug"); + + instance()->initFunc(); +} + +void Can232::setFilter(INT8U (*userFunc)(INT32U)) { + instance()->setFilterFunc(userFunc); +} + +void Can232::loop() { + instance()->loopFunc(); +} + +void Can232::serialEvent() { + instance()->serialEventFunc(); +} + +void Can232::initFunc() { + inputString.reserve(200); + // lw232AutoStart = true; //todo: read from eeprom + // lw232AutoPoll = false; //todo: read from eeprom + // lw232TimeStamp = //read from eeprom + // lw232Message[0] = 'Z'; lw232Message[1] = '1'; exec(); + // if (lw232AutoStart) { + lw232Message[0] = 'O'; exec(); + // } +} + +void Can232::setFilterFunc(INT8U (*userFunc)(INT32U)) { + instance()->userAddressFilterFunc = userFunc; +} + +void Can232::loopFunc() { + if (stringComplete) { + int len = inputString.length(); + if (len > 1 && len < 29) { + strcpy((char*)lw232Message, inputString.c_str()); + exec(); + } + // clear the string: + inputString = ""; + stringComplete = false; + } + if (lw232CanChannelMode != LW232_STATUS_CAN_CLOSED) { + int recv = 0; + while (CAN_MSGAVAIL == checkReceive() && recv++<5) { + dbg0('+'); + if (CAN_OK == receiveSingleFrame()) { + Serial.write(LW232_CR); + } + } + Serial.flush(); + } +} +void Can232::serialEventFunc() { + while (Serial.available()) { + char inChar = (char)Serial.read(); + inputString += inChar; + if (inChar == LW232_CR) { + stringComplete = true; + } + } +} + +INT8U Can232::exec() { + dbg2("Command received:", inputString); + lw232LastErr = parseAndRunCommand(); + + switch (lw232LastErr) { + case LW232_OK: + Serial.write(LW232_RET_ASCII_OK); + break; + case LW232_OK_SMALL: + Serial.write(LW232_RET_ASCII_OK_SMALL); + Serial.write(LW232_RET_ASCII_OK); + break; + case LW232_OK_BIG: + Serial.write(LW232_RET_ASCII_OK_BIG); + Serial.write(LW232_RET_ASCII_OK); + break; + case LW232_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(LW232_RET_ASCII_ERROR); + //Serial.write(LW232_RET_ASCII_OK); + break; + default: + Serial.write(LW232_RET_ASCII_ERROR); + } + return 0; +} + + +INT8U Can232::parseAndRunCommand() { + INT8U ret = LW232_OK; + INT8U idx = 0; + INT8U err = 0; + + lw232LastErr = LW232_OK; + + switch (lw232Message[0]) { + case LW232_CMD_SETUP: + // Sn[CR] Setup with standard CAN bit-rates where n is 0-9. + if (lw232CanChannelMode == LW232_STATUS_CAN_CLOSED) { + idx = HexHelper::parseNibbleWithLimit(lw232Message[1], LW232_CAN_BAUD_NUM); + lw232CanSpeedSelection = lw232CanBaudRates[idx]; + } + else { + ret = LW232_ERR; + } + break; + case LW232_CMD_SETUP_BTR: + // sxxyy[CR] Setup with BTR0/BTR1 CAN bit-rates where xx and yy is a hex value. + ret = LW232_ERR; break; + case LW232_CMD_OPEN: + // O[CR] Open the CAN channel in normal mode (sending & receiving). + if (lw232CanChannelMode == LW232_STATUS_CAN_CLOSED) { + lw232CanChannelMode = LW232_STATUS_CAN_OPEN_NORMAL; + ret = openCanBus(); + } + else { + ret = LW232_ERR; + } + break; + case LW232_CMD_LISTEN: + // L[CR] Open the CAN channel in listen only mode (receiving). + if (lw232CanChannelMode == LW232_STATUS_CAN_CLOSED) { + lw232CanChannelMode = LW232_STATUS_CAN_OPEN_LISTEN; + ret = openCanBus(); + } + else { + ret = LW232_ERR; + } + break; + case LW232_CMD_CLOSE: + // C[CR] Close the CAN channel. + if (lw232CanChannelMode != LW232_STATUS_CAN_CLOSED) { + lw232CanChannelMode = LW232_STATUS_CAN_CLOSED; + } + else { + ret = LW232_ERR; + } + break; + case LW232_CMD_TX11: + // tiiildd...[CR] Transmit a standard (11bit) CAN frame. + if (lw232CanChannelMode == LW232_STATUS_CAN_OPEN_NORMAL) { + parseCanStdId(); + lw232PacketLen = HexHelper::parseNibbleWithLimit(lw232Message[LW232_OFFSET_STD_PKT_LEN], LW232_FRAME_MAX_LENGTH); + for (; idx < lw232PacketLen; idx++) { + lw232Buffer[idx] = HexHelper::parseFullByte(lw232Message[LW232_OFFSET_STD_PKT_DATA + idx * 2 + 1], lw232Message[LW232_OFFSET_STD_PKT_DATA + idx * 2 + 1]); + } + if (CAN_OK != sendMsgBuf(lw232CanId, 0, 0, lw232PacketLen, lw232Buffer)) { + ret = LW232_ERR; + } + else if (lw232AutoPoll) { + ret = LW232_OK_SMALL; + } + } + else { + ret = LW232_ERR; + } + break; + case LW232_CMD_TX29: + // Tiiiiiiiildd...[CR] Transmit an extended (29bit) CAN frame + if (lw232CanChannelMode == LW232_STATUS_CAN_OPEN_NORMAL) { + parseCanExtId(); + lw232PacketLen = HexHelper::parseNibbleWithLimit(lw232Message[LW232_OFFSET_EXT_PKT_LEN], LW232_FRAME_MAX_LENGTH); + for (; idx < lw232PacketLen; idx++) { + lw232Buffer[idx] = HexHelper::parseFullByte(lw232Message[LW232_OFFSET_EXT_PKT_DATA + idx * 2 + 1], lw232Message[LW232_OFFSET_EXT_PKT_DATA + idx * 2 + 1]); + } + if (CAN_OK != sendMsgBuf(lw232CanId, 1, 0, lw232PacketLen, lw232Buffer)) { + ret = LW232_ERR; + } else if (lw232AutoPoll) { + ret = LW232_OK_BIG; + } + } + break; + case LW232_CMD_RTR11: + // riiil[CR] Transmit an standard RTR (11bit) CAN frame. + if (lw232CanChannelMode == LW232_STATUS_CAN_OPEN_NORMAL) { + parseCanStdId(); + lw232PacketLen = HexHelper::parseNibbleWithLimit(lw232Message[LW232_OFFSET_STD_PKT_LEN], LW232_FRAME_MAX_LENGTH); + if (CAN_OK != sendMsgBuf(lw232CanId, 0, 1, lw232PacketLen, lw232Buffer)) { + ret = LW232_ERR; + } + else if (lw232AutoPoll) { + ret = LW232_OK_SMALL; + } + } + else { + ret = LW232_ERR; + } + break; + case LW232_CMD_RTR29: + // Riiiiiiiil[CR] Transmit an extended RTR (29bit) CAN frame. + if (lw232CanChannelMode == LW232_STATUS_CAN_OPEN_NORMAL) { + parseCanExtId(); + lw232PacketLen = HexHelper::parseNibbleWithLimit(lw232Message[LW232_OFFSET_EXT_PKT_LEN], LW232_FRAME_MAX_LENGTH); + if (CAN_OK != sendMsgBuf(lw232CanId, 1, 1, lw232PacketLen, lw232Buffer)) { + ret = LW232_ERR; + } + else if (lw232AutoPoll) { + ret = LW232_OK_SMALL; // not a typo. strangely can232_v3.pdf tells to return "z[CR]", not "Z[CR]" as in 29bit. todo: check if it is error in pdf??? + } + } else { + ret = LW232_ERR; + } + break; + case LW232_CMD_POLL_ONE: + // P[CR] Poll incomming FIFO for CAN frames (single poll) + if (lw232CanChannelMode != LW232_STATUS_CAN_CLOSED && lw232AutoPoll == LW232_AUTOPOLL_OFF) { + if (CAN_MSGAVAIL == checkReceive()) { + ret = receiveSingleFrame(); + } + } else { + ret = LW232_ERR; + } + break; + case LW232_CMD_POLL_MANY: + // A[CR] Polls incomming FIFO for CAN frames (all pending frames) + if (lw232CanChannelMode != LW232_STATUS_CAN_CLOSED && lw232AutoPoll == LW232_AUTOPOLL_OFF) { + while (CAN_MSGAVAIL == checkReceive()) { + ret = ret ^ receiveSingleFrame(); + if (ret != CAN_OK) + break; + Serial.write(LW232_CR); + } + if (ret == CAN_OK) + Serial.print(LW232_ALL); + } else { + ret = LW232_ERR; + } + break; + case LW232_CMD_FLAGS: + // F[CR] Read Status Flags. + // LAWICEL CAN232 and CANUSB have some specific errors which differ from MCP2515/MCP2551 errors. We just return MCP2515 error. + Serial.print(LW232_FLAG); + if (lw232CAN.checkError(&err) == CAN_OK) + err = 0; + HexHelper::printFullByte(err & MCP_EFLG_ERRORMASK); + break; + case LW232_CMD_AUTOPOLL: + // Xn[CR] Sets Auto Poll/Send ON/OFF for received frames. + if (lw232CanChannelMode == LW232_STATUS_CAN_CLOSED) { + lw232AutoPoll = (lw232Message[1] == LW232_ON_ONE) ? LW232_AUTOPOLL_ON : LW232_AUTOPOLL_OFF; + //todo: save to eeprom + } else { + ret = LW232_ERR; + } + break; + case LW232_CMD_FILTER: + // Wn[CR] Filter mode setting. By default CAN232 works in dual filter mode (0) and is backwards compatible with previous CAN232 versions. + ret = LW232_ERR_NOT_IMPLEMENTED; break; + case LW232_CMD_ACC_CODE: + // Mxxxxxxxx[CR] Sets Acceptance Code Register (ACn Register of SJA1000). // we use MCP2515, + ret = LW232_ERR_NOT_IMPLEMENTED; break; + case LW232_CMD_ACC_MASK: + // mxxxxxxxx[CR] Sets Acceptance Mask Register (AMn Register of SJA1000). + ret = LW232_ERR_NOT_IMPLEMENTED; break; + case LW232_CMD_UART: + // Un[CR] Setup UART with a new baud rate where n is 0-6. + idx = HexHelper::parseNibbleWithLimit(lw232Message[1], LW232_UART_BAUD_NUM); + Serial.begin(lw232SerialBaudRates[idx]); + break; + case LW232_CMD_VERSION1: + case LW232_CMD_VERSION2: + // V[CR] Get Version number of both CAN232 hardware and software + Serial.print(LW232_LAWICEL_VERSION_STR); + break; + case LW232_CMD_SERIAL: + // N[CR] Get Serial number of the CAN232. + Serial.print(LW232_LAWICEL_SERIAL_NUM); + break; + case LW232_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 (lw232CanChannelMode == LW232_STATUS_CAN_CLOSED) { + // lw232TimeStamp = (lw232Message[1] == LW232_ON_ONE); + if (lw232Message[1] == LW232_ON_ONE) { + lw232TimeStamp = LW232_TIMESTAMP_ON_NORMAL; + } + else if (lw232Message[1] == LW232_ON_TWO) { + lw232TimeStamp = LW232_TIMESTAMP_ON_EXTENDED; + } + else { + lw232TimeStamp = LW232_TIMESTAMP_OFF; + } + } + else { + ret = LW232_ERR; + } + break; + case LW232_CMD_AUTOSTART: + // Qn[CR] Auto Startup feature (from power on). + if (lw232CanChannelMode != LW232_STATUS_CAN_CLOSED) { + if (lw232Message[1] == LW232_ON_ONE) { + lw232AutoStart = LW232_AUTOSTART_ON_NORMAL; + } + else if (lw232Message[1] == LW232_ON_TWO) { + lw232AutoStart = LW232_AUTOSTART_ON_LISTEN; + } + else { + lw232AutoStart = LW232_AUTOSTART_OFF; + } + //todo: save to eeprom + } + else { + ret = LW232_ERR; + } + break; + default: + ret = LW232_ERR_UNKNOWN_CMD; + } + + return ret; +} + +INT8U Can232::checkReceive() { +#ifndef _MCP_FAKE_MODE_ + return lw232CAN.checkReceive(); +#else + return CAN_MSGAVAIL; +#endif +} + +INT8U Can232::readMsgBufID(INT32U *ID, INT8U *len, INT8U buf[]) { +#ifndef _MCP_FAKE_MODE_ + return lw232CAN.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 Can232::receiveSingleFrame() { + INT8U ret = LW232_OK; + INT8U idx = 0; + if (CAN_OK == readMsgBufID(&lw232CanId, &lw232PacketLen, lw232Buffer)) { + if (lw232CanId > 0x1FFFFFFF) { + ret = LW232_ERR; // address if totally wrong + } + else if (checkPassFilter(lw232CanId)) {// do we want to skip some addresses? + if (isExtendedFrame()) { + Serial.print(LW232_TR29); + HexHelper::printFullByte(HIGH_BYTE(HIGH_WORD(lw232CanId))); + HexHelper::printFullByte(LOW_BYTE(HIGH_WORD(lw232CanId))); + HexHelper::printFullByte(HIGH_BYTE(LOW_WORD(lw232CanId))); + HexHelper::printFullByte(LOW_BYTE(LOW_WORD(lw232CanId))); + } + else { + Serial.print(LW232_TR11); + HexHelper::printNibble(HIGH_BYTE(LOW_WORD(lw232CanId))); + HexHelper::printFullByte(LOW_BYTE(LOW_WORD(lw232CanId))); + } + //write data len + HexHelper::printNibble(lw232PacketLen); + //write data + for (idx = 0; idx < lw232PacketLen; idx++) { + HexHelper::printFullByte(lw232Buffer[idx]); + } + //write timestamp if needed + if (lw232TimeStamp != LW232_TIMESTAMP_OFF) { + INT32U time = millis(); + if (lw232TimeStamp == LW232_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 = LW232_ERR; + } + return ret; +} + + +INT8U Can232::isExtendedFrame() { +#ifndef _MCP_FAKE_MODE_ + return lw232CAN.isExtendedFrame(); +#else + return lw232CanId > 0x7FF ? 1 : 0; //simple hack for fake mode +#endif +} + + +INT8U Can232::checkPassFilter(INT32U addr) { + if (userAddressFilterFunc == 0) + return LW232_FILTER_PROCESS; + + return (*userAddressFilterFunc)(addr); +} + +INT8U Can232::openCanBus() { + INT8U ret = LW232_OK; +#ifndef _MCP_FAKE_MODE_ + if (CAN_OK != lw232CAN.begin(lw232CanSpeedSelection)) + ret = LW232_ERR; +#endif + return ret; +} + + +INT8U Can232::sendMsgBuf(INT32U id, INT8U ext, INT8U rtr, INT8U len, INT8U *buf) { +#ifndef _MCP_FAKE_MODE_ + return lw232CAN.sendMsgBuf(id, ext, rtr, len, buf); +#else + Serial.print("= '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; +} diff --git a/can-232.h b/can-232.h new file mode 100644 index 0000000..8752d56 --- /dev/null +++ b/can-232.h @@ -0,0 +1,221 @@ +/***************************************************************************************** +* 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 +* https://github.com/latonita/can-ascii +* +* This library is free software. You may use/redistribute it under The MIT License terms. +* +*****************************************************************************************/ + + +#ifndef _CAN_232_H_ +#define _CAN_232_H_ + +//#if defined(ARDUINO) && ARDUINO >= 100 +// #include "arduino.h" +//#else +// #include "WProgram.h" +//#endif + +#include "mcp_can.h" +#include "SoftwareSerial.h" + +#define LW232_LAWICEL_VERSION_STR "V1013" +#define LW232_LAWICEL_SERIAL_NUM "NA123" +#define LW232_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 LW232_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 LW232_CMD_SETUP_BTR 's' // - sxxyy[CR] Setup with BTR0/BTR1 CAN bit-rates where xx and yy is a hex value. +#define LW232_CMD_OPEN 'O' // YES O[CR] Open the CAN channel in normal mode (sending & receiving). +#define LW232_CMD_LISTEN 'L' // YES L[CR] Open the CAN channel in listen only mode (receiving). +#define LW232_CMD_CLOSE 'C' // YES C[CR] Close the CAN channel. +#define LW232_CMD_TX11 't' // YES tiiildd...[CR] Transmit a standard (11bit) CAN frame. +#define LW232_CMD_TX29 'T' // YES Tiiiiiiiildd...[CR] Transmit an extended (29bit) CAN frame +#define LW232_CMD_RTR11 'r' // YES riiil[CR] Transmit an standard RTR (11bit) CAN frame. +#define LW232_CMD_RTR29 'R' // YES Riiiiiiiil[CR] Transmit an extended RTR (29bit) CAN frame. +#define LW232_CMD_POLL_ONE 'P' // YES P[CR] Poll incomming FIFO for CAN frames (single poll) +#define LW232_CMD_POLL_MANY 'A' // YES A[CR] Polls incomming FIFO for CAN frames (all pending frames) +#define LW232_CMD_FLAGS 'F' // YES+ F[CR] Read Status Flags. +#define LW232_CMD_AUTOPOLL 'X' // YES Xn[CR] Sets Auto Poll/Send ON/OFF for received frames. +#define LW232_CMD_FILTER 'W' // - Wn[CR] Filter mode setting. By default CAN232 works in dual filter mode (0) and is backwards compatible with previous CAN232 versions. +#define LW232_CMD_ACC_CODE 'M' // - Mxxxxxxxx[CR] Sets Acceptance Code Register (ACn Register of SJA1000). // we use MCP2515, not supported +#define LW232_CMD_ACC_MASK 'm' // - mxxxxxxxx[CR] Sets Acceptance Mask Register (AMn Register of SJA1000). // we use MCP2515, not supported +#define LW232_CMD_UART 'U' // YES Un[CR] Setup UART with a new baud rate where n is 0-6. +#define LW232_CMD_VERSION1 'V' // YES v[CR] Get Version number of both CAN232 hardware and software +#define LW232_CMD_VERSION2 'v' // YES V[CR] Get Version number of both CAN232 hardware and software +#define LW232_CMD_SERIAL 'N' // YES N[CR] Get Serial number of the CAN232. +#define LW232_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 LW232_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 LW232_OK 0x00 +#define LW232_OK_SMALL 0x01 +#define LW232_OK_BIG 0x02 +#define LW232_ERR 0x03 +#define LW232_ERR_NOT_IMPLEMENTED 0x04 +#define LW232_ERR_UNKNOWN_CMD 0x05 + +#define LW232_FILTER_SKIP 0x00 +#define LW232_FILTER_PROCESS 0x01 + +//#define LW232_IS_OK(x) ((x)==LW232_OK ||(x)==LW232_OK_NEW ? TRUE : FALSE) + +#define LW232_CR '\r' +#define LW232_ALL 'A' +#define LW232_FLAG 'F' +#define LW232_TR11 't' +#define LW232_TR29 'T' + +#define LW232_RET_ASCII_OK 0x0D +#define LW232_RET_ASCII_ERROR 0x07 +#define LW232_RET_ASCII_OK_SMALL 'z' +#define LW232_RET_ASCII_OK_BIG 'Z' + +#define LW232_STATUS_CAN_CLOSED 0x00 +#define LW232_STATUS_CAN_OPEN_NORMAL 0x01 +#define LW232_STATUS_CAN_OPEN_LISTEN 0x01 + +#define LW232_FRAME_MAX_LENGTH 0x08 +#define LW232_FRAME_MAX_SIZE (sizeof("Tiiiiiiiildddddddddddddddd\r")+1) + +#define LW232_OFF '0' +#define LW232_ON_ONE '1' +#define LW232_ON_TWO '2' + +#define LW232_AUTOPOLL_OFF 0x00 +#define LW232_AUTOPOLL_ON 0x01 + +#define LW232_AUTOSTART_OFF 0x00 +#define LW232_AUTOSTART_ON_NORMAL 0x01 +#define LW232_AUTOSTART_ON_LISTEN 0x02 + +#define LW232_TIMESTAMP_OFF 0x00 +#define LW232_TIMESTAMP_ON_NORMAL 0x01 +#define LW232_TIMESTAMP_ON_EXTENDED 0x02 + +#define LW232_OFFSET_STD_PKT_LEN 0x04 +#define LW232_OFFSET_STD_PKT_DATA 0x05 +#define LW232_OFFSET_EXT_PKT_LEN 0x09 +#define LW232_OFFSET_EXT_PKT_DATA 0x0A + + +#define LW232_DEFAULT_BAUD_RATE 115200 +#define LW232_CAN_BAUD_NUM 0x0a +#define LW232_UART_BAUD_NUM 0x07 + +const INT32U lw232SerialBaudRates[] //PROGMEM += { 230400, 115200, 57600, 38400, 19200, 9600, 2400 }; + +const INT32U lw232CanBaudRates[] //PROGMEM += { CAN_10KBPS, CAN_20KBPS, CAN_50KBPS, CAN_100KBPS, CAN_125KBPS, CAN_250KBPS, CAN_500KBPS, CAN_500KBPS /*CAN_800KBPS*/, CAN_1000KBPS, CAN_83K3BPS }; + +class Can232 +{ +public: + static void init(); + static void setFilter(INT8U (*userFunc)(INT32U)); + static void loop(); + static void serialEvent(); + +private: + static Can232* _instance; + static Can232* const& instance(); + + void initFunc(); + void setFilterFunc(INT8U (*userFunc)(INT32U)); + void loopFunc(); + void serialEventFunc(); + + INT8U (*userAddressFilterFunc)(INT32U addr) = 0; + + MCP_CAN lw232CAN = MCP_CAN(LW232_CAN_BUS_SHIELD_CS_PIN); + + INT8U lw232CanSpeedSelection = CAN_83K3BPS; + INT8U lw232CanChannelMode = LW232_STATUS_CAN_CLOSED; + INT8U lw232LastErr = LW232_OK; + + INT8U lw232AutoStart = LW232_AUTOSTART_OFF; + INT8U lw232AutoPoll = LW232_AUTOPOLL_OFF; + INT8U lw232TimeStamp = LW232_TIMESTAMP_OFF; + + INT32U lw232CanId = 0; + + INT8U lw232Buffer[8]; + INT8U lw232PacketLen = 0; + + INT8U lw232Message[LW232_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 Can232Fake : Can232 { + +}; + + +#endif diff --git a/can-ascii.ino b/can-ascii.ino new file mode 100644 index 0000000..fe6b835 --- /dev/null +++ b/can-ascii.ino @@ -0,0 +1,58 @@ +/***************************************************************************************** +* 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 +* https://github.com/latonita/can-ascii +* +* This library is free software. You may use/redistribute it under The MIT License terms. +* +*****************************************************************************************/ + +#include +#include +#include "can-232.h" +#include "SoftwareSerial.h" + + +INT8U myCustomAddressFilter(INT32U addr) { + INT8U ret = LW232_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 = LW232_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 setup() { + Serial.begin(LW232_DEFAULT_BAUD_RATE); + + Can232::init(); + //Can232::setFilter(myCustomAddressFilter); // optional +} +void loop() { + Can232::loop(); +} + +void serialEvent() { + Can232::serialEvent(); +} +