diff --git a/.ci/doxygen.groovy b/.ci/doxygen.groovy index e325cdb7e..b74d5e793 100644 --- a/.ci/doxygen.groovy +++ b/.ci/doxygen.groovy @@ -6,7 +6,7 @@ def call(config) { Documentation/doxygen.sh""" warnings canComputeNew: false, canResolveRelativePaths: false, defaultEncoding: '', - excludePattern: '''.*/hal/architecture/Linux/drivers/.*,.*/hal/transport/PJON/driver/.*,.*/hal/architecture/AVR/drivers/.*,.*/drivers/TinyGSM/.*''', + excludePattern: '''.*/hal/architecture/Linux/drivers/.*,.*/hal/transport/PJON/driver/.*,.*/hal/transport/CAN/driver/.*,.*/hal/architecture/AVR/drivers/.*,.*/drivers/TinyGSM/.*''', failedTotalAll: '', healthy: '', includePattern: '', messagesPattern: '', parserConfigurations: [[parserName: 'Doxygen', pattern: config.repository_root+'doxygen.log']], unHealthy: '', unstableTotalAll: '0' diff --git a/.mystools/cppcheck/config/suppressions.cfg b/.mystools/cppcheck/config/suppressions.cfg index 30a42f60d..d40d5e8b4 100644 --- a/.mystools/cppcheck/config/suppressions.cfg +++ b/.mystools/cppcheck/config/suppressions.cfg @@ -4,4 +4,5 @@ // 3rd party *:hal/architecture/Linux/* *:drivers/* -*:hal/transport/PJON/driver/* \ No newline at end of file +*:hal/transport/PJON/driver/* +*:hal/transport/CAN/driver/* \ No newline at end of file diff --git a/MyConfig.h b/MyConfig.h index c26ffdde2..8d8a60fd2 100755 --- a/MyConfig.h +++ b/MyConfig.h @@ -267,6 +267,52 @@ */ //#define MY_RS485 +/** + * @def MY_CAN + * @brief Define this to use the CAN wired transport for sensor network communication. + */ +//#define MY_CAN +/** + * @def MY_DEBUG_VERBOSE_CAN + * @brief Define this for verbose debug prints related to the %CAN driver. + */ +//#define MY_DEBUG_VERBOSE_CAN +/** + * @def MY_CAN_INT + * @brief Message arrived interrupt pin. + */ +#ifndef MY_CAN_INT +#define MY_CAN_INT (2u) +#endif +/** + * @def MY_CAN_CS + * @brief Chip select pin. + */ +#ifndef MY_CAN_CS +#define MY_CAN_CS (10u) +#endif +/** + * @def MY_CAN_SPEED + * @brief Baud rate. Allowed values can be found in mcp_can_dfs.h + */ +#ifndef MY_CAN_SPEED +#define MY_CAN_SPEED CAN_250KBPS +#endif +/** + * @def MY_CAN_CLOCK + * @brief can clock. Allowed values can be found in mcp_can_dfs.h + */ +#ifndef MY_CAN_CLOCK +#define MY_CAN_CLOCK MCP_8MHZ +#endif +/** + * @def MY_CAN_BUF_SIZE + * @brief assemble buffer size. Since long messages can be sliced and arrive mixed with other messages, assemble buffer is required. + */ +#ifndef MY_CAN_BUF_SIZE +#define MY_CAN_BUF_SIZE (8u) +#endif + /** * @def MY_RS485_BAUD_RATE * @brief The RS485 BAUD rate. @@ -2555,7 +2601,7 @@ #endif // Enable sensor network "feature" if one of the transport types was enabled -#if defined(MY_RADIO_RF24) || defined(MY_RADIO_NRF5_ESB) || defined(MY_RADIO_RFM69) || defined(MY_RADIO_RFM95) || defined(MY_RADIO_SX126x) || defined(MY_RS485) || defined(MY_PJON) +#if defined(MY_RADIO_RF24) || defined(MY_RADIO_NRF5_ESB) || defined(MY_RADIO_RFM69) || defined(MY_RADIO_RFM95) || defined(MY_RADIO_SX126x) || defined(MY_RS485) || defined(MY_PJON) || defined(MY_CAN) #define MY_SENSOR_NETWORK #endif @@ -2732,6 +2778,9 @@ // PJON #define MY_PJON #define MY_DEBUG_VERBOSE_PJON +// CAN +#define MY_CAN +#define MY_DEBUG_VERBOSE_CAN // RF24 #define MY_RADIO_RF24 #define MY_RADIO_NRF24 //deprecated diff --git a/MySensors.h b/MySensors.h index ec01be347..8f8f7a637 100644 --- a/MySensors.h +++ b/MySensors.h @@ -296,12 +296,17 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #else #define _PJONCNT 0 //!< _PJONCNT #endif +#if defined(MY_CAN) +#define __CANCNT 1 //!< __CANCNT +#else +#define __CANCNT 0 //!< __CANCNT +#endif #if defined(MY_RADIO_SX126x) #define __SX126xCNT 1 //!< __SX126xCNT #else #define __SX126xCNT 0 //!< __SX126xCNT #endif -#if (__RF24CNT + __NRF5ESBCNT + __RFM69CNT + __RFM95CNT + __RS485CNT + _PJONCNT + __SX126xCNT > 1) +#if (__RF24CNT + __NRF5ESBCNT + __RFM69CNT + __RFM95CNT + __RS485CNT + _PJONCNT + __SX126xCNT + __CANCNT > 1) #error Only one forward link driver can be activated #endif #endif //DOXYGEN @@ -312,7 +317,7 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #endif // TRANSPORT INCLUDES -#if defined(MY_RADIO_RF24) || defined(MY_RADIO_NRF5_ESB) || defined(MY_RADIO_RFM69) || defined(MY_RADIO_RFM95) || defined(MY_RS485) || defined (MY_PJON) || defined(MY_RADIO_SX126x) +#if defined(MY_RADIO_RF24) || defined(MY_RADIO_NRF5_ESB) || defined(MY_RADIO_RFM69) || defined(MY_RADIO_RFM95) || defined(MY_RS485) || defined (MY_PJON) || defined(MY_RADIO_SX126x) || defined(MY_CAN) #include "hal/transport/MyTransportHAL.h" #include "core/MyTransport.h" @@ -396,6 +401,8 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #elif defined(MY_RADIO_RFM95) #include "hal/transport/RFM95/driver/RFM95.cpp" #include "hal/transport/RFM95/MyTransportRFM95.cpp" +#elif defined(MY_CAN) +#include "hal/transport/CAN/MyTransportCAN.cpp" #elif defined(MY_PJON) #include "hal/transport/PJON/driver/PJON.h" #include "hal/transport/PJON/driver/PJONSoftwareBitBang.h" diff --git a/configure b/configure index 2f30e98de..0b4bf4c4c 100755 --- a/configure +++ b/configure @@ -62,7 +62,7 @@ MySensors options: MQTT publish topic prefix. --my-mqtt-subscribe-topic-prefix= MQTT subscribe topic prefix. - --my-transport=[none|rf24|rfm69|rfm95|rs485] + --my-transport=[none|rf24|rfm69|rfm95|rs485|can] Set the transport to be used to communicate with other nodes. [rf24] --my-rf24-channel=<0-125> RF channel for the sensor net. [76] --my-rf24-pa-level=[RF24_PA_MAX|RF24_PA_HIGH|RF24_PA_LOW|RF24_PA_MIN] @@ -669,6 +669,8 @@ elif [[ ${transport_type} == "rfm95" ]]; then CPPFLAGS="-DMY_RADIO_RFM95 $CPPFLAGS" elif [[ ${transport_type} == "rs485" ]]; then CPPFLAGS="-DMY_RS485 $CPPFLAGS" +elif [[ ${transport_type} == "can" ]]; then + CPPFLAGS="-DMY_CAN $CPPFLAGS" else die "Invalid transport type ${transport_type}." 3 fi diff --git a/examples/CANSwitch/CANSwitch.ino b/examples/CANSwitch/CANSwitch.ino new file mode 100644 index 000000000..e99e5222b --- /dev/null +++ b/examples/CANSwitch/CANSwitch.ino @@ -0,0 +1,98 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2019 Sensnology AB + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + ******************************* + * + * DESCRIPTION + * + * Interrupt driven binary switch example with dual interrupts + * Author: Patrick 'Anticimex' Fallberg + * Connect one button or door/window reed switch between + * digital I/O pin 3 (BUTTON_PIN below) and GND and the other + * one in similar fashion on digital I/O pin 2. + * This example is designed to fit Arduino Nano/Pro Mini + * + */ + + +// Enable debug prints to serial monitor +#define MY_DEBUG +//#define MY_DEBUG_VERBOSE_CAN +//#define MY_DEBUG_VERBOSE_CAN_INTERNAL + +// Enable and select radio type attached +#define MY_CAN +//#define MY_RADIO_NRF5_ESB +//#define MY_RADIO_RFM69 +//#define MY_RADIO_RFM95 + +#include + +#define SKETCH_NAME "Binary Sensor" +#define SKETCH_MAJOR_VER "1" +#define SKETCH_MINOR_VER "0" + +#define SECONDARY_CHILD_ID 4 + +#define SECONDARY_BUTTON_PIN 3 // Arduino Digital I/O pin for button/reed switch + +#if (SECONDARY_BUTTON_PIN < 2 || SECONDARY_BUTTON_PIN > 3) +#error SECONDARY_BUTTON_PIN must be either 2 or 3 for interrupts to work +#endif + +// Change to V_LIGHT if you use S_LIGHT in presentation below +//MyMessage msg(PRIMARY_CHILD_ID, V_TRIPPED); +MyMessage msg2(SECONDARY_CHILD_ID, V_TRIPPED); + +void setup() +{ + // Setup the buttons + pinMode(SECONDARY_BUTTON_PIN, INPUT_PULLUP); +} + +void presentation() +{ + // Send the sketch version information to the gateway and Controller + sendSketchInfo(SKETCH_NAME, SKETCH_MAJOR_VER "." SKETCH_MINOR_VER); + + // Register binary input sensor to sensor_node (they will be created as child devices) + // You can use S_DOOR, S_MOTION or S_LIGHT here depending on your usage. + // If S_LIGHT is used, remember to update variable type you send in. See "msg" above. + present(SECONDARY_CHILD_ID, S_DOOR); +} + +// Loop will iterate on changes on the BUTTON_PINs +void loop() +{ + uint8_t value; + static uint8_t sentValue2=2; + + // Short delay to allow buttons to properly settle + sleep(5); + + value = digitalRead(SECONDARY_BUTTON_PIN); + + if (value != sentValue2) { + // Value has changed from last transmission, send the updated value + send(msg2.set(value==HIGH)); + sentValue2 = value; + } + + // Sleep until something happens with the sensor + sleep(SECONDARY_BUTTON_PIN-2, CHANGE, 0); +} diff --git a/examples/GatewaySerialCAN/GatewaySerialCAN.ino b/examples/GatewaySerialCAN/GatewaySerialCAN.ino new file mode 100644 index 000000000..bd036c68b --- /dev/null +++ b/examples/GatewaySerialCAN/GatewaySerialCAN.ino @@ -0,0 +1,102 @@ +/** +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Henrik Ekblad +* Copyright (C) 2013-2019 Sensnology AB +* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors +* +* Documentation: http://www.mysensors.org +* Support Forum: http://forum.mysensors.org +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 2 as published by the Free Software Foundation. +* +******************************* +* +* DESCRIPTION +* The ArduinoGateway prints data received from sensors on the serial link. +* The gateway accepts input on serial which will be sent out on radio network. +* +* The GW code is designed for Arduino Nano 328p / 16MHz +* +* Wire connections (OPTIONAL): +* - Inclusion button should be connected between digital pin 3 and GND +* - RX/TX/ERR leds need to be connected between +5V (anode) and digital pin 6/5/4 with resistor 270-330R in a series +* +* LEDs (OPTIONAL): +* - To use the feature, uncomment any of the MY_DEFAULT_xx_LED_PINs +* - RX (green) - blink fast on radio message received. In inclusion mode will blink fast only on presentation received +* - TX (yellow) - blink fast on radio message transmitted. In inclusion mode will blink slowly +* - ERR (red) - fast blink on error during transmission error or receive crc error +* +*/ + +// Enable debug prints to serial monitor +#define MY_DEBUG +//#define MY_DEBUG_VERBOSE_CAN +//#define MY_DEBUG_VERBOSE_CAN_INTERNAL + +// Enable and select radio type attached +#define MY_CAN +//#define MY_RADIO_NRF5_ESB +//#define MY_RADIO_RFM69 +//#define MY_RADIO_RFM95 + +// Set LOW transmit power level as default, if you have an amplified NRF-module and +// power your radio separately with a good regulator you can turn up PA level. +//#define MY_RF24_PA_LEVEL RF24_PA_LOW/ + +// Enable serial gateway +#define MY_GATEWAY_SERIAL + +// Define a lower baud rate for Arduinos running on 8 MHz (Arduino Pro Mini 3.3V & SenseBender) +//#if F_CPU == 8000000L/ +//#define MY_BAUD_RATE 38400/ +//#endif/ + +// Enable inclusion mode +#define MY_INCLUSION_MODE_FEATURE +// Enable Inclusion mode button on gateway +//#define MY_INCLUSION_BUTTON_FEATURE + +// Inverses behavior of inclusion button (if using external pullup) +//#define MY_INCLUSION_BUTTON_EXTERNAL_PULLUP + +// Set inclusion mode duration (in seconds) +//#define MY_INCLUSION_MODE_DURATION 60 +// Digital pin used for inclusion mode button +//#define MY_INCLUSION_MODE_BUTTON_PIN 3 + +// Set blinking period +//#define MY_DEFAULT_LED_BLINK_PERIOD 300 + +// Inverses the behavior of leds +//#define MY_WITH_LEDS_BLINKING_INVERSE + +// Flash leds on rx/tx/err +// Uncomment to override default HW configurations +//#define MY_DEFAULT_ERR_LED_PIN 4 // Error led pin +//#define MY_DEFAULT_RX_LED_PIN 6 // Receive led pin +//#define MY_DEFAULT_TX_LED_PIN 5 // the PCB, on board LED + +#include + +void setup() +{ + // Setup locally attached sensors +} + +void presentation() +{ + // Present locally attached sensors +} + +void loop() +{ + // Send locally attached sensor data here +} diff --git a/hal/transport/CAN/MyTransportCAN.cpp b/hal/transport/CAN/MyTransportCAN.cpp new file mode 100644 index 000000000..f300f92c0 --- /dev/null +++ b/hal/transport/CAN/MyTransportCAN.cpp @@ -0,0 +1,391 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2022 Sensnology AB + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * CAN bus transport added by Adam Słowik + * Copyright (C) 2022 Adam Słowik + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ +#include "hal/transport/CAN/driver/mcp_can.h" +#include "hal/transport/CAN/driver/mcp_can.cpp" +#include "MyTransportCAN.h" +#if defined(MY_DEBUG_VERBOSE_CAN) +#define CAN_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) //!< Debug print +#else +#define CAN_DEBUG(x,...) //!< DEBUG null +#endif +MCP_CAN CAN0(MY_CAN_CS); +bool canInitialized = false; + +//input buffer for raw data (from library). +long unsigned int rxId; +unsigned char len = 0; +unsigned char rxBuf[8]; +unsigned char _nodeId; + +//message id updated for every outgoing mesage +uint8_t message_id = 0; + +//buffer element +typedef struct { + uint8_t len; + uint8_t data[MAX_MESSAGE_SIZE]; + bool packetReceived[4]; //Maximum number of frames + uint8_t address; + uint8_t totalReceivedParts; + bool locked; + uint8_t age; + uint8_t packetId; + bool ready; +} CAN_Packet; + +//buffer +CAN_Packet packets[MY_CAN_BUF_SIZE]; + +//filter incoming messages (MCP2515 feature). +bool _initFilters() +{ + if (!canInitialized) { + return false; + } + uint8_t err = 0; + err += CAN0.setMode(MODE_CONFIG); + + err += CAN0.init_Mask(0, 1, + 0x0000FF00); // Init first mask. Only destination address will be used to filter messages + err += CAN0.init_Filt(0, 1, BROADCAST_ADDRESS << + 8); // Init first filter. Accept broadcast messages. + err += CAN0.init_Filt(1, 1, + _nodeId << 8); // Init second filter. Accept messages send to this node. + //second mask and filters need to be set. Otherwise all messages would be accepted. + err += CAN0.init_Mask(1, 1, 0xFFFFFFFF); // Init second mask. + err += CAN0.init_Filt(2, 1, 0xFFFFFFFF); // Init third filter. + err += CAN0.init_Filt(3, 1, 0xFFFFFFFF); // Init fourth filter. + err += CAN0.init_Filt(4, 1, 0xFFFFFFFF); // Init fifth filter. + err += CAN0.init_Filt(5, 1, 0xFFFFFFFF); // Init sixth filter. + err += CAN0.setMode(MCP_NORMAL); + hwPinMode(MY_CAN_INT, INPUT); + return err == 0; +} + +bool transportInit(void) +{ + CAN_DEBUG(PSTR("CAN:INIT:CS=%" PRIu8 ",INT=%" PRIu8 ",SPE=%" PRIu8 ",CLK=%" PRIu8 "\n"), MY_CAN_CS, + MY_CAN_INT, MY_CAN_SPEED, MY_CAN_CLOCK); + + if (CAN0.begin(MCP_STDEXT, MY_CAN_SPEED, MY_CAN_CLOCK) != CAN_OK) { + canInitialized = false; + return false; + } + canInitialized = true; + for (uint8_t i = 0; i < MY_CAN_BUF_SIZE; i++) { + _cleanSlot(i); + } + return _initFilters(); +} + +//clear single slot in buffer. +void _cleanSlot(uint8_t slot) +{ + packets[slot].locked = false; + packets[slot].len = 0; + packets[slot].address = 0; + packets[slot].totalReceivedParts = 0; + packets[slot].age = 0; + packets[slot].packetId = 0; + packets[slot].ready = false; + packets[slot].packetReceived[0]=false; + packets[slot].packetReceived[1]=false; + packets[slot].packetReceived[2]=false; + packets[slot].packetReceived[3]=false; +} + +//find empty slot in buffer +uint8_t _findCanPacketSlot() +{ + uint8_t slot = MY_CAN_BUF_SIZE; + uint8_t i; + for (i = 0; i < MY_CAN_BUF_SIZE; i++) { + if (packets[i].locked) { + packets[i].age++; + } else { + slot = i; + } + } + if (slot < MY_CAN_BUF_SIZE) { + return slot; + } + //if empty slot not found. Clear oldest message. + slot = 0; + for (i = 1; i < MY_CAN_BUF_SIZE; i++) { + if (packets[i].age > packets[slot].age) { + slot = i; + } + } + _cleanSlot(slot); + CAN_DEBUG(PSTR("!CAN:RCV:SLOT=%" PRIu8 " message dropped\n"), slot); + + return slot; +} + +//find slot with previous data parts. +uint8_t _findCanPacketSlot(long unsigned int from, long unsigned int currentPart, + long unsigned int messageId) +{ + uint8_t slot = MY_CAN_BUF_SIZE; + uint8_t i; + for (i = 0; i < MY_CAN_BUF_SIZE; i++) { +#if defined(MY_DEBUG_VERBOSE_CAN_INTERNAL) + CAN_DEBUG(PSTR("CAN:RCV:LCK=%" PRIu8 ",ADDR=%" PRIu8 + ",PACK_ID=%" PRIu8 ",RCV_PARTS=%" PRIu8 "\n"), packets[i].locked, packets[i].address, + packets[i].packetId, + packets[i].totalReceivedParts); +#endif + if (packets[i].locked && packets[i].address == from && packets[i].packetId == messageId && + packets[i].packetReceived[currentPart] == false) { + slot = i; + break; + } + } + return slot; +} + +// from address 8bits (A) +// to address 8 bits (B) +// current part number 4 bits (C) +// total part count 4 bits (D) +// 3 bits message_id (E) +// 1 bit require ack (F) +// 1 bit is ack (G) +// 1 bit is extended frame (H). (FIXED) +// 1 bit RTR (Remote Transmission Request) (I) (FIXED) +// 1 bit SRR (Substitute Remote Request) (J) (FIXED) +// header model (32 bits) +// HIJG FEEE DDDD CCCC BBBB BBBB AAAA AAAA +long unsigned int _buildHeader(uint8_t messageId, uint8_t totalPartCount, uint8_t currentPartNumber, + uint8_t toAddress, uint8_t fromAddress) +{ + long unsigned int header = + 0x80; // set H=1 (FIXED), I=0 (FIXED), J=0 (FIXED), G=0 (To be implemented), F=0 (To be implemented) + header += (messageId & 0x07); //set messageId + header = header << 4; + header += (totalPartCount & 0x0F);//set total part count + header = header << 4; + header += (currentPartNumber & 0x0F);//set current part number + header = header << 8; + header += toAddress;//set destination address + header = header << 8; + header += fromAddress;//set source address + CAN_DEBUG(PSTR("CAN:SND:CANH=%" PRIu32 ",ID=%" PRIu8 ",TOTAL=%" PRIu8 ",CURR=%" PRIu8 ",TO=%" PRIu8 + ",FROM=%" PRIu8 "\n"), + header, messageId, totalPartCount, currentPartNumber, toAddress, fromAddress); + return header; +} + +bool transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK) +{ + (void) noACK; // some ack is provided by CAN itself. TODO implement application layer ack. + const char *datap = static_cast(data); + //calculate number of frames + uint8_t noOfFrames = len / 8; + if (len % 8 != 0) { + noOfFrames++; + } + //update message_id + message_id++; + //make sure message_id isn't longer than 3 bits. + message_id = message_id & 0x07; + + CAN_DEBUG(PSTR("CAN:SND:LN=%" PRIu8 ",NOF=%" PRIu8 "\n"), len, noOfFrames); + uint8_t currentFrame; + for (currentFrame = 0; currentFrame < noOfFrames; currentFrame++) { + uint8_t partLen; + if (len <= 8) { + partLen = len; + } else if (currentFrame * 8 + 8 <= len) { + partLen = 8; + } else { + partLen = len % 8; + } + uint8_t buff[8]; + uint8_t j = 0; + for (j = 0; j < partLen; j++) { + buff[j] = datap[currentFrame * 8 + j]; + } + + CAN_DEBUG(PSTR("CAN:SND:LN=%" PRIu8 ",DTA0=%" PRIu8 ",DTA1=%" PRIu8 ",DTA2=%" PRIu8 ",DTA3=%" PRIu8 + ",DTA4=%" PRIu8 ",DTA5=%" PRIu8 ",DTA6=%" PRIu8 ",DTA7=%" PRIu8 "\n"), + partLen, buff[0], buff[1], buff[2], buff[3], buff[4], buff[5], buff[6], buff[7]); + + byte sndStat = CAN0.sendMsgBuf(_buildHeader(message_id, noOfFrames, currentFrame, to, _nodeId), + partLen, buff); + if (sndStat == CAN_OK) { + CAN_DEBUG(PSTR("CAN:SND:OK\n")); + } else { + CAN_DEBUG(PSTR("!CAN:SND:FAIL\n")); + return false; + } + } + return true; +} + +bool transportDataAvailable(void) +{ + if (!hwDigitalRead(MY_CAN_INT)) { // If CAN_INT pin is low, read receive buffer + CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) + long unsigned int from = (rxId & 0x000000FF); + // cppcheck-suppress unreadVariable + long unsigned int currentPart = (rxId & 0x000F0000) >> 16; + long unsigned int totalPartCount = (rxId & 0x00F00000) >> 20; + long unsigned int messageId = (rxId & 0x07000000) >> 24; +#if defined(MY_DEBUG_VERBOSE_CAN) + long unsigned int to = (rxId & 0x0000FF00) >> 8; + CAN_DEBUG(PSTR("CAN:RCV:CANH=%" PRIu32 ",ID=%" PRIu32 ",TOTAL=%" PRIu32 ",CURR=%" PRIu32 ",TO=%" + PRIu32 ",FROM=%" PRIu32 "\n"), + rxId, messageId, totalPartCount, currentPart, to, from); + CAN_DEBUG(PSTR("CAN:RCV:LN=%" PRIu8 ",DTA0=%" PRIu8 ",DTA1=%" PRIu8 ",DTA2=%" PRIu8 ",DTA3=%" PRIu8 + ",DTA4=%" PRIu8 ",DTA5=%" PRIu8 ",DTA6=%" PRIu8 ",DTA7=%" PRIu8 "\n"), + len, rxBuf[0], rxBuf[1], rxBuf[2], rxBuf[3], rxBuf[4], rxBuf[5], rxBuf[6], rxBuf[7]); +#endif + uint8_t slot; + slot = _findCanPacketSlot(from, currentPart, messageId); + if (slot == MY_CAN_BUF_SIZE) { + slot = _findCanPacketSlot(); + packets[slot].locked = true; + packets[slot].address = from; + packets[slot].packetId = messageId; + } + memcpy(packets[slot].data + currentPart*8, rxBuf, len); + packets[slot].totalReceivedParts++; + packets[slot].len += len; + packets[slot].packetReceived[currentPart]=true; + CAN_DEBUG(PSTR("CAN:RCV:SLOT=%" PRIu8 ",PART=%" PRIu8 "\n"), slot, + packets[slot].totalReceivedParts); + uint8_t i; + bool ready = true; + for (i = 0; i < totalPartCount; i++) { + if (packets[slot].packetReceived[i]==false) { + ready=false; + break; + } + } + if (ready) { + CAN_DEBUG(PSTR("CAN:RCV:SLOT=%" PRIu8 " complete\n"), slot); + } + packets[slot].ready=ready; + return ready; + } + return false; +} + +uint8_t transportReceive(void *data) +{ + uint8_t slot = MY_CAN_BUF_SIZE; + uint8_t i; + for (i = 0; i < MY_CAN_BUF_SIZE; i++) { + if (packets[i].ready) { + slot = i; + } + } + if (slot < MY_CAN_BUF_SIZE) { + memcpy(data, packets[slot].data, packets[slot].len); + i = packets[slot].len; + _cleanSlot(slot); + return i; + } else { + return (0); + } +} + +void transportSetAddress(const uint8_t address) +{ + _nodeId = address; + _initFilters(); +} + +uint8_t transportGetAddress(void) +{ + return _nodeId; +} + +bool transportSanityCheck(void) +{ + // not implemented yet + return true; +} + +void transportPowerDown(void) +{ + // Nothing to shut down here +} + +void transportPowerUp(void) +{ + // not implemented +} + +void transportSleep(void) +{ + // not implemented +} + +void transportStandBy(void) +{ + // not implemented +} + +int16_t transportGetSendingRSSI(void) +{ + // not implemented + return INVALID_RSSI; +} + +int16_t transportGetReceivingRSSI(void) +{ + // not implemented + return INVALID_RSSI; +} + +int16_t transportGetSendingSNR(void) +{ + // not implemented + return INVALID_SNR; +} + +int16_t transportGetReceivingSNR(void) +{ + // not implemented + return INVALID_SNR; +} + +int16_t transportGetTxPowerPercent(void) +{ + // not implemented + return static_cast(100); +} + +int16_t transportGetTxPowerLevel(void) +{ + // not implemented + return static_cast(100); +} + +bool transportSetTxPowerPercent(const uint8_t powerPercent) +{ + // not possible + (void) powerPercent; + return false; +} diff --git a/hal/transport/CAN/MyTransportCAN.h b/hal/transport/CAN/MyTransportCAN.h new file mode 100644 index 000000000..1d203451a --- /dev/null +++ b/hal/transport/CAN/MyTransportCAN.h @@ -0,0 +1,103 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2022 Sensnology AB + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * CAN bus transport added by Adam Słowik + * Copyright (C) 2022 Adam Słowik + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +/** +* @file MyTransportCAN.h +* +* @defgroup CANgrp CAN +* @ingroup internals +* @{ +* +* CAN driver-related log messages, format: [!]SYSTEM:[SUB SYSTEM:]MESSAGE +* - [!] Exclamation mark is prepended in case of error +* +* |E| SYS | SUB | Message | Comment +* |-|-----|------|---------------------------------|--------------------------------------------------------------------- +* | | CAN | INIT | CS=%%d,INT=%%d,SPE=%%d",CLK=%%d | Initialise CAN MCP2515 module radio, chip select (CS), + | | | | | interrupt pin (INT), CAN speed (SPE), CAN clock (CLK) +* |!| CAN | RCV | SLOT=%%d message dropped. | clean buffer (SLOT) to make space for new message. +* | | CAN | RCV | LCK=%%d,ADDR=%%d,PACK_ID=%%d, | Print current slot info. Is locked (LCK), send from (ADDR), +* | | | | RCV_PARTS=%%d | packet id (PACK_ID), number of received parts (RCV_PARTS) +* | | CAN | SND | CANH=%%d,ID=%%d,TOTAL=%%d, | prints raw frame header (CANH), and decoded data from header: +* | | | | CURR=%%d,TO=%%d,FROM=%%d | message id (ID), total number of frames to be send (TOTAL), +* | | | | | current frame number (CURR), destination address (TO), +* | | | | | source address (FROM) +* | | CAN | SND | LN=%%d,NOF=%%d | prints total data length to be send (LN) +* | | | | | and total number of frames (NOF) +* | | CAN | SND | LN=%%d,DTA0=%%d,DTA1=%%d, | prints body length of message (LN) and data itself (DTA0...DTA7) +* | | | | DTA2=%%d,DTA3=%%d,DTA4=%%d, | +* | | | | DTA5=%%d,DTA6=%%d,DTA7=%%d | +* | | CAN | SND | OK | frame send +* |!| CAN | SND | FAIL | frame not send +* | | CAN | RCV | CANH=%%d,ID=%%d,TOTAL=%%d, | received header (CANH), and decoded data from header: +* | | | | CURR=%%d,TO=%%d,FROM=%%d | message id (ID), total number of frames to be send (TOTAL), +* | | | | | current frame number (CURR), destination address (TO), +* | | CAN | RCV | LN=%%d,DTA0=%%d,DTA1=%%d, | prints body length of received message (LN) +* | | | | DTA2=%%d,DTA3=%%d,DTA4=%%d, | and received data itself (DTA0...DTA7) +* | | | | DTA5=%%d,DTA6=%%d,DTA7=%%d | +* | | CAN | RCV | SLOT=%%d,PART=%%d | frame stored in buffer under (SLOT). Number of received part (PART) +* | | CAN | RCV | SLOT=%%d complete | message in buffer under (SLOT) is complete. +* +*/ +bool _initFilters(); +bool transportInit(void); + +void _cleanSlot(uint8_t slot); + +uint8_t _findCanPacketSlot(); + +uint8_t _findCanPacketSlot(long unsigned int from, long unsigned int currentPart, + long unsigned int messageId); + +bool transportSend(const uint8_t to, const void* data, const uint8_t len, const bool noACK); + +bool transportDataAvailable(void); + +uint8_t transportReceive(void* data); + +void transportSetAddress(const uint8_t address); + +uint8_t transportGetAddress(void); + +bool transportSanityCheck(void); + +void transportPowerDown(void); + +void transportPowerUp(void); + +void transportSleep(void); + +void transportStandBy(void); + +int16_t transportGetSendingRSSI(void); + +int16_t transportGetReceivingRSSI(void); + +int16_t transportGetSendingSNR(void); + +int16_t transportGetReceivingSNR(void); + +int16_t transportGetTxPowerPercent(void); + +int16_t transportGetTxPowerLevel(void); + +bool transportSetTxPowerPercent(const uint8_t powerPercent); diff --git a/hal/transport/CAN/driver/.gitattributes b/hal/transport/CAN/driver/.gitattributes new file mode 100644 index 000000000..412eeda78 --- /dev/null +++ b/hal/transport/CAN/driver/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/hal/transport/CAN/driver/.gitignore b/hal/transport/CAN/driver/.gitignore new file mode 100644 index 000000000..5ebd21a16 --- /dev/null +++ b/hal/transport/CAN/driver/.gitignore @@ -0,0 +1,163 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store diff --git a/hal/transport/CAN/driver/MCP2515Calc.xlsx b/hal/transport/CAN/driver/MCP2515Calc.xlsx new file mode 100644 index 000000000..66d1a3027 Binary files /dev/null and b/hal/transport/CAN/driver/MCP2515Calc.xlsx differ diff --git a/hal/transport/CAN/driver/README.md b/hal/transport/CAN/driver/README.md new file mode 100644 index 000000000..291a1d06c --- /dev/null +++ b/hal/transport/CAN/driver/README.md @@ -0,0 +1,39 @@ +MCP_CAN Library for Arduino +============== +MCP_CAN library v1.5 +This library is compatible with any shield or board that uses the MCP2515 or MCP25625 CAN protocol controller. + +This version supports setting the ID filter mode of the protocol controller, the BAUD rate with clock speed with the begin() function. Baudrates 5k, 10k, 20k, 50k, 100k, 125k, 250k, 500k, & 1000k using 16MHz clock on the MCP2515 are confirmed to work using a Peak-System PCAN-USB dongle as a reference. Baudrates for 8MHz and 20MHz crystals are yet to be confirmed but were calculated appropiately. + +**The readMsgBuf() functions bring in the message ID. The getCanId() function is obsolete and no longer exists, don't use it.** + +The readMsgBuf(*ID, *DLC, *DATA) function will return the ID type (extended or standard) and it will bring back the remote request status bit. +If the ID AND 0x80000000 EQUALS 0x80000000, the ID is of the Extended type, otherwise it is standard. +If the ID AND 0x40000000 EQUALS 0x40000000, the message is a remote request. + +The readMsgBuf(*ID, *EXT, *DLC, *DATA) function will return the ID unaltered and doesn't inform us of a remote request. +If EXT is true, the ID is extended. + +The sendMsgBuf(ID, DLC, DATA) function can send extended or standard IDs. +To mark an ID as extended, OR the ID with 0x80000000. +To send a remote request, OR the ID with 0x40000000. + +The sendMsgBuf(ID, EXT, DLC, DATA) has not changed other than fixing return values. + +Using the setMode() function the sketch can now put the protocol controller into sleep, loop-back, or listen-only modes as well as normal operation. Right now the code defaults to loop-back mode after the begin() function runs. I have found this to increase the stability of filtering when the controller is initialized while connected to an active bus. + +User can enable and disable (default) One-Shot transmission mode from the sketch using enOneShotTX() or disOneShotTX() respectively. + +Installation +============== +Copy this into the "[.../MySketches/]libraries/" folder and restart the Arduino editor. + +NOTE: If an older version of the library exists (e.g. CAN_BUS_Shield) be sure to remove it from the libraries folder or replace the files with those in this library to avoid conflicts. + + +Help and Support +============== +This is primarily for non-bug related issues: Please start a *new thread* in an appropriate area at Seeedstudio forums or Arduino.cc forums and then send me (coryjfowler) a link through the PM system, my user name is the same as it is here. I will receive an email about the PM and generally get to it with-in a week or less. Keep in mind, I do this in my spare time. + + +*Happy Coding!* diff --git a/hal/transport/CAN/driver/examples/CAN_loopback/CAN_loopback.ino b/hal/transport/CAN/driver/examples/CAN_loopback/CAN_loopback.ino new file mode 100644 index 000000000..dfafeacea --- /dev/null +++ b/hal/transport/CAN/driver/examples/CAN_loopback/CAN_loopback.ino @@ -0,0 +1,88 @@ +/* CAN Loopback Example + * This example sends a message once a second and receives that message + * no CAN bus is required. This example will test the functionality + * of the protocol controller, and connections to it. + * + * Written By: Cory J. Fowler - October 5th 2016 + */ + +#include +#include + +// CAN TX Variables +unsigned long prevTX = 0; // Variable to store last execution time +const unsigned int invlTX = 1000; // One second interval constant +byte data[] = {0xAA, 0x55, 0x01, 0x10, 0xFF, 0x12, 0x34, 0x56}; // Generic CAN data to send + +// CAN RX Variables +long unsigned int rxId; +unsigned char len; +unsigned char rxBuf[8]; + +// Serial Output String Buffer +char msgString[128]; + +// CAN0 INT and CS +#define CAN0_INT 2 // Set INT to pin 2 +MCP_CAN CAN0(10); // Set CS to pin 10 + + +void setup() +{ + Serial.begin(115200); // CAN is running at 500,000BPS; 115,200BPS is SLOW, not FAST, thus 9600 is crippling. + + // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled. + if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) + Serial.println("MCP2515 Initialized Successfully!"); + else + Serial.println("Error Initializing MCP2515..."); + + // Since we do not set NORMAL mode, we are in loopback mode by default. + //CAN0.setMode(MCP_NORMAL); + + pinMode(CAN0_INT, INPUT); // Configuring pin for /INT input + + Serial.println("MCP2515 Library Loopback Example..."); +} + +void loop() +{ + if(!digitalRead(CAN0_INT)) // If CAN0_INT pin is low, read receive buffer + { + CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) + + if((rxId & 0x80000000) == 0x80000000) // Determine if ID is standard (11 bits) or extended (29 bits) + sprintf(msgString, "Extended ID: 0x%.8lX DLC: %1d Data:", (rxId & 0x1FFFFFFF), len); + else + sprintf(msgString, "Standard ID: 0x%.3lX DLC: %1d Data:", rxId, len); + + Serial.print(msgString); + + if((rxId & 0x40000000) == 0x40000000){ // Determine if message is a remote request frame. + sprintf(msgString, " REMOTE REQUEST FRAME"); + Serial.print(msgString); + } else { + for(byte i = 0; i= invlTX){ // Send this at a one second interval. + prevTX = millis(); + byte sndStat = CAN0.sendMsgBuf(0x100, 8, data); + + if(sndStat == CAN_OK) + Serial.println("Message Sent Successfully!"); + else + Serial.println("Error Sending Message..."); + + } +} + +/********************************************************************************************************* + END FILE +*********************************************************************************************************/ diff --git a/hal/transport/CAN/driver/examples/CAN_receive/CAN_receive.ino b/hal/transport/CAN/driver/examples/CAN_receive/CAN_receive.ino new file mode 100644 index 000000000..958c9a083 --- /dev/null +++ b/hal/transport/CAN/driver/examples/CAN_receive/CAN_receive.ino @@ -0,0 +1,62 @@ +// CAN Receive Example +// + +#include +#include + +long unsigned int rxId; +unsigned char len = 0; +unsigned char rxBuf[8]; +char msgString[128]; // Array to store serial string + +#define CAN0_INT 2 // Set INT to pin 2 +MCP_CAN CAN0(10); // Set CS to pin 10 + + +void setup() +{ + Serial.begin(115200); + + // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled. + if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) + Serial.println("MCP2515 Initialized Successfully!"); + else + Serial.println("Error Initializing MCP2515..."); + + CAN0.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP2515 sends acks to received data. + + pinMode(CAN0_INT, INPUT); // Configuring pin for /INT input + + Serial.println("MCP2515 Library Receive Example..."); +} + +void loop() +{ + if(!digitalRead(CAN0_INT)) // If CAN0_INT pin is low, read receive buffer + { + CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) + + if((rxId & 0x80000000) == 0x80000000) // Determine if ID is standard (11 bits) or extended (29 bits) + sprintf(msgString, "Extended ID: 0x%.8lX DLC: %1d Data:", (rxId & 0x1FFFFFFF), len); + else + sprintf(msgString, "Standard ID: 0x%.3lX DLC: %1d Data:", rxId, len); + + Serial.print(msgString); + + if((rxId & 0x40000000) == 0x40000000){ // Determine if message is a remote request frame. + sprintf(msgString, " REMOTE REQUEST FRAME"); + Serial.print(msgString); + } else { + for(byte i = 0; i +#include + +MCP_CAN CAN0(10); // Set CS to pin 10 + +void setup() +{ + Serial.begin(115200); + + // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled. + if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) Serial.println("MCP2515 Initialized Successfully!"); + else Serial.println("Error Initializing MCP2515..."); + + CAN0.setMode(MCP_NORMAL); // Change to normal mode to allow messages to be transmitted +} + +byte data[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + +void loop() +{ + // send data: ID = 0x100, Standard CAN Frame, Data length = 8 bytes, 'data' = array of data bytes to send + byte sndStat = CAN0.sendMsgBuf(0x100, 0, 8, data); + if(sndStat == CAN_OK){ + Serial.println("Message Sent Successfully!"); + } else { + Serial.println("Error Sending Message..."); + } + delay(100); // send data per 100ms +} + +/********************************************************************************************************* + END FILE +*********************************************************************************************************/ diff --git a/hal/transport/CAN/driver/examples/CAN_to_Ethernet/CAN_to_Ethernet.ino b/hal/transport/CAN/driver/examples/CAN_to_Ethernet/CAN_to_Ethernet.ino new file mode 100644 index 000000000..157341ded --- /dev/null +++ b/hal/transport/CAN/driver/examples/CAN_to_Ethernet/CAN_to_Ethernet.ino @@ -0,0 +1,59 @@ +// CAN to Ethernet +// Jan 28th, 2014 +// Written by: Cory J. Fowler + +#include +#include +#include +#include + +// Change these for your network! +byte mac[] = {0x00, 0x55, 0x66, 0xEE, 0xFF, 0xFF}; +IPAddress ip(10, 100, 50, 233); +IPAddress gateway(10, 100, 50, 254); +IPAddress dest(10, 100, 50, 210); + +unsigned int localPort = 8888; +unsigned int remPort = 54321; + +unsigned long rxId; +byte len = 0; +byte rxBuf[8]; +char buffer[50]; + +MCP_CAN CAN0(9); // Set CS to pin 9 + +EthernetUDP UDP; +void setup() +{ + Serial.begin(115200); +// CAN0.begin(CAN_250KBPS); // init CAN Bus with 250kb/s baudrate + CAN0.begin(MCP_ANY, CAN_250KBPS, MCP_16MHZ); // init CAN Bus with 250kb/s baudrate at 16MHz with Mask & Filters Disabled + CAN0.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP2515 sends acks to received data. + pinMode(2, INPUT); // Setting pin 2, MCP2515 /INT, to input mode + Ethernet.begin(mac,ip); // Initialize Ethernet + UDP.begin(localPort); // Initialize the UDP listen port that is currently unused! + + Serial.println("CAN to Ethernet..."); +} + +void loop() +{ + if(!digitalRead(2)) // If pin 2 is low, read receive buffer + { + CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read Data: rxID = Message ID, len = Data Length, buf = Data Byte(s) +// CAN0.readMsgBuf(&len, rxBuf); // Read Data: len = Data Length, buf = Data Byte(s) +// rxId = CAN0.getCanId(); // Function will be depreciated soon due to readMsgBuf now returning ID + + sprintf(buffer, "ID: %.8lX Data: %.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X\n\r", + rxId, rxBuf[0], rxBuf[1], rxBuf[2], rxBuf[3], rxBuf[4], rxBuf[5], rxBuf[6], rxBuf[7]); + + UDP.beginPacket(dest, remPort); + UDP.write(buffer); + UDP.endPacket(); + } +} + +/********************************************************************************************************* + END FILE +*********************************************************************************************************/ diff --git a/hal/transport/CAN/driver/examples/CAN_to_Ethernet/udp_listen.pl b/hal/transport/CAN/driver/examples/CAN_to_Ethernet/udp_listen.pl new file mode 100644 index 000000000..413c803d2 --- /dev/null +++ b/hal/transport/CAN/driver/examples/CAN_to_Ethernet/udp_listen.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl -w +#This works on Debian Linux +#I have not tested it on a Windows system running Perl, but it should work if all dependencies are met. +#Also, FIREWALL. <--- I gaurantee that will be the issue. + +use IO::Socket; + +### Create UDP Listen Socket +my $udpsocket = new IO::Socket::INET ( + LocalPort => '54321', + Proto => 'udp', + ); + die "Could not create socket: $!\n" unless $udpsocket; + +### Data Manipulation and Display +while(1) { + $udpsocket->recv(my $data,512); + print $data; +} diff --git a/hal/transport/CAN/driver/examples/Dual_CAN/Dual_CAN.ino b/hal/transport/CAN/driver/examples/Dual_CAN/Dual_CAN.ino new file mode 100644 index 000000000..860feb7d0 --- /dev/null +++ b/hal/transport/CAN/driver/examples/Dual_CAN/Dual_CAN.ino @@ -0,0 +1,55 @@ +// Demo: Dual CAN-BUS Shields, Data Pass-through +// Written by: Cory J. Fowler +// January 31st 2014 +// This examples the ability of this library to support more than one MCP2515 based CAN interface. + + +#include +#include + +unsigned long rxId; +byte len; +byte rxBuf[8]; + +byte txBuf0[] = {AA,55,AA,55,AA,55,AA,55}; +byte txBuf1[] = {55,AA,55,AA,55,AA,55,AA}; + +MCP_CAN CAN0(10); // CAN0 interface usins CS on digital pin 10 +MCP_CAN CAN1(9); // CAN1 interface using CS on digital pin 9 + +void setup() +{ + Serial.begin(115200); + + // init CAN0 bus, baudrate: 250k@16MHz + if(CAN0.begin(MCP_EXT, CAN_250KBPS, MCP_16MHZ) == CAN_OK){ + Serial.print("CAN0: Init OK!\r\n"); + CAN0.setMode(MCP_NORMAL); + } else Serial.print("CAN0: Init Fail!!!\r\n"); + + // init CAN1 bus, baudrate: 250k@16MHz + if(CAN1.begin(MCP_EXT, CAN_250KBPS, MCP_16MHZ) == CAN_OK){ + Serial.print("CAN1: Init OK!\r\n"); + CAN1.setMode(MCP_NORMAL); + } else Serial.print("CAN1: Init Fail!!!\r\n"); + + SPI.setClockDivider(SPI_CLOCK_DIV2); // Set SPI to run at 8MHz (16MHz / 2 = 8 MHz) + + CAN0.sendMsgBuf(0x1000000, 1, 8, tx0Buf); + CAN1.sendMsgBuf(0x1000001, 1, 8, tx1Buf); +} + +void loop(){ + if(!digitalRead(2)){ // If pin 2 is low, read CAN0 receive buffer + CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) + CAN1.sendMsgBuf(rxId, 1, len, rxBuf); // Immediately send message out CAN1 interface + } + if(!digitalRead(3)){ // If pin 3 is low, read CAN1 receive buffer + CAN1.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) + CAN0.sendMsgBuf(rxId, 1, len, rxBuf); // Immediately send message out CAN0 interface + } +} + +/********************************************************************************************************* + END FILE +*********************************************************************************************************/ diff --git a/hal/transport/CAN/driver/examples/Extended_MaskFilter/Extended_MaskFilter.ino b/hal/transport/CAN/driver/examples/Extended_MaskFilter/Extended_MaskFilter.ino new file mode 100644 index 000000000..e9dbd541d --- /dev/null +++ b/hal/transport/CAN/driver/examples/Extended_MaskFilter/Extended_MaskFilter.ino @@ -0,0 +1,87 @@ + +// MCP2515 Mask and Filter example for extended CAN message frames. +// Written by Cory J. Fowler (20140717) + +/*********************************************************************************** +If you send the following extended IDs below to an Arduino loaded with this sketch +you will find that 0x00FFCC00 and 0x00FF9900 will not get in. + + ID in Hex is the same as the Filter in Hex. + 0x00FFEE00 + 0x00FFDD00 + 0x00FFCC00 This example will NOT be receiving this ID + 0x00FFBB00 + 0x00FFAA00 + 0x00FF9900 This example will NOT be receiving this ID + 0x00FF8800 + 0x00FF7700 + + This mask will check the filters against ID bits 23 through 8. + (Those familiar with J1939 might see why I used this mask.) + MASK = 0x00FFFF00 + If there is an explicit filter match to those bits, the message will be passed to the + receive buffer and the interrupt pin will be set. + + This example will NOT be exclusive to ONLY the above message IDs, for that a mask such + as the below would be used: + MASK = 0x1FFFFFFF + + At the moment, to disable a filter or mask, copy the value of a used filter or mask. + +***********************************************************************************/ + + +#include +#include + +long unsigned int rxId; +unsigned char len = 0; +unsigned char rxBuf[8]; + +MCP_CAN CAN0(10); // Set CS to pin 10 + +void setup() +{ + Serial.begin(115200); + if(CAN0.begin(MCP_STDEXT, CAN_500KBPS, MCP_16MHZ) == CAN_OK) Serial.print("MCP2515 Init Okay!!\r\n"); + else Serial.print("MCP2515 Init Failed!!\r\n"); + pinMode(2, INPUT); // Setting pin 2 for /INT input + + CAN0.init_Mask(0,1,0x00FFFF00); // Init first mask... + CAN0.init_Filt(0,1,0x00FFEE00); // Init first filter... + CAN0.init_Filt(1,1,0x00FFDD00); // Init second filter... + + CAN0.init_Mask(1,1,0x00FFFF00); // Init second mask... + CAN0.init_Filt(2,1,0x00FFBB00); // Init third filter... + CAN0.init_Filt(3,1,0x00FFAA00); // Init fouth filter... + CAN0.init_Filt(4,1,0x00FF8800); // Init fifth filter... + CAN0.init_Filt(5,1,0x00FF7700); // Init sixth filter... + + Serial.println("MCP2515 Library Mask & Filter Example..."); + CAN0.setMode(MCP_NORMAL); // Change to normal mode to allow messages to be transmitted +} + +void loop() +{ + if(!digitalRead(2)) // If pin 2 is low, read receive buffer + { + CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) + Serial.print("ID: "); + Serial.print(rxId, HEX); + Serial.print(" Data: "); + for(int i = 0; i +#include + + +#define PAD 0x00 + +// What CAN ID type? Standard or Extended +#define standard 0 + +// 7E0/8 = Engine ECM +// 7E1/9 = Transmission ECM + +#if standard == 1 + #define REPLY_ID 0x7E9 + #define LISTEN_ID 0x7E1 + #define FUNCTIONAL_ID 0x7DF +#else + #define REPLY_ID 0x98DAF101 + #define LISTEN_ID 0x98DA01F1 + #define FUNCTIONAL_ID 0x98DB33F1 +#endif + + +// CAN RX Variables +unsigned long rxId; +byte dlc; +byte rxBuf[8]; + +// CAN Interrupt and Chip Select +#define CAN0_INT 2 // Set CAN0 INT to pin 2 +MCP_CAN CAN0(9); // Set CAN0 CS to pin 9 + + +void setup() +{ + Serial.begin(115200); + while(!Serial); + + // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled. + if(CAN0.begin(MCP_STDEXT, CAN_500KBPS, MCP_20MHZ) == CAN_OK) + Serial.println("MCP2515 Initialized Successfully!"); + else + Serial.println("Error Initializing MCP2515..."); + + +#if standard == 1 + // Standard ID Filters + CAN0.init_Mask(0,0x7F00000); // Init first mask... + CAN0.init_Filt(0,0x7DF0000); // Init first filter... + CAN0.init_Filt(1,0x7E10000); // Init second filter... + + CAN0.init_Mask(1,0x7F00000); // Init second mask... + CAN0.init_Filt(2,0x7DF0000); // Init third filter... + CAN0.init_Filt(3,0x7E10000); // Init fouth filter... + CAN0.init_Filt(4,0x7DF0000); // Init fifth filter... + CAN0.init_Filt(5,0x7E10000); // Init sixth filter... + +#else + // Extended ID Filters + CAN0.init_Mask(0,0x90FFFF00); // Init first mask... + CAN0.init_Filt(0,0x90DB3300); // Init first filter... + CAN0.init_Filt(1,0x90DA0100); // Init second filter... + + CAN0.init_Mask(1,0x90FFFF00); // Init second mask... + CAN0.init_Filt(2,0x90DB3300); // Init third filter... + CAN0.init_Filt(3,0x90DA0100); // Init fouth filter... + CAN0.init_Filt(4,0x90DB3300); // Init fifth filter... + CAN0.init_Filt(5,0x90DA0100); // Init sixth filter... +#endif + + CAN0.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP2515 sends acks to received data. + + pinMode(CAN0_INT, INPUT); // Configuring pin for /INT input + + Serial.println("OBD-II CAN Simulator"); +} + +void loop() +{ + if(!digitalRead(CAN0_INT)) // If CAN0_INT pin is low, read receive buffer + { + CAN0.readMsgBuf(&rxId, &dlc, rxBuf); // Get CAN data + + // First request from most adapters... + if(rxId == FUNCTIONAL_ID){ + obdReq(rxBuf); + } + } +} + + +void obdReq(byte *data){ + byte numofBytes = data[0]; + byte mode = data[1] & 0x0F; + byte pid = data[2]; + bool tx = false; + byte txData[] = {0x00,(0x40 | mode),pid,PAD,PAD,PAD,PAD,PAD}; + + + //txData[1] = 0x40 | mode; + //txData[2] = pid; + + //============================================================================= + // MODE $01 - Show current data + //============================================================================= + if(mode == 0x01){ + if(pid == 0x00){ // Supported PIDs 01-20 + txData[0] = 0x06; + + txData[3] = 0x80; + txData[4] = 0x38; + txData[5] = 0x00; + txData[6] = 0x01; + tx = true; + } + else if(pid == 0x01){ // Monitor status since DTs cleared. + bool MIL = true; + byte DTC = 5; + txData[0] = 0x06; + + txData[3] = (MIL << 7) | (DTC & 0x7F); + txData[4] = 0x07; + txData[5] = 0xFF; + txData[6] = 0x00; + tx = true; + } +// else if(pid == 0x02){ // Freeze DTC +// } + else if(pid == 0x03){ // Fuel system status + txData[0] = 0x03; + + txData[3] = 0xFA; + tx = true; + } +// else if(pid == 0x04){ // Calculated engine load +// } + else if(pid == 0x05){ // Engine coolant temperature + txData[0] = 0x03; + + txData[3] = 0xFA; + tx = true; + } +// else if(pid == 0x06){ // Short term fuel trim - Bank 1 +// } +// else if(pid == 0x07){ // Long tern fuel trim - Bank 1 +// } +// else if(pid == 0x08){ // Short term fuel trim - Bank 2 +// } +// else if(pid == 0x09){ // Long term fuel trim - Bank 2 +// } +// else if(pid == 0x0A){ // Fuel pressure (gauge) +// } + else if(pid == 0x0B){ // Intake manifold absolute pressure + txData[0] = 0x03; + + txData[3] = 0x64; + tx = true; + } + else if(pid == 0x0C){ // Engine RPM + txData[0] = 0x04; + + txData[3] = 0x9C; + txData[4] = 0x40; + tx = true; + } + else if(pid == 0x0D){ // Vehicle speed + txData[0] = 0x03; + + txData[3] = 0xFA; + tx = true; + } +// else if(pid == 0x0E){ // Timing advance +// } + else if(pid == 0x0F){ // Intake air temperature + txData[0] = 0x03; + + txData[3] = 0xFA; + tx = true; + } +// else if(pid == 0x10){ // MAF air flow rate +// } + else if(pid == 0x11){ // Throttle position + txData[0] = 0x03; + + txData[3] = 0xFA; + tx = true; + } +// else if(pid == 0x12){ // Commanded secondary air status +// } +// else if(pid == 0x13){ // Oxygen sensors present (in 2 banks) +// } +// else if(pid == 0x14){ // Oxygen Sensor 1 (Voltage & Trim) +// } +// else if(pid == 0x15){ // Oxygen Sensor 2 (Voltage & Trim) +// } +// else if(pid == 0x16){ // Oxygen Sensor 3 (Voltage & Trim) +// } +// else if(pid == 0x17){ // Oxygen Sensor 4 (Voltage & Trim) +// } +// else if(pid == 0x18){ // Oxygen Sensor 5 (Voltage & Trim) +// } +// else if(pid == 0x19){ // Oxygen Sensor 6 (Voltage & Trim) +// } +// else if(pid == 0x1A){ // Oxygen Sensor 7 (Voltage & Trim) +// } +// else if(pid == 0x1B){ // Oxygen Sensor 8 (Voltage & Trim) +// } +// else if(pid == 0x1C){ // OBD standards this vehicle conforms to +// } +// else if(pid == 0x1D){ // Oxygen sensors present (in 4 banks) +// } +// else if(pid == 0x1E){ // Auxillary input status +// } +// else if(pid == 0x1F){ // Run time since engine start +// } + else if(pid == 0x20){ // Supported PIDs 21-40 + txData[0] = 0x06; + + txData[3] = 0x80; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x01; + tx = true; + } + else if(pid == 0x21){ // Distance traveled with MIL on + txData[0] = 0x04; + + txData[3] = 0x00; + txData[4] = 0x23; + tx = true; + } +// else if(pid == 0x22){ // Fuel rail pressure (Relative to Manifold Vacuum) +// } +// else if(pid == 0x23){ // Fuel rail gauge pressure (diesel or gasoline direct injection) +// } +// else if(pid == 0x24){ // Oxygen Sensor 1 (Fuel to Air & Voltage) +// } +// else if(pid == 0x25){ // Oxygen Sensor 2 (Fuel to Air & Voltage) +// } +// else if(pid == 0x26){ // Oxygen Sensor 3 (Fuel to Air & Voltage) +// } +// else if(pid == 0x27){ // Oxygen Sensor 4 (Fuel to Air & Voltage) +// } +// else if(pid == 0x28){ // Oxygen Sensor 5 (Fuel to Air & Voltage) +// } +// else if(pid == 0x29){ // Oxygen Sensor 6 (Fuel to Air & Voltage) +// } +// else if(pid == 0x2A){ // Oxygen Sensor 7 (Fuel to Air & Voltage) +// } +// else if(pid == 0x2B){ // Oxygen Sensor 8 (Fuel to Air & Voltage) +// } +// else if(pid == 0x2C){ // Commanded EGR +// } +// else if(pid == 0x2D){ // EGR Error +// } +// else if(pid == 0x2E){ // Commanded evaporative purge +// } +// else if(pid == 0x2F){ // Fuel tank level input +// } +// else if(pid == 0x30){ // Warm-ups since codes cleared +// } +// else if(pid == 0x31){ // Distance traveled since codes cleared +// } +// else if(pid == 0x32){ // Evap. System Vapor Pressure +// } +// else if(pid == 0x33){ // Absolute Barometric Pressure +// } +// else if(pid == 0x34){ // Oxygen Sensor 1 (Fuel to Air & Current) +// } +// else if(pid == 0x35){ // Oxygen Sensor 2 (Fuel to Air & Current) +// } +// else if(pid == 0x36){ // Oxygen Sensor 3 (Fuel to Air & Current) +// } +// else if(pid == 0x37){ // Oxygen Sensor 4 (Fuel to Air & Current) +// } +// else if(pid == 0x38){ // Oxygen Sensor 5 (Fuel to Air & Current) +// } +// else if(pid == 0x39){ // Oxygen Sensor 6 (Fuel to Air & Current) +// } +// else if(pid == 0x3A){ // Oxygen Sensor 7 (Fuel to Air & Current) +// } +// else if(pid == 0x3B){ // Oxygen Sensor 8 (Fuel to Air & Current) +// } +// else if(pid == 0x3C){ // Catalyst Temperature: Bank 1, Sensor 1 +// } +// else if(pid == 0x3D){ // Catalyst Temperature: Bank 2, Sensor 1 +// } +// else if(pid == 0x3E){ // Catalyst Temperature: Bank 1, Sensor 2 +// } +// else if(pid == 0x3F){ // Catalyst Temperature: Bank 2, Sensor 2 +// } + else if(pid == 0x40){ // Supported PIDs 41-60 + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x08; + txData[5] = 0x00; + txData[6] = 0x0D; + tx = true; + } +// else if(pid == 0x41){ // Monitor status this drive cycle +// } +// else if(pid == 0x42){ // Control module voltage +// } +// else if(pid == 0x43){ // Absolute load value +// } +// else if(pid == 0x44){ // Fuel-Air commanded equivalence ratio +// } +// else if(pid == 0x45){ // Relative throttle position +// } +// else if(pid == 0x46){ // Ambient air temperature +// } +// else if(pid == 0x47){ // Absolute throttle postion B +// } +// else if(pid == 0x48){ // Absolute throttle postion C +// } +// else if(pid == 0x49){ // Accelerator pedal position D +// } +// else if(pid == 0x4A){ // Accelerator pedal position E +// } +// else if(pid == 0x4B){ // Accelerator pedal position F +// } +// else if(pid == 0x4C){ // Commanded throttle actuator +// } + else if(pid == 0x4D){ // Time run with MIL on + txData[0] = 0x04; + + txData[3] = 0x00; + txData[4] = 0x3C; + tx = true; + } +// else if(pid == 0x4E){ // Time since troble codes cleared +// } +// else if(pid == 0x4F){ // Time since trouble codes cleared +// } +// else if(pid == 0x50){ // Maximum value for Fuel-Air equivalence ratio, oxygen sensor voltage, oxygen sensro current, and intake manifold absolute-pressure +// } +// else if(pid == 0x51){ // Fuel Type +// } +// else if(pid == 0x52){ // Ethanol Fuel % +// } +// else if(pid == 0x53){ // Absolute evap system vapor pressure +// } +// else if(pid == 0x54){ // Evap system vapor pressure +// } +// else if(pid == 0x55){ // Short term secondary oxygen sensor trim, A: bank 1, B: bank 3 +// } +// else if(pid == 0x56){ // Long term secondary oxygen sensor trim, A: bank 1, B: bank 3 +// } +// else if(pid == 0x57){ // Short term secondary oxygen sensor trim, A: bank 2, B: bank 4 +// } +// else if(pid == 0x58){ // Long term secondary oxygen sensor trim, A: bank 2, B: bank 4 +// } +// else if(pid == 0x59){ // Fuel rail absolute pressure +// } +// else if(pid == 0x5A){ // Relative accelerator pedal position +// } +// else if(pid == 0x5B){ // Hybrid battery pack remaining life +// } + else if(pid == 0x5C){ // Engine oil Temperature + txData[0] = 0x03; + + txData[3] = 0x1E; + tx = true; + } + else if(pid == 0x5D){ // Fuel injection timing + txData[0] = 0x04; + + txData[3] = 0x61; + txData[4] = 0x80; + tx = true; + } + else if(pid == 0x5E){ // Engine fuel rate + txData[0] = 0x04; + + txData[3] = 0x07; + txData[4] = 0xD0; + tx = true; + } +// else if(pid == 0x5F){ // Emissions requirements to which vehicle is designed +// } + else if(pid == 0x60){ // Supported PIDs 61-80 + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x01; + tx = true; + } +// else if(pid == 0x61){ // Driver's demand engine - percent torque +// } +// else if(pid == 0x62){ // Actual engine - percent torque +// } +// else if(pid == 0x63){ // Engine reference torque +// } +// else if(pid == 0x64){ // Engine percent torque data +// } +// else if(pid == 0x65){ // Auxiliary input / output supported +// } +// else if(pid == 0x66){ // Mas air flow sensor +// } +// else if(pid == 0x67){ // Engine coolant temperature +// } +// else if(pid == 0x68){ // Intake air temperature sensor +// } +// else if(pid == 0x69){ // Commanded EGR and EGR error +// } +// else if(pid == 0x6A){ // Commanded Diesel intake air flow control and relative intake air flow position +// } +// else if(pid == 0x6B){ // Exhaust gas recirculation temperature +// } +// else if(pid == 0x6C){ // Commanded throttle actuator control and relative throttle position +// } +// else if(pid == 0x6D){ // Fuel pressure control system +// } +// else if(pid == 0x6E){ // Injection pressure control system +// } +// else if(pid == 0x6F){ // Turbocharger compressor inlet pressure +// } +// else if(pid == 0x70){ // Boost pressure control +// } +// else if(pid == 0x71){ // Variable Geometry turbo sensor +// } +// else if(pid == 0x72){ // Wastegate control +// } +// else if(pid == 0x73){ // Exhaust pressure +// } +// else if(pid == 0x74){ // Turbocharger RPM +// } +// else if(pid == 0x75){ // Turbocharger temperature +// } +// else if(pid == 0x76){ // Turbocharger temperature +// } +// else if(pid == 0x77){ // Charge air cooler temperature (CACT) +// } +// else if(pid == 0x78){ // Exhaust Gas Temperature (EGT) bank 1 +// } +// else if(pid == 0x79){ // Exhaust Gas Temperature (EGT) bank 2 +// } +// else if(pid == 0x7A){ // Diesel particulate filter (DPF) +// } +// else if(pid == 0x7B){ // Diesel particulate filter (DPF) +// } +// else if(pid == 0x7C){ // Diesel particulate filter (DPF) temperature +// } +// else if(pid == 0x7D){ // NOx NTE control area status +// } +// else if(pid == 0x7E){ // PM NTE control area status +// } +// else if(pid == 0x7F){ // Engine run time +// } + else if(pid == 0x80){ // Supported PIDs 81-A0 + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x01; + tx = true; + } +// else if(pid == 0x81){ // Engine run time for Auxiliary Emissions Control Device (AECD) +// } +// else if(pid == 0x82){ // Engine run time for Auxiliary Emissions Control Device (AECD) +// } +// else if(pid == 0x83){ // NOx sensor +// } +// else if(pid == 0x84){ // Manifold surface temperature +// } +// else if(pid == 0x85){ // NOx reqgent system +// } +// else if(pid == 0x86){ // Particulate Matter (PM) sensor +// } +// else if(pid == 0x87){ // Intake manifold absolute pressure +// } + else if(pid == 0xA0){ // Supported PIDs A1-C0 + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x01; + tx = true; + } + else if(pid == 0xC0){ // Supported PIDs C1-E0 + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x01; + tx = true; + } + else if(pid == 0xE0){ // Supported PIDs E1-FF? + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x00; + tx = true; + } + else{ + unsupported(mode, pid); + } + } + + //============================================================================= + // MODE $02 - Show freeze frame data + //============================================================================= + else if(mode == 0x02){ + unsupported(mode, pid); + } + + //============================================================================= + // MODE $03 - Show stored DTCs + //============================================================================= + else if(mode == 0x03){ + byte DTCs[] = {(0x40 | mode), 0x05, 0xC0, 0xBA, 0x00, 0x11, 0x80, 0x13, 0x90, 0x45, 0xA0, 0x31}; + iso_tp(mode, pid, 12, DTCs); + } + + //============================================================================= + // MODE $04 - Clear DTCs and stored values + //============================================================================= + else if(mode == 0x04){ + // Need to cleat DTCs. We just acknowledge the command for now. + txData[0] = 0x01; + tx = true; + } + + //============================================================================= + // MODE $05 - Test Results, oxygen sensor monitoring (non CAN only) + //============================================================================= + else if(mode == 0x05){ + unsupported(mode, pid); + } + + //============================================================================= + // MODE $06 - Test Results, On-Board Monitoring (Oxygen sensor monitoring for CAN only) + //============================================================================= + else if(mode == 0x06){ + if(pid == 0x00){ // Supported PIDs 01-20 + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x00; + tx = true; + } + else{ + unsupported(mode, pid); + } + } + + //============================================================================= + // MODE $07 - Show pending DTCs (Detected during current or last driving cycle) + //============================================================================= + else if(mode == 0x07){ + byte DTCs[] = {(0x40 | mode), 0x05, 0xC0, 0xBA, 0x00, 0x11, 0x80, 0x13, 0x90, 0x45, 0xA0, 0x31}; + iso_tp(mode, pid, 12, DTCs); + } + + //============================================================================= + // MODE $08 - Control operation of on-board component/system + //============================================================================= + else if(mode == 0x08){ + unsupported(mode, pid); + } + + //============================================================================= + // MODE $09 - Request vehcile information + //============================================================================= + else if(mode == 0x09){ + if(pid == 0x00){ // Supported PIDs 01-20 + txData[0] = 0x06; + + txData[3] = 0x54; + txData[4] = 0x40; + txData[5] = 0x00; + txData[6] = 0x00; + tx = true; + } +// else if(pid == 0x01){ // VIN message count for PID 02. (Only for ISO 9141-2, ISO 14230-4 and SAE J1850.) +// } + else if(pid == 0x02){ // VIN (17 to 20 Bytes) Uses ISO-TP + byte VIN[] = {(0x40 | mode), pid, 0x01, 0x31, 0x5a, 0x56, 0x42, 0x50, 0x38, 0x41, 0x4d, 0x37, 0x44, 0x35, 0x32, 0x32, 0x30, 0x31, 0x38, 0x31}; + iso_tp(mode, pid, 20, VIN); + } +// else if(pid == 0x03){ // Calibration ID message count for PID 04. (Only for ISO 9141-2, ISO 14230-4 and SAE J1850.) +// } + else if(pid == 0x04){ // Calibration ID + byte CID[] = {(0x40 | mode), pid, 0x01, 0x41, 0x72, 0x64, 0x75, 0x69, 0x6E, 0x6F, 0x20, 0x4F, 0x42, 0x44, 0x49, 0x49, 0x73, 0x69, 0x6D, 0x51, 0x52, 0x53, 0x54}; + iso_tp(mode, pid, 23, CID); + } +// else if(pid == 0x05){ // Calibration Verification Number (CVN) message count for PID 06. (Only for ISO 9141-2, ISO 14230-4 and SAE J1850.) +// } + else if(pid == 0x06){ // CVN + byte CVN[] = {(0x40 | mode), pid, 0x02, 0x11, 0x42, 0x42, 0x42, 0x22, 0x43, 0x43, 0x43}; + iso_tp(mode, pid, 11, CVN); + } +// else if(pid == 0x07){ // In-use performance tracking message count for PID 08 and 0B. (Only for ISO 9141-2, ISO 14230-4 and SAE J1850.) +// } +// else if(pid == 0x08){ // In-use performance tracking for spark ignition vehicles. +// } +// else if(pid == 0x09){ // ECU name message count for PID 0A. +// } + else if(pid == 0x0A){ // ECM Name + byte ECMname[] = {(0x40 | mode), pid, 0x01, 0x45, 0x43, 0x4D, 0x00, 0x2D, 0x41, 0x72, 0x64, 0x75, 0x69, 0x6E, 0x6F, 0x4F, 0x42, 0x44, 0x49, 0x49, 0x73, 0x69, 0x6D}; + iso_tp(mode, pid, 23, ECMname); + } +// else if(pid == 0x0B){ // In-use performance tracking for compression ignition vehicles. +// } +// else if(pid == 0x0C){ // ESN message count for PID 0D. +// } + else if(pid == 0x0D){ // ESN + byte ESN[] = {(0x40 | mode), pid, 0x01, 0x41, 0x72, 0x64, 0x75, 0x69, 0x6E, 0x6F, 0x2D, 0x4F, 0x42, 0x44, 0x49, 0x49, 0x73, 0x69, 0x6D, 0x00}; + iso_tp(mode, pid, 20, ESN); + } + else{ + unsupported(mode, pid); + } + } + + //============================================================================= + // MODE $0A - Show permanent DTCs + //============================================================================= + else if(mode == 0x0A){ + byte DTCs[] = {(0x40 | mode), 0x05, 0xC0, 0xBA, 0x00, 0x11, 0x80, 0x13, 0x90, 0x45, 0xA0, 0x31}; + iso_tp(mode, pid, 12, DTCs); + } + + // UDS Modes: Diagonstic and Communications Management ======================================= + //============================================================================= + // MODE $10 - Diagnostic Session Control + //============================================================================= +// else if(mode == 0x10){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $11 - ECU Reset + //============================================================================= +// else if(mode == 0x11){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// tx = true; +// } + + //============================================================================= + // MODE $27 - Security Access + //============================================================================= +// else if(mode == 0x27){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $28 - Communication Control + //============================================================================= +// else if(mode == 0x28){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $3E - Tester Present + //============================================================================= +// else if(mode == 0x3E){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $83 - Access Timing Parameters + //============================================================================= +// else if(mode == 0x83){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $84 - Secured Data Transmission + //============================================================================= +// else if(mode == 0x84){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } +// + //============================================================================= + // MODE $85 - Control DTC Sentings + //============================================================================= +// else if(mode == 0x85){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $86 - Response On Event + //============================================================================= +// else if(mode == 0x86){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $87 - Link Control + //============================================================================= +// else if(mode == 0x87){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + // UDS Modes: Data Transmission ============================================================== + //============================================================================= + // MODE $22 - Read Data By Identifier + //============================================================================= +// else if(mode == 0x22){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $23 - Read Memory By Address + //============================================================================= +// else if(mode == 0x23){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// tx = true; +// } + + //============================================================================= + // MODE $24 - Read Scaling Data By Identifier + //============================================================================= +// else if(mode == 0x24){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $2A - Read Data By Periodic Identifier + //============================================================================= +// else if(mode == 0x2A){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $2C - Dynamically Define Data Identifier + //============================================================================= +// else if(mode == 0x2C){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $2E - Write Data By Identifier + //============================================================================= +// else if(mode == 0x2E){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $3D - Write Memory By Address + //============================================================================= +// else if(mode == 0x3D){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + // UDS Modes: Stored Data Transmission ======================================================= + //============================================================================= + // MODE $14 - Clear Diagnostic Information + //============================================================================= +// else if(mode == 0x14){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $19 - Read DTC Information + //============================================================================= +// else if(mode == 0x19){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// tx = true; +// } + + // UDS Modes: Input Output Control =========================================================== + //============================================================================= + // MODE $2F - Input Output Control By Identifier + //============================================================================= +// else if(mode == 0x2F){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + // UDS Modes: Remote Activation of Routine =================================================== + //============================================================================= + // MODE $31 - Routine Control + //============================================================================= +// else if(mode == 0x2F){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + // UDS Modes: Upload / Download ============================================================== + //============================================================================= + // MODE $34 - Request Download + //============================================================================= +// else if(mode == 0x34){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $35 - Request Upload + //============================================================================= +// else if(mode == 0x35){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// tx = true; +// } + + //============================================================================= + // MODE $36 - Transfer Data + //============================================================================= +// else if(mode == 0x36){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $37 - Request Transfer Exit + //============================================================================= +// else if(mode == 0x37){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $38 - Request File Transfer + //============================================================================= +// else if(mode == 0x38){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + else { + unsupported(mode, pid); + } + + if(tx) + CAN0.sendMsgBuf(REPLY_ID, 8, txData); +} + + +// Generic debug serial output +void unsupported(byte mode, byte pid){ + negAck(mode, 0x12); + unsupportedPrint(mode, pid); +} + + +// Generic debug serial output +void negAck(byte mode, byte reason){ + byte txData[] = {0x03,0x7F,mode,reason,PAD,PAD,PAD,PAD}; + CAN0.sendMsgBuf(REPLY_ID, 8, txData); +} + + +// Generic debug serial output +void unsupportedPrint(byte mode, byte pid){ + char msgstring[64]; + sprintf(msgstring, "Mode $%02X: Unsupported PID $%02X requested!", mode, pid); + Serial.println(msgstring); +} + + +// Blocking example of ISO transport +void iso_tp(byte mode, byte pid, int len, byte *data){ + byte tpData[8]; + int offset = 0; + byte index = 0; +// byte packetcnt = ((len & 0x0FFF) - 6) / 7; +// if((((len & 0x0FFF) - 6) % 7) > 0) +// packetcnt++; + + // First frame + tpData[0] = 0x10 | ((len >> 8) & 0x0F); + tpData[1] = 0x00FF & len; + for(byte i=2; i<8; i++){ + tpData[i] = data[offset++]; + } + CAN0.sendMsgBuf(REPLY_ID, 8, tpData); + index++; // We sent a packet so increase our index. + + bool not_done = true; + unsigned long sepPrev = millis(); + byte sepInvl = 0; + byte frames = 0; + bool lockout = false; + while(not_done){ + // Need to wait for flow frame + if(!digitalRead(CAN0_INT)){ + CAN0.readMsgBuf(&rxId, &dlc, rxBuf); + + if((rxId == LISTEN_ID) && ((rxBuf[0] & 0xF0) == 0x30)){ + if((rxBuf[0] & 0x0F) == 0x00){ + // Continue + frames = rxBuf[1]; + sepInvl = rxBuf[2]; + lockout = true; + } else if((rxBuf[0] & 0x0F) == 0x01){ + // Wait + lockout = false; + delay(rxBuf[2]); + } else if((rxBuf[0] & 0x0F) == 0x03){ + // Abort + not_done = false; + return; + } + } + } + + if(((millis() - sepPrev) >= sepInvl) && lockout){ + sepPrev = millis(); + + tpData[0] = 0x20 | index++; + for(byte i=1; i<8; i++){ + if(offset != len) + tpData[i] = data[offset++]; + else + tpData[i] = 0x00; + } + + // Do consecutive frames as instructed via flow frame + CAN0.sendMsgBuf(REPLY_ID, 8, tpData); + + if(frames-- == 1) + lockout = false; + + } + + if(offset == len) + not_done = false; + else{ + char msgstring[32]; + sprintf(msgstring,"Offset: 0x%04X\tLen: 0x%04X", offset, len); + Serial.println(msgstring); + } + + + // Timeout + if((millis() - sepPrev) >= 1000) + not_done = false; + } + +} + + +/********************************************************************************************************* + END FILE +*********************************************************************************************************/ diff --git a/hal/transport/CAN/driver/examples/Standard_MaskFilter/Standard_MaskFilter.ino b/hal/transport/CAN/driver/examples/Standard_MaskFilter/Standard_MaskFilter.ino new file mode 100644 index 000000000..bfd693ce8 --- /dev/null +++ b/hal/transport/CAN/driver/examples/Standard_MaskFilter/Standard_MaskFilter.ino @@ -0,0 +1,94 @@ + +// MCP2515 Mask and Filter example for standard CAN message frames. +// Written by Cory J. Fowler (20140717) + +/*********************************************************************************** +If you send the following standard IDs below to an Arduino loaded with this sketch +you will find that 0x102 and 0x105 will not get in. + +ID in Hex - Two Data Bytes! - Filter/Mask in HEX + 0x100 + 0000 0000 0000 0000 = 0x01000000 + 0x101 + 0000 0000 0000 0000 = 0x01010000 + 0x102 + 0000 0000 0000 0000 = 0x01020000 This example will NOT be receiving this ID + 0x103 + 0000 0000 0000 0000 = 0x01030000 + 0x104 + 0000 0000 0000 0000 = 0x01040000 + 0x105 + 0000 0000 0000 0000 = 0x01050000 This example will NOT be receiving this ID + 0x106 + 0000 0000 0000 0000 = 0x01060000 + 0x107 + 0000 0000 0000 0000 = 0x01070000 + + This mask will check the filters against ID bit 8 and ID bits 3-0. + MASK + 0000 0000 0000 0000 = 0x010F0000 + + If there is an explicit filter match to those bits, the message will be passed to the + receive buffer and the interrupt pin will be set. + This example will NOT be exclusive to ONLY the above frame IDs, for that a mask such + as the below would be used: + MASK + 0000 0000 0000 0000 = 0x07FF0000 + + This mask will check the filters against all ID bits and the first data byte: + MASK + 1111 1111 0000 0000 = 0x07FFFF00 + If you use this mask and do not touch the filters below, you will find that your first + data byte must be 0x00 for the message to enter the receive buffer. + + At the moment, to disable a filter or mask, copy the value of a used filter or mask. + + Data bytes are ONLY checked when the MCP2515 is in 'MCP_STDEXT' mode via the begin + function, otherwise ('MCP_STD') only the ID is checked. +***********************************************************************************/ + + +#include +#include + +long unsigned int rxId; +unsigned char len = 0; +unsigned char rxBuf[8]; + +MCP_CAN CAN0(10); // Set CS to pin 10 + +void setup() +{ + Serial.begin(115200); + if(CAN0.begin(MCP_STDEXT, CAN_500KBPS, MCP_16MHZ) == CAN_OK) Serial.print("MCP2515 Init Okay!!\r\n"); + else Serial.print("MCP2515 Init Failed!!\r\n"); + pinMode(2, INPUT); // Setting pin 2 for /INT input + + + CAN0.init_Mask(0,0,0x010F0000); // Init first mask... + CAN0.init_Filt(0,0,0x01000000); // Init first filter... + CAN0.init_Filt(1,0,0x01010000); // Init second filter... + + CAN0.init_Mask(1,0,0x010F0000); // Init second mask... + CAN0.init_Filt(2,0,0x01030000); // Init third filter... + CAN0.init_Filt(3,0,0x01040000); // Init fouth filter... + CAN0.init_Filt(4,0,0x01060000); // Init fifth filter... + CAN0.init_Filt(5,0,0x01070000); // Init sixth filter... + + Serial.println("MCP2515 Library Mask & Filter Example..."); + CAN0.setMode(MCP_NORMAL); // Change to normal mode to allow messages to be transmitted +} + +void loop() +{ + if(!digitalRead(2)) // If pin 2 is low, read receive buffer + { + CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) + Serial.print("ID: "); + Serial.print(rxId, HEX); + Serial.print(" Data: "); + for(int i = 0; i TXBnD7 */ + a1 = MCP_TXB0CTRL; + a2 = MCP_TXB1CTRL; + a3 = MCP_TXB2CTRL; + for (i = 0; i < 14; + i++) { /* in-buffer loop */ + mcp2515_setRegister(a1, 0); + mcp2515_setRegister(a2, 0); + mcp2515_setRegister(a3, 0); + a1++; + a2++; + a3++; + } + mcp2515_setRegister(MCP_RXB0CTRL, 0); + mcp2515_setRegister(MCP_RXB1CTRL, 0); +} + +/********************************************************************************************************* +** Function name: mcp2515_init +** Descriptions: Initialize the controller +*********************************************************************************************************/ +INT8U MCP_CAN::mcp2515_init(const INT8U canIDMode, const INT8U canSpeed, const INT8U canClock) +{ + + INT8U res; + + mcp2515_reset(); + + mcpMode = MCP_LOOPBACK; + + res = mcp2515_setCANCTRL_Mode(MODE_CONFIG); + if (res > 0) { +#if DEBUG_MODE + Serial.print("Entering Configuration Mode Failure...\r\n"); +#endif + return res; + } +#if DEBUG_MODE + Serial.print("Entering Configuration Mode Successful!\r\n"); +#endif + + // Set Baudrate + if (mcp2515_configRate(canSpeed, canClock)) { +#if DEBUG_MODE + Serial.print("Setting Baudrate Failure...\r\n"); +#endif + return res; + } +#if DEBUG_MODE + Serial.print("Setting Baudrate Successful!\r\n"); +#endif + + if (res == MCP2515_OK) { + + /* init canbuffers */ + mcp2515_initCANBuffers(); + + /* interrupt mode */ + mcp2515_setRegister(MCP_CANINTE, MCP_RX0IF | MCP_RX1IF); + + //Sets BF pins as GPO + mcp2515_setRegister(MCP_BFPCTRL, MCP_BxBFS_MASK | MCP_BxBFE_MASK); + //Sets RTS pins as GPI + mcp2515_setRegister(MCP_TXRTSCTRL, 0x00); + + switch (canIDMode) { + case (MCP_ANY): + mcp2515_modifyRegister(MCP_RXB0CTRL, + MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK, + MCP_RXB_RX_ANY | MCP_RXB_BUKT_MASK); + mcp2515_modifyRegister(MCP_RXB1CTRL, MCP_RXB_RX_MASK, + MCP_RXB_RX_ANY); + break; + /* The followingn two functions of the MCP2515 do not work, there is a bug in the silicon. + case (MCP_STD): + mcp2515_modifyRegister(MCP_RXB0CTRL, + MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK, + MCP_RXB_RX_STD | MCP_RXB_BUKT_MASK ); + mcp2515_modifyRegister(MCP_RXB1CTRL, MCP_RXB_RX_MASK, + MCP_RXB_RX_STD); + break; + + case (MCP_EXT): + mcp2515_modifyRegister(MCP_RXB0CTRL, + MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK, + MCP_RXB_RX_EXT | MCP_RXB_BUKT_MASK ); + mcp2515_modifyRegister(MCP_RXB1CTRL, MCP_RXB_RX_MASK, + MCP_RXB_RX_EXT); + break; + */ + case (MCP_STDEXT): + mcp2515_modifyRegister(MCP_RXB0CTRL, + MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK, + MCP_RXB_RX_STDEXT | MCP_RXB_BUKT_MASK); + mcp2515_modifyRegister(MCP_RXB1CTRL, MCP_RXB_RX_MASK, + MCP_RXB_RX_STDEXT); + break; + + default: +#if DEBUG_MODE + Serial.print("`Setting ID Mode Failure...\r\n"); +#endif + return MCP2515_FAIL; + break; + } + + + res = mcp2515_setCANCTRL_Mode(mcpMode); + if (res) { +#if DEBUG_MODE + Serial.print("Returning to Previous Mode Failure...\r\n"); +#endif + return res; + } + + } + return res; + +} + +/********************************************************************************************************* +** Function name: mcp2515_write_id +** Descriptions: Write CAN ID +*********************************************************************************************************/ +void MCP_CAN::mcp2515_write_id( const INT8U mcp_addr, const INT8U ext, const INT32U id ) +{ + uint16_t canid; + INT8U tbufdata[4]; + + canid = (uint16_t) (id & 0x0FFFF); + + if (ext == 1) { + tbufdata[MCP_EID0] = (INT8U) (canid & 0xFF); + tbufdata[MCP_EID8] = (INT8U) (canid >> 8); + canid = (uint16_t) (id >> 16); + tbufdata[MCP_SIDL] = (INT8U) (canid & 0x03); + tbufdata[MCP_SIDL] += (INT8U) ((canid & 0x1C) << 3); + tbufdata[MCP_SIDL] |= MCP_TXB_EXIDE_M; + tbufdata[MCP_SIDH] = (INT8U) (canid >> 5); + } else { + tbufdata[MCP_SIDH] = (INT8U) (canid >> 3); + tbufdata[MCP_SIDL] = (INT8U) ((canid & 0x07) << 5); + tbufdata[MCP_EID0] = 0; + tbufdata[MCP_EID8] = 0; + } + + mcp2515_setRegisterS(mcp_addr, tbufdata, 4); +} + +/********************************************************************************************************* +** Function name: mcp2515_write_mf +** Descriptions: Write Masks and Filters +*********************************************************************************************************/ +void MCP_CAN::mcp2515_write_mf( const INT8U mcp_addr, const INT8U ext, const INT32U id ) +{ + uint16_t canid; + INT8U tbufdata[4]; + + canid = (uint16_t) (id & 0x0FFFF); + + if (ext == 1) { + tbufdata[MCP_EID0] = (INT8U) (canid & 0xFF); + tbufdata[MCP_EID8] = (INT8U) (canid >> 8); + canid = (uint16_t) (id >> 16); + tbufdata[MCP_SIDL] = (INT8U) (canid & 0x03); + tbufdata[MCP_SIDL] += (INT8U) ((canid & 0x1C) << 3); + tbufdata[MCP_SIDL] |= MCP_TXB_EXIDE_M; + tbufdata[MCP_SIDH] = (INT8U) (canid >> 5); + } else { + tbufdata[MCP_EID0] = (INT8U) (canid & 0xFF); + tbufdata[MCP_EID8] = (INT8U) (canid >> 8); + canid = (uint16_t) (id >> 16); + tbufdata[MCP_SIDL] = (INT8U) ((canid & 0x07) << 5); + tbufdata[MCP_SIDH] = (INT8U) (canid >> 3); + } + + mcp2515_setRegisterS(mcp_addr, tbufdata, 4); +} + +/********************************************************************************************************* +** Function name: mcp2515_read_id +** Descriptions: Read CAN ID +*********************************************************************************************************/ +void MCP_CAN::mcp2515_read_id( const INT8U mcp_addr, INT8U* ext, INT32U* id ) +{ + INT8U tbufdata[4]; + + *ext = 0; + *id = 0; + + mcp2515_readRegisterS(mcp_addr, tbufdata, 4); + + *id = (tbufdata[MCP_SIDH] << 3) + (tbufdata[MCP_SIDL] >> 5); + + if ((tbufdata[MCP_SIDL] & MCP_TXB_EXIDE_M) == MCP_TXB_EXIDE_M) { + /* extended id */ + *id = (*id << 2) + (tbufdata[MCP_SIDL] & 0x03); + *id = (*id << 8) + tbufdata[MCP_EID8]; + *id = (*id << 8) + tbufdata[MCP_EID0]; + *ext = 1; + } +} + +/********************************************************************************************************* +** Function name: mcp2515_write_canMsg +** Descriptions: Write message +*********************************************************************************************************/ +void MCP_CAN::mcp2515_write_canMsg( const INT8U buffer_sidh_addr) +{ + INT8U mcp_addr; + mcp_addr = buffer_sidh_addr; + mcp2515_setRegisterS(mcp_addr + 5, m_nDta, + m_nDlc); /* write data bytes */ + + if (m_nRtr == + 1) { /* if RTR set bit in byte */ + m_nDlc |= MCP_RTR_MASK; + } + + mcp2515_setRegister((mcp_addr + 4), + m_nDlc); /* write the RTR and DLC */ + mcp2515_write_id(mcp_addr, m_nExtFlg, + m_nID); /* write CAN id */ + +} + +/********************************************************************************************************* +** Function name: mcp2515_read_canMsg +** Descriptions: Read message +*********************************************************************************************************/ +void MCP_CAN::mcp2515_read_canMsg(const INT8U + buffer_sidh_addr) /* read can msg */ +{ + INT8U mcp_addr, ctrl; + + mcp_addr = buffer_sidh_addr; + + mcp2515_read_id(mcp_addr, &m_nExtFlg, &m_nID); + + ctrl = mcp2515_readRegister(mcp_addr - 1); + m_nDlc = mcp2515_readRegister(mcp_addr + 4); + + if (ctrl & 0x08) { + m_nRtr = 1; + } else { + m_nRtr = 0; + } + + m_nDlc &= MCP_DLC_MASK; + mcp2515_readRegisterS(mcp_addr + 5, &(m_nDta[0]), m_nDlc); +} + +/********************************************************************************************************* +** Function name: mcp2515_getNextFreeTXBuf +** Descriptions: Send message +*********************************************************************************************************/ +INT8U MCP_CAN::mcp2515_getNextFreeTXBuf(INT8U + *txbuf_n) /* get Next free txbuf */ +{ + INT8U res, i, ctrlval; + INT8U ctrlregs[MCP_N_TXBUFFERS] = {MCP_TXB0CTRL, MCP_TXB1CTRL, MCP_TXB2CTRL}; + + res = MCP_ALLTXBUSY; + *txbuf_n = 0x00; + + /* check all 3 TX-Buffers */ + for (i = 0; i < MCP_N_TXBUFFERS; i++) { + ctrlval = mcp2515_readRegister(ctrlregs[i]); + if ((ctrlval & MCP_TXB_TXREQ_M) == 0) { + *txbuf_n = ctrlregs[i] + 1; /* return SIDH-address of Buffer*/ + + res = MCP2515_OK; + return res; /* ! function exit */ + } + } + return res; +} + +/********************************************************************************************************* +** Function name: MCP_CAN +** Descriptions: Public function to declare CAN class and the /CS pin. +*********************************************************************************************************/ +MCP_CAN::MCP_CAN(INT8U _CS) +{ + MCPCS = _CS; + MCP2515_UNSELECT(); + pinMode(MCPCS, OUTPUT); +} + +/********************************************************************************************************* +** Function name: begin +** Descriptions: Public function to declare controller initialization parameters. +*********************************************************************************************************/ +INT8U MCP_CAN::begin(INT8U idmodeset, INT8U speedset, INT8U clockset) +{ + INT8U res; + + SPI.begin(); + res = mcp2515_init(idmodeset, speedset, clockset); + if (res == MCP2515_OK) { + return CAN_OK; + } + + return CAN_FAILINIT; +} + +/********************************************************************************************************* +** Function name: init_Mask +** Descriptions: Public function to set mask(s). +*********************************************************************************************************/ +INT8U MCP_CAN::init_Mask(INT8U num, INT8U ext, INT32U ulData) +{ + INT8U res = MCP2515_OK; +#if DEBUG_MODE + Serial.print("Starting to Set Mask!\r\n"); +#endif + res = mcp2515_setCANCTRL_Mode(MODE_CONFIG); + if (res > 0) { +#if DEBUG_MODE + Serial.print("Entering Configuration Mode Failure...\r\n"); +#endif + return res; + } + + if (num == 0) { + mcp2515_write_mf(MCP_RXM0SIDH, ext, ulData); + + } else if (num == 1) { + mcp2515_write_mf(MCP_RXM1SIDH, ext, ulData); + } else { + res = MCP2515_FAIL; + } + + res = mcp2515_setCANCTRL_Mode(mcpMode); + if (res > 0) { +#if DEBUG_MODE + Serial.print("Entering Previous Mode Failure...\r\nSetting Mask Failure...\r\n"); +#endif + return res; + } +#if DEBUG_MODE + Serial.print("Setting Mask Successful!\r\n"); +#endif + return res; +} + +/********************************************************************************************************* +** Function name: init_Mask +** Descriptions: Public function to set mask(s). +*********************************************************************************************************/ +INT8U MCP_CAN::init_Mask(INT8U num, INT32U ulData) +{ + INT8U res = MCP2515_OK; + INT8U ext = 0; +#if DEBUG_MODE + Serial.print("Starting to Set Mask!\r\n"); +#endif + res = mcp2515_setCANCTRL_Mode(MODE_CONFIG); + if (res > 0) { +#if DEBUG_MODE + Serial.print("Entering Configuration Mode Failure...\r\n"); +#endif + return res; + } + + if ((ulData & 0x80000000) == 0x80000000) { + ext = 1; + } + + if (num == 0) { + mcp2515_write_mf(MCP_RXM0SIDH, ext, ulData); + + } else if (num == 1) { + mcp2515_write_mf(MCP_RXM1SIDH, ext, ulData); + } else { + res = MCP2515_FAIL; + } + + res = mcp2515_setCANCTRL_Mode(mcpMode); + if (res > 0) { +#if DEBUG_MODE + Serial.print("Entering Previous Mode Failure...\r\nSetting Mask Failure...\r\n"); +#endif + return res; + } +#if DEBUG_MODE + Serial.print("Setting Mask Successful!\r\n"); +#endif + return res; +} + +/********************************************************************************************************* +** Function name: init_Filt +** Descriptions: Public function to set filter(s). +*********************************************************************************************************/ +INT8U MCP_CAN::init_Filt(INT8U num, INT8U ext, INT32U ulData) +{ + INT8U res = MCP2515_OK; +#if DEBUG_MODE + Serial.print("Starting to Set Filter!\r\n"); +#endif + res = mcp2515_setCANCTRL_Mode(MODE_CONFIG); + if (res > 0) { +#if DEBUG_MODE + Serial.print("Enter Configuration Mode Failure...\r\n"); +#endif + return res; + } + + switch (num) { + case 0: + mcp2515_write_mf(MCP_RXF0SIDH, ext, ulData); + break; + + case 1: + mcp2515_write_mf(MCP_RXF1SIDH, ext, ulData); + break; + + case 2: + mcp2515_write_mf(MCP_RXF2SIDH, ext, ulData); + break; + + case 3: + mcp2515_write_mf(MCP_RXF3SIDH, ext, ulData); + break; + + case 4: + mcp2515_write_mf(MCP_RXF4SIDH, ext, ulData); + break; + + case 5: + mcp2515_write_mf(MCP_RXF5SIDH, ext, ulData); + break; + + default: + res = MCP2515_FAIL; + } + + res = mcp2515_setCANCTRL_Mode(mcpMode); + if (res > 0) { +#if DEBUG_MODE + Serial.print("Entering Previous Mode Failure...\r\nSetting Filter Failure...\r\n"); +#endif + return res; + } +#if DEBUG_MODE + Serial.print("Setting Filter Successfull!\r\n"); +#endif + + return res; +} + +/********************************************************************************************************* +** Function name: init_Filt +** Descriptions: Public function to set filter(s). +*********************************************************************************************************/ +INT8U MCP_CAN::init_Filt(INT8U num, INT32U ulData) +{ + INT8U res = MCP2515_OK; + INT8U ext = 0; + +#if DEBUG_MODE + Serial.print("Starting to Set Filter!\r\n"); +#endif + res = mcp2515_setCANCTRL_Mode(MODE_CONFIG); + if (res > 0) { +#if DEBUG_MODE + Serial.print("Enter Configuration Mode Failure...\r\n"); +#endif + return res; + } + + if ((ulData & 0x80000000) == 0x80000000) { + ext = 1; + } + + switch (num) { + case 0: + mcp2515_write_mf(MCP_RXF0SIDH, ext, ulData); + break; + + case 1: + mcp2515_write_mf(MCP_RXF1SIDH, ext, ulData); + break; + + case 2: + mcp2515_write_mf(MCP_RXF2SIDH, ext, ulData); + break; + + case 3: + mcp2515_write_mf(MCP_RXF3SIDH, ext, ulData); + break; + + case 4: + mcp2515_write_mf(MCP_RXF4SIDH, ext, ulData); + break; + + case 5: + mcp2515_write_mf(MCP_RXF5SIDH, ext, ulData); + break; + + default: + res = MCP2515_FAIL; + } + + res = mcp2515_setCANCTRL_Mode(mcpMode); + if (res > 0) { +#if DEBUG_MODE + Serial.print("Entering Previous Mode Failure...\r\nSetting Filter Failure...\r\n"); +#endif + return res; + } +#if DEBUG_MODE + Serial.print("Setting Filter Successfull!\r\n"); +#endif + + return res; +} + +/********************************************************************************************************* +** Function name: setMsg +** Descriptions: Set can message, such as dlc, id, dta[] and so on +*********************************************************************************************************/ +INT8U MCP_CAN::setMsg(INT32U id, INT8U rtr, INT8U ext, INT8U len, INT8U *pData) +{ + int i = 0; + m_nID = id; + m_nRtr = rtr; + m_nExtFlg = ext; + m_nDlc = len; + for (i = 0; i < MAX_CHAR_IN_MESSAGE; i++) { + m_nDta[i] = *(pData + i); + } + + return MCP2515_OK; +} + +/********************************************************************************************************* +** Function name: clearMsg +** Descriptions: Set all messages to zero +*********************************************************************************************************/ +INT8U MCP_CAN::clearMsg() +{ + m_nID = 0; + m_nDlc = 0; + m_nExtFlg = 0; + m_nRtr = 0; + m_nfilhit = 0; + for (int i = 0; i < m_nDlc; i++) { + m_nDta[i] = 0x00; + } + + return MCP2515_OK; +} + +/********************************************************************************************************* +** Function name: sendMsg +** Descriptions: Send message +*********************************************************************************************************/ +INT8U MCP_CAN::sendMsg() +{ + INT8U res, res1, txbuf_n; + uint16_t uiTimeOut = 0; + + do { + res = mcp2515_getNextFreeTXBuf(&txbuf_n); /* info = addr. */ + uiTimeOut++; + } while (res == MCP_ALLTXBUSY && (uiTimeOut < TIMEOUTVALUE)); + + if (uiTimeOut == TIMEOUTVALUE) { + return CAN_GETTXBFTIMEOUT; /* get tx buff time out */ + } + uiTimeOut = 0; + mcp2515_write_canMsg(txbuf_n); + mcp2515_modifyRegister(txbuf_n - 1, MCP_TXB_TXREQ_M, MCP_TXB_TXREQ_M); + + do { + uiTimeOut++; + res1 = mcp2515_readRegister(txbuf_n - 1); /* read send buff ctrl reg */ + res1 = res1 & 0x08; + } while (res1 && (uiTimeOut < TIMEOUTVALUE)); + + if (uiTimeOut == + TIMEOUTVALUE) { /* send msg timeout */ + return CAN_SENDMSGTIMEOUT; + } + + return CAN_OK; +} + +/********************************************************************************************************* +** Function name: sendMsgBuf +** Descriptions: Send message to transmitt buffer +*********************************************************************************************************/ +INT8U MCP_CAN::sendMsgBuf(INT32U id, INT8U ext, INT8U len, INT8U *buf) +{ + INT8U res; + + setMsg(id, 0, ext, len, buf); + res = sendMsg(); + + return res; +} + +/********************************************************************************************************* +** Function name: sendMsgBuf +** Descriptions: Send message to transmitt buffer +*********************************************************************************************************/ +INT8U MCP_CAN::sendMsgBuf(INT32U id, INT8U len, INT8U *buf) +{ + INT8U ext = 0, rtr = 0; + INT8U res; + + if ((id & 0x80000000) == 0x80000000) { + ext = 1; + } + + if ((id & 0x40000000) == 0x40000000) { + rtr = 1; + } + + setMsg(id, rtr, ext, len, buf); + res = sendMsg(); + + return res; +} + +/********************************************************************************************************* +** Function name: readMsg +** Descriptions: Read message +*********************************************************************************************************/ +INT8U MCP_CAN::readMsg() +{ + INT8U stat, res; + + stat = mcp2515_readStatus(); + + if (stat & + MCP_STAT_RX0IF) { /* Msg in Buffer 0 */ + mcp2515_read_canMsg(MCP_RXBUF_0); + mcp2515_modifyRegister(MCP_CANINTF, MCP_RX0IF, 0); + res = CAN_OK; + } else if (stat & + MCP_STAT_RX1IF) { /* Msg in Buffer 1 */ + mcp2515_read_canMsg(MCP_RXBUF_1); + mcp2515_modifyRegister(MCP_CANINTF, MCP_RX1IF, 0); + res = CAN_OK; + } else { + res = CAN_NOMSG; + } + + return res; +} + +/********************************************************************************************************* +** Function name: readMsgBuf +** Descriptions: Public function, Reads message from receive buffer. +*********************************************************************************************************/ +INT8U MCP_CAN::readMsgBuf(INT32U *id, INT8U *ext, INT8U *len, INT8U buf[]) +{ + if (readMsg() == CAN_NOMSG) { + return CAN_NOMSG; + } + + *id = m_nID; + *len = m_nDlc; + *ext = m_nExtFlg; + for (int i = 0; i < m_nDlc; i++) { + buf[i] = m_nDta[i]; + } + + return CAN_OK; +} + +/********************************************************************************************************* +** Function name: readMsgBuf +** Descriptions: Public function, Reads message from receive buffer. +*********************************************************************************************************/ +INT8U MCP_CAN::readMsgBuf(INT32U *id, INT8U *len, INT8U buf[]) +{ + if (readMsg() == CAN_NOMSG) { + return CAN_NOMSG; + } + + if (m_nExtFlg) { + m_nID |= 0x80000000; + } + + if (m_nRtr) { + m_nID |= 0x40000000; + } + + *id = m_nID; + *len = m_nDlc; + + for (int i = 0; i < m_nDlc; i++) { + buf[i] = m_nDta[i]; + } + + return CAN_OK; +} + +/********************************************************************************************************* +** Function name: checkReceive +** Descriptions: Public function, Checks for received data. (Used if not using the interrupt output) +*********************************************************************************************************/ +INT8U MCP_CAN::checkReceive(void) +{ + INT8U res; + res = mcp2515_readStatus(); /* RXnIF in Bit 1 and 0 */ + if (res & MCP_STAT_RXIF_MASK) { + return CAN_MSGAVAIL; + } else { + return CAN_NOMSG; + } +} + +/********************************************************************************************************* +** Function name: checkError +** Descriptions: Public function, Returns error register data. +*********************************************************************************************************/ +INT8U MCP_CAN::checkError(void) +{ + INT8U eflg = mcp2515_readRegister(MCP_EFLG); + + if (eflg & MCP_EFLG_ERRORMASK) { + return CAN_CTRLERROR; + } else { + return CAN_OK; + } +} + +/********************************************************************************************************* +** Function name: getError +** Descriptions: Returns error register value. +*********************************************************************************************************/ +INT8U MCP_CAN::getError(void) +{ + return mcp2515_readRegister(MCP_EFLG); +} + +/********************************************************************************************************* +** Function name: mcp2515_errorCountRX +** Descriptions: Returns REC register value +*********************************************************************************************************/ +INT8U MCP_CAN::errorCountRX(void) +{ + return mcp2515_readRegister(MCP_REC); +} + +/********************************************************************************************************* +** Function name: mcp2515_errorCountTX +** Descriptions: Returns TEC register value +*********************************************************************************************************/ +INT8U MCP_CAN::errorCountTX(void) +{ + return mcp2515_readRegister(MCP_TEC); +} + +/********************************************************************************************************* +** Function name: mcp2515_enOneShotTX +** Descriptions: Enables one shot transmission mode +*********************************************************************************************************/ +INT8U MCP_CAN::enOneShotTX(void) +{ + mcp2515_modifyRegister(MCP_CANCTRL, MODE_ONESHOT, MODE_ONESHOT); + if ((mcp2515_readRegister(MCP_CANCTRL) & MODE_ONESHOT) != MODE_ONESHOT) { + return CAN_FAIL; + } else { + return CAN_OK; + } +} + +/********************************************************************************************************* +** Function name: mcp2515_disOneShotTX +** Descriptions: Disables one shot transmission mode +*********************************************************************************************************/ +INT8U MCP_CAN::disOneShotTX(void) +{ + mcp2515_modifyRegister(MCP_CANCTRL, MODE_ONESHOT, 0); + if ((mcp2515_readRegister(MCP_CANCTRL) & MODE_ONESHOT) != 0) { + return CAN_FAIL; + } else { + return CAN_OK; + } +} + +/********************************************************************************************************* +** Function name: mcp2515_abortTX +** Descriptions: Aborts any queued transmissions +*********************************************************************************************************/ +INT8U MCP_CAN::abortTX(void) +{ + mcp2515_modifyRegister(MCP_CANCTRL, ABORT_TX, ABORT_TX); + + // Maybe check to see if the TX buffer transmission request bits are cleared instead? + if ((mcp2515_readRegister(MCP_CANCTRL) & ABORT_TX) != ABORT_TX) { + return CAN_FAIL; + } else { + return CAN_OK; + } +} + +/********************************************************************************************************* +** Function name: setGPO +** Descriptions: Public function, Checks for r +*********************************************************************************************************/ +INT8U MCP_CAN::setGPO(INT8U data) +{ + mcp2515_modifyRegister(MCP_BFPCTRL, MCP_BxBFS_MASK, (data << 4)); + + return 0; +} + +/********************************************************************************************************* +** Function name: getGPI +** Descriptions: Public function, Checks for r +*********************************************************************************************************/ +INT8U MCP_CAN::getGPI(void) +{ + INT8U res; + res = mcp2515_readRegister(MCP_TXRTSCTRL) & MCP_BxRTS_MASK; + return (res >> 3); +} + +/********************************************************************************************************* + END FILE +*********************************************************************************************************/ diff --git a/hal/transport/CAN/driver/mcp_can.h b/hal/transport/CAN/driver/mcp_can.h new file mode 100644 index 000000000..adb2c2ed2 --- /dev/null +++ b/hal/transport/CAN/driver/mcp_can.h @@ -0,0 +1,142 @@ +/* + mcp_can.h + 2012 Copyright (c) Seeed Technology Inc. All right reserved. + 2017 Copyright (c) Cory J. Fowler All Rights Reserved. + + Author:Loovee + Contributor: Cory J. Fowler + 2017-09-25 + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110- + 1301 USA +*/ +#ifndef _MCP2515_H_ +#define _MCP2515_H_ + +#include "mcp_can_dfs.h" +#define MAX_CHAR_IN_MESSAGE 8 + +class MCP_CAN +{ +private: + + INT8U m_nExtFlg; // Identifier Type + // Extended (29 bit) or Standard (11 bit) + INT32U m_nID; // CAN ID + INT8U m_nDlc; // Data Length Code + INT8U m_nDta[MAX_CHAR_IN_MESSAGE]; // Data array + INT8U m_nRtr; // Remote request flag + INT8U m_nfilhit; // The number of the filter that matched the message + INT8U MCPCS; // Chip Select pin number + INT8U mcpMode; // Mode to return to after configurations are performed. + + + /********************************************************************************************************* + * mcp2515 driver function + *********************************************************************************************************/ + // private: +private: + + void mcp2515_reset(void); // Soft Reset MCP2515 + + INT8U mcp2515_readRegister(const INT8U address); // Read MCP2515 register + + void mcp2515_readRegisterS(const INT8U + address, // Read MCP2515 successive registers + INT8U values[], + const INT8U n); + + void mcp2515_setRegister(const INT8U address, // Set MCP2515 register + const INT8U value); + + void mcp2515_setRegisterS(const INT8U + address, // Set MCP2515 successive registers + const INT8U values[], + const INT8U n); + + void mcp2515_initCANBuffers(void); + + void mcp2515_modifyRegister(const INT8U + address, // Set specific bit(s) of a register + const INT8U mask, + const INT8U data); + + INT8U mcp2515_readStatus(void); // Read MCP2515 Status + INT8U mcp2515_setCANCTRL_Mode(const INT8U newmode); // Set mode + INT8U mcp2515_configRate(const INT8U canSpeed, // Set baud rate + const INT8U canClock); + + INT8U mcp2515_init(const INT8U canIDMode, // Initialize Controller + const INT8U canSpeed, + const INT8U canClock); + + void mcp2515_write_mf(const INT8U mcp_addr, // Write CAN Mask or Filter + const INT8U ext, + const INT32U id); + + void mcp2515_write_id(const INT8U mcp_addr, // Write CAN ID + const INT8U ext, + const INT32U id); + + void mcp2515_read_id(const INT8U mcp_addr, // Read CAN ID + INT8U *ext, + INT32U *id); + + void mcp2515_write_canMsg(const INT8U buffer_sidh_addr); // Write CAN message + void mcp2515_read_canMsg(const INT8U buffer_sidh_addr); // Read CAN message + INT8U mcp2515_getNextFreeTXBuf(INT8U *txbuf_n); // Find empty transmit buffer + + /********************************************************************************************************* + * CAN operator function + *********************************************************************************************************/ + + INT8U setMsg(INT32U id, INT8U rtr, INT8U ext, INT8U len, INT8U *pData); // Set message + INT8U clearMsg(); // Clear all message to zero + INT8U readMsg(); // Read message + INT8U sendMsg(); // Send message + +public: + MCP_CAN(INT8U _CS); + + INT8U begin(INT8U idmodeset, INT8U speedset, + INT8U clockset); // Initialize controller parameters + INT8U init_Mask(INT8U num, INT8U ext, INT32U ulData); // Initialize Mask(s) + INT8U init_Mask(INT8U num, INT32U ulData); // Initialize Mask(s) + INT8U init_Filt(INT8U num, INT8U ext, INT32U ulData); // Initialize Filter(s) + INT8U init_Filt(INT8U num, INT32U ulData); // Initialize Filter(s) + INT8U setMode(INT8U opMode); // Set operational mode + INT8U sendMsgBuf(INT32U id, INT8U ext, INT8U len, + INT8U *buf); // Send message to transmit buffer + INT8U sendMsgBuf(INT32U id, INT8U len, + INT8U *buf); // Send message to transmit buffer + INT8U readMsgBuf(INT32U *id, INT8U *ext, INT8U *len, + INT8U *buf); // Read message from receive buffer + INT8U readMsgBuf(INT32U *id, INT8U *len, + INT8U *buf); // Read message from receive buffer + INT8U checkReceive(void); // Check for received data + INT8U checkError(void); // Check for errors + INT8U getError(void); // Check for errors + INT8U errorCountRX(void); // Get error count + INT8U errorCountTX(void); // Get error count + INT8U enOneShotTX(void); // Enable one-shot transmission + INT8U disOneShotTX(void); // Disable one-shot transmission + INT8U abortTX(void); // Abort queued transmission(s) + INT8U setGPO(INT8U data); // Sets GPO + INT8U getGPI(void); // Reads GPI +}; + +#endif +/********************************************************************************************************* + * END FILE + *********************************************************************************************************/ diff --git a/hal/transport/CAN/driver/mcp_can_dfs.h b/hal/transport/CAN/driver/mcp_can_dfs.h new file mode 100644 index 000000000..ff544a68e --- /dev/null +++ b/hal/transport/CAN/driver/mcp_can_dfs.h @@ -0,0 +1,476 @@ +/* + mcp_can_dfs.h + 2012 Copyright (c) Seeed Technology Inc. All right reserved. + 2017 Copyright (c) Cory J. Fowler All Rights Reserved. + + Author:Loovee + Contributor: Cory J. Fowler + 2017-09-25 + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110- + 1301 USA +*/ +#ifndef _MCP2515DFS_H_ +#define _MCP2515DFS_H_ + +#include +#include +#include + +#ifndef INT32U +#define INT32U unsigned long +#endif + +#ifndef INT8U +#define INT8U byte +#endif + +// if print debug information +#define DEBUG_MODE 0 + +/* + * Begin mt + */ +#define TIMEOUTVALUE 50 +#define MCP_SIDH 0 +#define MCP_SIDL 1 +#define MCP_EID8 2 +#define MCP_EID0 3 + +#define MCP_TXB_EXIDE_M 0x08 /* In TXBnSIDL */ +#define MCP_DLC_MASK 0x0F /* 4 LSBits */ +#define MCP_RTR_MASK 0x40 /* (1<<6) Bit 6 */ + +#define MCP_RXB_RX_ANY 0x60 +#define MCP_RXB_RX_EXT 0x40 +#define MCP_RXB_RX_STD 0x20 +#define MCP_RXB_RX_STDEXT 0x00 +#define MCP_RXB_RX_MASK 0x60 +#define MCP_RXB_BUKT_MASK (1<<2) + +/* +** Bits in the TXBnCTRL registers. +*/ +#define MCP_TXB_TXBUFE_M 0x80 +#define MCP_TXB_ABTF_M 0x40 +#define MCP_TXB_MLOA_M 0x20 +#define MCP_TXB_TXERR_M 0x10 +#define MCP_TXB_TXREQ_M 0x08 +#define MCP_TXB_TXIE_M 0x04 +#define MCP_TXB_TXP10_M 0x03 + +#define MCP_TXB_RTR_M 0x40 /* In TXBnDLC */ +#define MCP_RXB_IDE_M 0x08 /* In RXBnSIDL */ +#define MCP_RXB_RTR_M 0x40 /* In RXBnDLC */ + +#define MCP_STAT_RXIF_MASK (0x03) +#define MCP_STAT_RX0IF (1<<0) +#define MCP_STAT_RX1IF (1<<1) + +#define MCP_EFLG_RX1OVR (1<<7) +#define MCP_EFLG_RX0OVR (1<<6) +#define MCP_EFLG_TXBO (1<<5) +#define MCP_EFLG_TXEP (1<<4) +#define MCP_EFLG_RXEP (1<<3) +#define MCP_EFLG_TXWAR (1<<2) +#define MCP_EFLG_RXWAR (1<<1) +#define MCP_EFLG_EWARN (1<<0) +#define MCP_EFLG_ERRORMASK (0xF8) /* 5 MS-Bits */ + +#define MCP_BxBFS_MASK 0x30 +#define MCP_BxBFE_MASK 0x0C +#define MCP_BxBFM_MASK 0x03 + +#define MCP_BxRTS_MASK 0x38 +#define MCP_BxRTSM_MASK 0x07 + +/* + * Define MCP2515 register addresses + */ +#define MCP_RXF0SIDH 0x00 +#define MCP_RXF0SIDL 0x01 +#define MCP_RXF0EID8 0x02 +#define MCP_RXF0EID0 0x03 +#define MCP_RXF1SIDH 0x04 +#define MCP_RXF1SIDL 0x05 +#define MCP_RXF1EID8 0x06 +#define MCP_RXF1EID0 0x07 +#define MCP_RXF2SIDH 0x08 +#define MCP_RXF2SIDL 0x09 +#define MCP_RXF2EID8 0x0A +#define MCP_RXF2EID0 0x0B +#define MCP_BFPCTRL 0x0C +#define MCP_TXRTSCTRL 0x0D +#define MCP_CANSTAT 0x0E +#define MCP_CANCTRL 0x0F +#define MCP_RXF3SIDH 0x10 +#define MCP_RXF3SIDL 0x11 +#define MCP_RXF3EID8 0x12 +#define MCP_RXF3EID0 0x13 +#define MCP_RXF4SIDH 0x14 +#define MCP_RXF4SIDL 0x15 +#define MCP_RXF4EID8 0x16 +#define MCP_RXF4EID0 0x17 +#define MCP_RXF5SIDH 0x18 +#define MCP_RXF5SIDL 0x19 +#define MCP_RXF5EID8 0x1A +#define MCP_RXF5EID0 0x1B +#define MCP_TEC 0x1C +#define MCP_REC 0x1D +#define MCP_RXM0SIDH 0x20 +#define MCP_RXM0SIDL 0x21 +#define MCP_RXM0EID8 0x22 +#define MCP_RXM0EID0 0x23 +#define MCP_RXM1SIDH 0x24 +#define MCP_RXM1SIDL 0x25 +#define MCP_RXM1EID8 0x26 +#define MCP_RXM1EID0 0x27 +#define MCP_CNF3 0x28 +#define MCP_CNF2 0x29 +#define MCP_CNF1 0x2A +#define MCP_CANINTE 0x2B +#define MCP_CANINTF 0x2C +#define MCP_EFLG 0x2D +#define MCP_TXB0CTRL 0x30 +#define MCP_TXB1CTRL 0x40 +#define MCP_TXB2CTRL 0x50 +#define MCP_RXB0CTRL 0x60 +#define MCP_RXB0SIDH 0x61 +#define MCP_RXB1CTRL 0x70 +#define MCP_RXB1SIDH 0x71 + + +#define MCP_TX_INT 0x1C /* Enable all transmit interrup ts */ +#define MCP_TX01_INT 0x0C /* Enable TXB0 and TXB1 interru pts */ +#define MCP_RX_INT 0x03 /* Enable receive interrupts */ +#define MCP_NO_INT 0x00 /* Disable all interrupts */ + +#define MCP_TX01_MASK 0x14 +#define MCP_TX_MASK 0x54 + +/* + * Define SPI Instruction Set + */ +#define MCP_WRITE 0x02 + +#define MCP_READ 0x03 + +#define MCP_BITMOD 0x05 + +#define MCP_LOAD_TX0 0x40 +#define MCP_LOAD_TX1 0x42 +#define MCP_LOAD_TX2 0x44 + +#define MCP_RTS_TX0 0x81 +#define MCP_RTS_TX1 0x82 +#define MCP_RTS_TX2 0x84 +#define MCP_RTS_ALL 0x87 + +#define MCP_READ_RX0 0x90 +#define MCP_READ_RX1 0x94 + +#define MCP_READ_STATUS 0xA0 + +#define MCP_RX_STATUS 0xB0 + +#define MCP_RESET 0xC0 + + +/* + * CANCTRL Register Values + */ +#define MCP_NORMAL 0x00 +#define MCP_SLEEP 0x20 +#define MCP_LOOPBACK 0x40 +#define MCP_LISTENONLY 0x60 +#define MODE_CONFIG 0x80 +#define MODE_POWERUP 0xE0 +#define MODE_MASK 0xE0 +#define ABORT_TX 0x10 +#define MODE_ONESHOT 0x08 +#define CLKOUT_ENABLE 0x04 +#define CLKOUT_DISABLE 0x00 +#define CLKOUT_PS1 0x00 +#define CLKOUT_PS2 0x01 +#define CLKOUT_PS4 0x02 +#define CLKOUT_PS8 0x03 + + +/* + * CNF1 Register Values + */ +#define SJW1 0x00 +#define SJW2 0x40 +#define SJW3 0x80 +#define SJW4 0xC0 + + +/* + * CNF2 Register Values + */ +#define BTLMODE 0x80 +#define SAMPLE_1X 0x00 +#define SAMPLE_3X 0x40 + + +/* + * CNF3 Register Values + */ +#define SOF_ENABLE 0x80 +#define SOF_DISABLE 0x00 +#define WAKFIL_ENABLE 0x40 +#define WAKFIL_DISABLE 0x00 + + +/* + * CANINTF Register Bits + */ +#define MCP_RX0IF 0x01 +#define MCP_RX1IF 0x02 +#define MCP_TX0IF 0x04 +#define MCP_TX1IF 0x08 +#define MCP_TX2IF 0x10 +#define MCP_ERRIF 0x20 +#define MCP_WAKIF 0x40 +#define MCP_MERRF 0x80 + + +/* + * Speed 8M + */ +#define MCP_8MHz_1000kBPS_CFG1 (0x00) +#define MCP_8MHz_1000kBPS_CFG2 (0xC0) /* Enabled SAM bit */ +#define MCP_8MHz_1000kBPS_CFG3 (0x80) /* Sample point at 75% */ + +#define MCP_8MHz_500kBPS_CFG1 (0x00) +#define MCP_8MHz_500kBPS_CFG2 (0xD1) /* Enabled SAM bit */ +#define MCP_8MHz_500kBPS_CFG3 (0x81) /* Sample point at 75% */ + +#define MCP_8MHz_250kBPS_CFG1 (0x80) /* Increased SJW */ +#define MCP_8MHz_250kBPS_CFG2 (0xE5) /* Enabled SAM bit */ +#define MCP_8MHz_250kBPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_8MHz_200kBPS_CFG1 (0x80) /* Increased SJW */ +#define MCP_8MHz_200kBPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_200kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_8MHz_125kBPS_CFG1 (0x81) /* Increased SJW */ +#define MCP_8MHz_125kBPS_CFG2 (0xE5) /* Enabled SAM bit */ +#define MCP_8MHz_125kBPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_8MHz_100kBPS_CFG1 (0x81) /* Increased SJW */ +#define MCP_8MHz_100kBPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_100kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_8MHz_80kBPS_CFG1 (0x84) /* Increased SJW */ +#define MCP_8MHz_80kBPS_CFG2 (0xD3) /* Enabled SAM bit */ +#define MCP_8MHz_80kBPS_CFG3 (0x81) /* Sample point at 75% */ + +#define MCP_8MHz_50kBPS_CFG1 (0x84) /* Increased SJW */ +#define MCP_8MHz_50kBPS_CFG2 (0xE5) /* Enabled SAM bit */ +#define MCP_8MHz_50kBPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_8MHz_40kBPS_CFG1 (0x84) /* Increased SJW */ +#define MCP_8MHz_40kBPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_40kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_8MHz_33k3BPS_CFG1 (0x85) /* Increased SJW */ +#define MCP_8MHz_33k3BPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_33k3BPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_8MHz_31k25BPS_CFG1 (0x87) /* Increased SJW */ +#define MCP_8MHz_31k25BPS_CFG2 (0xE5) /* Enabled SAM bit */ +#define MCP_8MHz_31k25BPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_8MHz_20kBPS_CFG1 (0x89) /* Increased SJW */ +#define MCP_8MHz_20kBPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_20kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_8MHz_10kBPS_CFG1 (0x93) /* Increased SJW */ +#define MCP_8MHz_10kBPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_10kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_8MHz_5kBPS_CFG1 (0xA7) /* Increased SJW */ +#define MCP_8MHz_5kBPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_5kBPS_CFG3 (0x84) /* Sample point at 75% */ + +/* + * speed 16M + */ +#define MCP_16MHz_1000kBPS_CFG1 (0x00) +#define MCP_16MHz_1000kBPS_CFG2 (0xCA) +#define MCP_16MHz_1000kBPS_CFG3 (0x81) /* Sample point at 75% */ + +#define MCP_16MHz_500kBPS_CFG1 (0x40) /* Increased SJW */ +#define MCP_16MHz_500kBPS_CFG2 (0xE5) +#define MCP_16MHz_500kBPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_16MHz_250kBPS_CFG1 (0x41) +#define MCP_16MHz_250kBPS_CFG2 (0xE5) +#define MCP_16MHz_250kBPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_16MHz_200kBPS_CFG1 (0x41) /* Increased SJW */ +#define MCP_16MHz_200kBPS_CFG2 (0xF6) +#define MCP_16MHz_200kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_16MHz_125kBPS_CFG1 (0x43) /* Increased SJW */ +#define MCP_16MHz_125kBPS_CFG2 (0xE5) +#define MCP_16MHz_125kBPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_16MHz_100kBPS_CFG1 (0x44) /* Increased SJW */ +#define MCP_16MHz_100kBPS_CFG2 (0xE5) +#define MCP_16MHz_100kBPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_16MHz_80kBPS_CFG1 (0x44) /* Increased SJW */ +#define MCP_16MHz_80kBPS_CFG2 (0xF6) +#define MCP_16MHz_80kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_16MHz_50kBPS_CFG1 (0x47) /* Increased SJW */ +#define MCP_16MHz_50kBPS_CFG2 (0xF6) +#define MCP_16MHz_50kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_16MHz_40kBPS_CFG1 (0x49) /* Increased SJW */ +#define MCP_16MHz_40kBPS_CFG2 (0xF6) +#define MCP_16MHz_40kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_16MHz_33k3BPS_CFG1 (0x4E) +#define MCP_16MHz_33k3BPS_CFG2 (0xE5) +#define MCP_16MHz_33k3BPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_16MHz_20kBPS_CFG1 (0x53) /* Increased SJW */ +#define MCP_16MHz_20kBPS_CFG2 (0xF6) +#define MCP_16MHz_20kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_16MHz_10kBPS_CFG1 (0x67) /* Increased SJW */ +#define MCP_16MHz_10kBPS_CFG2 (0xF6) +#define MCP_16MHz_10kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_16MHz_5kBPS_CFG1 (0x3F) +#define MCP_16MHz_5kBPS_CFG2 (0xFF) +#define MCP_16MHz_5kBPS_CFG3 (0x87) /* Sample point at 68% */ + +/* + * speed 20M + */ +#define MCP_20MHz_1000kBPS_CFG1 (0x00) +#define MCP_20MHz_1000kBPS_CFG2 (0xD9) +#define MCP_20MHz_1000kBPS_CFG3 (0x82) /* Sample point at 80% */ + +#define MCP_20MHz_500kBPS_CFG1 (0x40) /* Increased SJW */ +#define MCP_20MHz_500kBPS_CFG2 (0xF6) +#define MCP_20MHz_500kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_20MHz_250kBPS_CFG1 (0x41) /* Increased SJW */ +#define MCP_20MHz_250kBPS_CFG2 (0xF6) +#define MCP_20MHz_250kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_20MHz_200kBPS_CFG1 (0x44) /* Increased SJW */ +#define MCP_20MHz_200kBPS_CFG2 (0xD3) +#define MCP_20MHz_200kBPS_CFG3 (0x81) /* Sample point at 80% */ + +#define MCP_20MHz_125kBPS_CFG1 (0x44) /* Increased SJW */ +#define MCP_20MHz_125kBPS_CFG2 (0xE5) +#define MCP_20MHz_125kBPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_20MHz_100kBPS_CFG1 (0x44) /* Increased SJW */ +#define MCP_20MHz_100kBPS_CFG2 (0xF6) +#define MCP_20MHz_100kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_20MHz_80kBPS_CFG1 (0xC4) /* Increased SJW */ +#define MCP_20MHz_80kBPS_CFG2 (0xFF) +#define MCP_20MHz_80kBPS_CFG3 (0x87) /* Sample point at 68% */ + +#define MCP_20MHz_50kBPS_CFG1 (0x49) /* Increased SJW */ +#define MCP_20MHz_50kBPS_CFG2 (0xF6) +#define MCP_20MHz_50kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_20MHz_40kBPS_CFG1 (0x18) +#define MCP_20MHz_40kBPS_CFG2 (0xD3) +#define MCP_20MHz_40kBPS_CFG3 (0x81) /* Sample point at 80% */ + + +#define MCPDEBUG (0) +#define MCPDEBUG_TXBUF (0) +#define MCP_N_TXBUFFERS (3) + +#define MCP_RXBUF_0 (MCP_RXB0SIDH) +#define MCP_RXBUF_1 (MCP_RXB1SIDH) + +#define MCP2515_SELECT() digitalWrite(MCPCS, LOW) +#define MCP2515_UNSELECT() digitalWrite(MCPCS, HIGH) + +#define MCP2515_OK (0) +#define MCP2515_FAIL (1) +#define MCP_ALLTXBUSY (2) + +#define CANDEBUG 1 + +#define CANUSELOOP 0 + +#define CANSENDTIMEOUT (200) /* milliseconds */ + +/* + * initial value of gCANAutoProcess + */ +#define CANAUTOPROCESS (1) +#define CANAUTOON (1) +#define CANAUTOOFF (0) + +#define CAN_STDID (0) +#define CAN_EXTID (1) + +#define CANDEFAULTIDENT (0x55CC) +#define CANDEFAULTIDENTEXT (CAN_EXTID) + +#define MCP_STDEXT 0 /* Standard and Extended */ +#define MCP_STD 1 /* Standard IDs ONLY */ +#define MCP_EXT 2 /* Extended IDs ONLY */ +#define MCP_ANY 3 /* Disables Masks and Filters */ + +#define MCP_20MHZ 0 +#define MCP_16MHZ 1 +#define MCP_8MHZ 2 + +#define CAN_4K096BPS 0 +#define CAN_5KBPS 1 +#define CAN_10KBPS 2 +#define CAN_20KBPS 3 +#define CAN_31K25BPS 4 +#define CAN_33K3BPS 5 +#define CAN_40KBPS 6 +#define CAN_50KBPS 7 +#define CAN_80KBPS 8 +#define CAN_100KBPS 9 +#define CAN_125KBPS 10 +#define CAN_200KBPS 11 +#define CAN_250KBPS 12 +#define CAN_500KBPS 13 +#define CAN_1000KBPS 14 + +#define CAN_OK (0) +#define CAN_FAILINIT (1) +#define CAN_FAILTX (2) +#define CAN_MSGAVAIL (3) +#define CAN_NOMSG (4) +#define CAN_CTRLERROR (5) +#define CAN_GETTXBFTIMEOUT (6) +#define CAN_SENDMSGTIMEOUT (7) +#define CAN_FAIL (0xff) + +#define CAN_MAX_CHAR_IN_MESSAGE (8) + +#endif +/********************************************************************************************************* + END FILE +*********************************************************************************************************/ diff --git a/hal/transport/MyTransportHAL.h b/hal/transport/MyTransportHAL.h index 66b415085..62d189a94 100644 --- a/hal/transport/MyTransportHAL.h +++ b/hal/transport/MyTransportHAL.h @@ -61,6 +61,9 @@ #if defined(MY_RS485) #error Receive message buffering not supported for RS485! #endif +#if defined(MY_CAN) +#error Receive message buffering not supported for CAN! +#endif #elif defined(MY_RX_MESSAGE_BUFFER_SIZE) #error Receive message buffering requires message buffering feature enabled! #endif diff --git a/keywords.txt b/keywords.txt index 89d9091ec..1b069ef59 100755 --- a/keywords.txt +++ b/keywords.txt @@ -261,6 +261,16 @@ MY_PJON_PIN LITERAL1 MY_DEBUG_VERBOSE_PJON LITERAL1 MY_PJON_MAX_RETRIES LITERAL1 +# CAN +MY_CAN LITERAL1 +MY_DEBUG_VERBOSE_CAN LITERAL1 +MY_DEBUG_VERBOSE_CAN_INTERNAL LITERAL1 +MY_CAN_INT LITERAL1 +MY_CAN_CS LITERAL1 +MY_CAN_SPEED LITERAL1 +MY_CAN_CLOCK LITERAL1 +MY_CAN_BUF_SIZE LITERAL1 + # Gateway / MQTT MY_GATEWAY_CLIENT_MODE LITERAL1 MY_GATEWAY_ENC28J60 LITERAL1 diff --git a/tests/Arduino/sketches/can_transport/can_transport.ino b/tests/Arduino/sketches/can_transport/can_transport.ino new file mode 100644 index 000000000..6047f5524 --- /dev/null +++ b/tests/Arduino/sketches/can_transport/can_transport.ino @@ -0,0 +1,34 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2022 Sensnology AB + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + ******************************* + */ +#define MY_DEBUG +#define MY_CAN +#define MY_CAN_CS (10u) +#define MY_DEBUG_VERBOSE_CAN +#define MY_DEBUG_VERBOSE_CAN_INTERNAL +#include + +void setup() +{ +} + +void loop() +{ +}