Чтение can

main
Bill1389 2 years ago
commit df19f10442

5
.gitignore vendored

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

@ -0,0 +1,272 @@
# CAN API
## Include Library
```arduino
#include <CAN.h>
```
## Setup
### Begin
Initialize the library with the specified bit rate.
```arduino
CAN.begin(bitrate);
```
* `bitrate` - bit rate in bits per seconds (bps) (`1000E3`, `500E3`, `250E3`, `200E3`, `125E3`, `100E3`, `80E3`, `50E3`, `40E3`, `20E3`, `10E3`, `5E3`)
Returns `1` on success, `0` on failure.
### Set pins
#### MCP2515
Override the default `CS` and `INT` pins used by the library. **Must** be called before `CAN.begin(...)`.
```arduino
CAN.setPins(cs, irq);
```
* `cs` - new chip select pin to use, defaults to `10`
* `irq` - new INT pin to use, defaults to `2`. **Must** be interrupt capable via [attachInterrupt(...)](https://www.arduino.cc/en/Reference/AttachInterrupt).
This call is optional and only needs to be used if you need to change the default pins used.
#### ESP32
Override the default `CTX` and `CRX` pins used by the library. **Must** be called before `CAN.begin(...)`.
```arduino
CAN.setPins(rx, tx);
```
* `rx` - new CRX pin to use, defaults to `4`
* `tx` - new CTX pin to use, defaults to `5`.
This call is optional and only needs to be used if you need to change the default pins used.
### Set SPI Frequency
**MCP2515 only**
Override the default SPI frequency of 10 MHz used by the library. **Must** be called before `CAN.begin(...)`.
```arduino
CAN.setSPIFrequency(frequency);
```
* `frequency` - new SPI frequency to use, defaults to `10E6`
This call is optional and only needs to be used if you need to change the default SPI frequency used. Some logic level converters cannot support high speeds such as 10 MHz, so a lower SPI frequency can be selected with `CAN.setSPIFrequency(frequency)`.
### Set Clock Frequency
**MCP2515 only**
Override the default clock source frequency that is connected to the MCP2515. **Must** be called before `CAN.begin(...)`.
```arduino
CAN.setClockFrequency(clockFrequency);
```
* `clockFrequency` - new clock frequency to use (`8E6`, `16E6`) connected to MCP2515, defaults to `16 Mhz`
This call is optional and only needs to be used if you need to change the clock source frequency connected to the MCP2515. Most shields have a 16 MHz clock source on board, some breakout boards have a 8 MHz source.
### End
Stop the library
```arduino
CAN.end()
```
## Sending data
### Begin packet
Start the sequence of sending a packet.
```arduino
CAN.beginPacket(id);
CAN.beginPacket(id, dlc);
CAN.beginPacket(id, dlc, rtr);
CAN.beginExtendedPacket(id);
CAN.beginExtendedPacket(id, dlc);
CAN.beginExtendedPacket(id, dlc, rtr);
```
* `id` - 11-bit id (standard packet) or 29-bit packet id (extended packet)
* `dlc` - (optional) value of Data Length Code (DLC) field of packet, default is size of data written in packet
* `rtr` - (optional) value of Remote Transmission Request (RTR) field of packet (`false` or `true`), defaults to `false`. RTR packets contain no data, the DLC field of the packet represents the requested length.
Returns `1` on success, `0` on failure.
### Writing
Write data to the packet. Each packet can contain up to 8 bytes.
```arduino
CAN.write(byte);
CAN.write(buffer, length);
```
* `byte` - single byte to write to packet
or
* `buffer` - data to write to packet
* `length` - size of data to write
Returns the number of bytes written.
**Note:** Other Arduino `Print` API's can also be used to write data into the packet
### End packet
End the sequence of sending a packet.
```arduino
CAN.endPacket()
```
Returns `1` on success, `0` on failure.
## Receiving data
### Parsing packet
Check if a packet has been received.
```arduino
int packetSize = CAN.parsePacket();
```
Returns the packet size in bytes or `0` if no packet was received. For RTR packets the size reflects the DLC field of the packet.
### Register callback
Register a callback function for when a packet is received.
```arduino
CAN.onReceive(onReceive);
void onReceive(int packetSize) {
// ...
}
```
* `onReceive` - function to call when a packet is received.
### Packet ID
```arduino
long id = CAN.packetId();
```
Returns the id (11-bit or 29 bit) of the received packet. Standard packets have an 11-bit id, extended packets have an 29-bit id.
### Packet Extended
```arduino
bool extended = CAN.packetExtended();
```
Returns `true` if the received packet is extended, `false` otherwise.
### Packet RTR
```arduino
bool rtr = CAN.packetRtr();
```
Returns the value of the Remote Transmission Request (RTR) field of the packet `true`/`false`. RTR packets contain no data, the DLC field is the requested data length.
### Packet DLC
```arduino
int DLC = CAN.packetDlc();
```
Returns the value of the Data Length Code (DLC) field of the packet.
### Available
```arduino
int availableBytes = CAN.available()
```
Returns number of bytes available for reading.
### Peeking
Peek at the next byte in the packet.
```arduino
int b = CAN.peek();
```
Returns the next byte in the packet or `-1` if no bytes are available.
### Reading
Read the next byte from the packet.
```arduino
int b = CAN.read();
```
Returns the next byte in the packet or `-1` if no bytes are available.
**Note:** Other Arduino [`Stream` API's](https://www.arduino.cc/en/Reference/Stream) can also be used to read data from the packet
### Filtering
Filter packets that meet the desired criteria.
```
CAN.filter(id);
CAN.filter(id, mask);
CAN.filterExtended(id);
CAN.filterExtended(id, mask);
```
* `id` - 11-bit id (standard packet) or 29-bit packet id (extended packet)
* `mask` - (optional) 11-bit mask (standard packet) or 29-bit mask (extended packet), defaults to `0x7ff` or `0x1fffffff` (extended)
Only packets that meet the following criteria are acknowleged and received, other packets are ignored:
```
if ((packetId & mask) == id) {
// acknowleged and received
} else {
// ignored
}
```
Returns `1` on success, `0` on failure.
## Other modes
### Loopback mode
Put the CAN controller in loopback mode, any outgoing packets will also be received.
```arduino
CAN.loopback();
```
### Sleep mode
Put the CAN contoller in sleep mode.
```arduino
CAN.sleep();
```
Wake up the CAN contoller if it was previously in sleep mode.
```arduino
CAN.wakeup();
```

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Sandeep Mistry
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,69 @@
# Arduino CAN
[![Build Status](https://travis-ci.org/sandeepmistry/arduino-CAN.svg?branch=master)](https://travis-ci.org/sandeepmistry/arduino-CAN)
An Arduino library for sending and receiving data using CAN bus.
## Compatible Hardware
* [Microchip MCP2515](http://www.microchip.com/wwwproducts/en/en010406) based boards/shields
* [Arduino MKR CAN shield](https://store.arduino.cc/arduino-mkr-can-shield)
* [Espressif ESP32](http://espressif.com/en/products/hardware/esp32/overview)'s built-in [SJA1000](https://www.nxp.com/products/analog/interfaces/in-vehicle-network/can-transceiver-and-controllers/stand-alone-can-controller:SJA1000T) compatible CAN controller with an external 3.3V CAN transceiver
### Microchip MCP2515 wiring
| Microchip MCP2515 | Arduino |
| :---------------: | :-----: |
| VCC | 5V |
| GND | GND |
| SCK | SCK |
| SO | MISO |
| SI | MOSI |
| CS | 10 |
| INT | 2 |
`CS` and `INT` pins can be changed by using `CAN.setPins(cs, irq)`. `INT` pin is optional, it is only needed for receive callback mode. If `INT` pin is used, it **must** be interrupt capable via [`attachInterrupt(...)`](https://www.arduino.cc/en/Reference/AttachInterrupt).
**NOTE**: Logic level converters must be used for boards which operate at 3.3V.
### Espressif ESP32 wiring
Requires an external 3.3V CAN transceiver, such as a [TI SN65HVD230](http://www.ti.com/product/SN65HVD230).
| CAN transceiver | ESP32 |
| :-------------: | :---: |
| 3V3 | 3V3 |
| GND | GND |
| CTX | 5 |
| CRX | 4 |
`CTX` and `CRX` pins can be changed by using `CAN.setPins(rx, tx)`.
## Installation
### Using the Arduino IDE Library Manager
1. Choose `Sketch` -> `Include Library` -> `Manage Libraries...`
2. Type `CAN` into the search box.
3. Click the row to select the library.
4. Click the `Install` button to install the library.
### Using Git
```sh
cd ~/Documents/Arduino/libraries/
git clone https://github.com/sandeepmistry/arduino-CAN CAN
```
## API
See [API.md](API.md).
## Examples
See [examples](examples) folder.
## License
This library is [licensed](LICENSE) under the [MIT Licence](http://en.wikipedia.org/wiki/MIT_License).

@ -0,0 +1,56 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <CAN.h>
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("CAN Receiver");
// start the CAN bus at 500 kbps
if (!CAN.begin(500E3)) {
Serial.println("Starting CAN failed!");
while (1);
}
}
void loop() {
// try to parse packet
int packetSize = CAN.parsePacket();
if (packetSize) {
// received a packet
Serial.print("Received ");
if (CAN.packetExtended()) {
Serial.print("extended ");
}
if (CAN.packetRtr()) {
// Remote transmission request, packet contains no data
Serial.print("RTR ");
}
Serial.print("packet with id 0x");
Serial.print(CAN.packetId(), HEX);
if (CAN.packetRtr()) {
Serial.print(" and requested length ");
Serial.println(CAN.packetDlc());
} else {
Serial.print(" and length ");
Serial.println(packetSize);
// only print packet data for non-RTR packets
while (CAN.available()) {
Serial.print((char)CAN.read());
}
Serial.println();
}
Serial.println();
}
}

@ -0,0 +1,59 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <CAN.h>
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("CAN Receiver Callback");
// start the CAN bus at 500 kbps
if (!CAN.begin(500E3)) {
Serial.println("Starting CAN failed!");
while (1);
}
// register the receive callback
CAN.onReceive(onReceive);
}
void loop() {
// do nothing
}
void onReceive(int packetSize) {
// received a packet
Serial.print("Received ");
if (CAN.packetExtended()) {
Serial.print("extended ");
}
if (CAN.packetRtr()) {
// Remote transmission request, packet contains no data
Serial.print("RTR ");
}
Serial.print("packet with id 0x");
Serial.print(CAN.packetId(), HEX);
if (CAN.packetRtr()) {
Serial.print(" and requested length ");
Serial.println(CAN.packetDlc());
} else {
Serial.print(" and length ");
Serial.println(packetSize);
// only print packet data for non-RTR packets
while (CAN.available()) {
Serial.print((char)CAN.read());
}
Serial.println();
}
Serial.println();
}

@ -0,0 +1,50 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <CAN.h>
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("CAN Sender");
// start the CAN bus at 500 kbps
if (!CAN.begin(500E3)) {
Serial.println("Starting CAN failed!");
while (1);
}
}
void loop() {
// send packet: id is 11 bits, packet can contain up to 8 bytes of data
Serial.print("Sending packet ... ");
CAN.beginPacket(0x12);
CAN.write('h');
CAN.write('e');
CAN.write('l');
CAN.write('l');
CAN.write('o');
CAN.endPacket();
Serial.println("done");
delay(1000);
// send extended packet: id is 29 bits, packet can contain up to 8 bytes of data
Serial.print("Sending extended packet ... ");
CAN.beginExtendedPacket(0xabcdef);
CAN.write('w');
CAN.write('o');
CAN.write('r');
CAN.write('l');
CAN.write('d');
CAN.endPacket();
Serial.println("done");
delay(1000);
}

@ -0,0 +1,57 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
//
// This examples queries the engine RPM (OBD-II PID 0x0c) once a seconds and
// prints the value to the serial monitor
//
#include <CAN.h>
// Most cars support 11-bit adddress, others (like Honda),
// require 29-bit (extended) addressing, set the next line
// to true to use extended addressing
const bool useStandardAddressing = true;
void setup() {
Serial.begin(9600);
Serial.println("CAN OBD-II engine RPM");
// start the CAN bus at 500 kbps
if (!CAN.begin(500E3)) {
Serial.println("Starting CAN failed!");
while (1);
}
// add filter to only receive the CAN bus ID's we care about
if (useStandardAddressing) {
CAN.filter(0x7e8);
} else {
CAN.filterExtended(0x18daf110);
}
}
void loop() {
if (useStandardAddressing) {
CAN.beginPacket(0x7df, 8);
} else {
CAN.beginExtendedPacket(0x18db33f1, 8);
}
CAN.write(0x02); // number of additional bytes
CAN.write(0x01); // show current data
CAN.write(0x0c); // engine RPM
CAN.endPacket();
// wait for response
while (CAN.parsePacket() == 0 ||
CAN.read() < 3 || // correct length
CAN.read() != 0x41 || // correct mode
CAN.read() != 0x0c); // correct PID
float rpm = ((CAN.read() * 256.0) + CAN.read()) / 4.0;
Serial.print("Engine RPM = ");
Serial.println(rpm);
delay(1000);
}

@ -0,0 +1,83 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
//
// This examples queries the ECU for the Mode 01 OBD-II PID's it supports and prints the supported
// OBD-II PID's to the serial monitor
//
// A full list of PID's and their meaning can be found here:
// https://en.wikipedia.org/wiki/OBD-II_PIDs#Mode_01
//
#include <CAN.h>
// Most cars support 11-bit adddress, others (like Honda),
// require 29-bit (extended) addressing, set the next line
// to true to use extended addressing
const bool useStandardAddressing = true;
void setup() {
Serial.begin(9600);
Serial.println("CAN OBD-II supported pids");
// start the CAN bus at 500 kbps
if (!CAN.begin(500E3)) {
Serial.println("Starting CAN failed!");
while (1);
}
// add filter to only receive the CAN bus ID's we care about
if (useStandardAddressing) {
CAN.filter(0x7e8);
} else {
CAN.filterExtended(0x18daf110);
}
}
void loop() {
for (int pid = 0x00; pid < 0xe0; pid += 0x20) {
if (useStandardAddressing) {
CAN.beginPacket(0x7df, 8);
} else {
CAN.beginExtendedPacket(0x18db33f1, 8);
}
CAN.write(0x02); // number of additional bytes
CAN.write(0x01); // show current data
CAN.write(pid); // PID
CAN.endPacket();
// wait for response
while (CAN.parsePacket() == 0 ||
CAN.read() < 6 || // correct length
CAN.read() != 0x41 || // correct mode
CAN.read() != pid); // correct PID
unsigned long pidsSupported = 0;
for (int i = 0; i < 4; i++) {
pidsSupported <<= 8;
pidsSupported |= CAN.read();
}
for (unsigned int i = 31; i > 0; i--) {
if (pidsSupported & (1UL << i)) {
int pidSupported = pid + (32 - i);
Serial.print("0x");
if (pidSupported < 16) {
Serial.print("0");
}
Serial.println(pidSupported, HEX);
}
}
if ((pidsSupported & 0x00000001) == 0x00000000) {
// next round not supported, all done
break;
}
}
Serial.println("That's all folks!");
while (1); // all done
}

@ -0,0 +1,82 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
//
// This examples queries the ECU for the car's Vehicle Identification Number (VIN) and
// prints it out to the serial monitor using Mode 09 and OBD-II PID 0x02
//
#include <CAN.h>
// Most cars support 11-bit adddress, others (like Honda),
// require 29-bit (extended) addressing, set the next line
// to true to use extended addressing
const bool useStandardAddressing = true;
void setup() {
Serial.begin(9600);
Serial.println("CAN OBD-II VIN reader");
// start the CAN bus at 500 kbps
if (!CAN.begin(500E3)) {
Serial.println("Starting CAN failed!");
while (1);
}
// add filter to only receive the CAN bus ID's we care about
if (useStandardAddressing) {
CAN.filter(0x7e8);
} else {
CAN.filterExtended(0x18daf110);
}
}
void loop() {
// send the request for the first chunk
if (useStandardAddressing) {
CAN.beginPacket(0x7df, 8);
} else {
CAN.beginExtendedPacket(0x18db33f1, 8);
}
CAN.write(0x02); // Number of additional bytes
CAN.write(0x09); // Request vehicle information
CAN.write(0x02); // Vehicle Identification Number (VIN)
CAN.endPacket();
// wait for response
while (CAN.parsePacket() == 0 ||
CAN.read() != 0x10 || CAN.read() != 0x14 || // correct length
CAN.read() != 0x49 || // correct mode
CAN.read() != 0x02 || // correct PID
CAN.read() != 0x01);
// print out
while (CAN.available()) {
Serial.write((char)CAN.read());
}
// read in remaining chunks
for (int i = 0; i < 2; i++) {
// send the request for the next chunk
if (useStandardAddressing) {
CAN.beginPacket(0x7e0, 8);
} else {
CAN.beginExtendedPacket(0x18db33f1, 8);
}
CAN.write(0x30);
CAN.endPacket();
// wait for response
while (CAN.parsePacket() == 0 ||
CAN.read() != (0x21 + i)); // correct sequence number
// print out
while (CAN.available()) {
Serial.write((char)CAN.read());
}
}
Serial.println("That's all folks!");
while (1); // all done
}

@ -0,0 +1,49 @@
#######################################
# Syntax Coloring Map For CAN
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
CAN KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
end KEYWORD2
beginPacket KEYWORD2
beginExtendedPacket KEYWORD2
endPacket KEYWORD2
parsePacket KEYWORD2
packetId KEYWORD2
packetExtended KEYWORD2
packetRtr KEYWORD2
packetDlc KEYWORD2
write KEYWORD2
available KEYWORD2
read KEYWORD2
peek KEYWORD2
flush KEYWORD2
onReceive KEYWORD2
filter KEYWORD2
filterExtended KEYWORD2
loopback KEYWORD2
sleep KEYWORD2
wakeup KEYWORD2
setPins KEYWORD2
setSPIFrequency KEYWORD2
setClockFrequency KEYWORD2
dumpRegisters KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

@ -0,0 +1,10 @@
name=CAN
version=0.3.1
author=Sandeep Mistry <sandeep.mistry@gmail.com>
maintainer=Sandeep Mistry <sandeep.mistry@gmail.com>
sentence=An Arduino library for sending and receiving data using CAN bus.
paragraph=Supports Microchip MCP2515 based boards/shields and the Espressif ESP32's built-in SJA1000 compatible CAN controller.
category=Communication
url=https://github.com/sandeepmistry/arduino-CAN
architectures=*
includes=CAN.h

@ -0,0 +1,13 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef CAN_H
#define CAN_H
#ifdef ARDUINO_ARCH_ESP32
#include "ESP32SJA1000.h"
#else
#include "MCP2515.h"
#endif
#endif

@ -0,0 +1,216 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "CANController.h"
CANControllerClass::CANControllerClass() :
_onReceive(NULL),
_packetBegun(false),
_txId(-1),
_txExtended(-1),
_txRtr(false),
_txDlc(0),
_txLength(0),
_rxId(-1),
_rxExtended(false),
_rxRtr(false),
_rxDlc(0),
_rxLength(0),
_rxIndex(0)
{
// overide Stream timeout value
setTimeout(0);
}
CANControllerClass::~CANControllerClass()
{
}
int CANControllerClass::begin(long /*baudRate*/)
{
_packetBegun = false;
_txId = -1;
_txRtr =false;
_txDlc = 0;
_txLength = 0;
_rxId = -1;
_rxRtr = false;
_rxDlc = 0;
_rxLength = 0;
_rxIndex = 0;
return 1;
}
void CANControllerClass::end()
{
}
int CANControllerClass::beginPacket(int id, int dlc, bool rtr)
{
if (id < 0 || id > 0x7FF) {
return 0;
}
if (dlc > 8) {
return 0;
}
_packetBegun = true;
_txId = id;
_txExtended = false;
_txRtr = rtr;
_txDlc = dlc;
_txLength = 0;
memset(_txData, 0x00, sizeof(_txData));
return 1;
}
int CANControllerClass::beginExtendedPacket(long id, int dlc, bool rtr)
{
if (id < 0 || id > 0x1FFFFFFF) {
return 0;
}
if (dlc > 8) {
return 0;
}
_packetBegun = true;
_txId = id;
_txExtended = true;
_txRtr = rtr;
_txDlc = dlc;
_txLength = 0;
memset(_txData, 0x00, sizeof(_txData));
return 1;
}
int CANControllerClass::endPacket()
{
if (!_packetBegun) {
return 0;
}
_packetBegun = false;
if (_txDlc >= 0) {
_txLength = _txDlc;
}
return 1;
}
int CANControllerClass::parsePacket()
{
return 0;
}
long CANControllerClass::packetId()
{
return _rxId;
}
bool CANControllerClass::packetExtended()
{
return _rxExtended;
}
bool CANControllerClass::packetRtr()
{
return _rxRtr;
}
int CANControllerClass::packetDlc()
{
return _rxDlc;
}
size_t CANControllerClass::write(uint8_t byte)
{
return write(&byte, sizeof(byte));
}
size_t CANControllerClass::write(const uint8_t *buffer, size_t size)
{
if (!_packetBegun) {
return 0;
}
if (size > (sizeof(_txData) - _txLength)) {
size = sizeof(_txData) - _txLength;
}
memcpy(&_txData[_txLength], buffer, size);
_txLength += size;
return size;
}
int CANControllerClass::available()
{
return (_rxLength - _rxIndex);
}
int CANControllerClass::read()
{
if (!available()) {
return -1;
}
return _rxData[_rxIndex++];
}
int CANControllerClass::peek()
{
if (!available()) {
return -1;
}
return _rxData[_rxIndex];
}
void CANControllerClass::flush()
{
}
void CANControllerClass::onReceive(void(*callback)(int))
{
_onReceive = callback;
}
int CANControllerClass::filter(int /*id*/, int /*mask*/)
{
return 0;
}
int CANControllerClass::filterExtended(long /*id*/, long /*mask*/)
{
return 0;
}
int CANControllerClass::observe()
{
return 0;
}
int CANControllerClass::loopback()
{
return 0;
}
int CANControllerClass::sleep()
{
return 0;
}
int CANControllerClass::wakeup()
{
return 0;
}

@ -0,0 +1,71 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef CAN_CONTROLLER_H
#define CAN_CONTROLLER_H
#include <Arduino.h>
class CANControllerClass : public Stream {
public:
virtual int begin(long baudRate);
virtual void end();
int beginPacket(int id, int dlc = -1, bool rtr = false);
int beginExtendedPacket(long id, int dlc = -1, bool rtr = false);
virtual int endPacket();
virtual int parsePacket();
long packetId();
bool packetExtended();
bool packetRtr();
int packetDlc();
// from Print
virtual size_t write(uint8_t byte);
virtual size_t write(const uint8_t *buffer, size_t size);
// from Stream
virtual int available();
virtual int read();
virtual int peek();
virtual void flush();
virtual void onReceive(void(*callback)(int));
virtual int filter(int id) { return filter(id, 0x7ff); }
virtual int filter(int id, int mask);
virtual int filterExtended(long id) { return filterExtended(id, 0x1fffffff); }
virtual int filterExtended(long id, long mask);
virtual int observe();
virtual int loopback();
virtual int sleep();
virtual int wakeup();
protected:
CANControllerClass();
virtual ~CANControllerClass();
protected:
void (*_onReceive)(int);
bool _packetBegun;
long _txId;
bool _txExtended;
bool _txRtr;
int _txDlc;
int _txLength;
uint8_t _txData[8];
long _rxId;
bool _rxExtended;
bool _rxRtr;
int _rxDlc;
int _rxLength;
int _rxIndex;
uint8_t _rxData[8];
};
#endif

@ -0,0 +1,427 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifdef ARDUINO_ARCH_ESP32
#include "esp_intr.h"
#include "soc/dport_reg.h"
#include "driver/gpio.h"
#include "ESP32SJA1000.h"
#define REG_BASE 0x3ff6b000
#define REG_MOD 0x00
#define REG_CMR 0x01
#define REG_SR 0x02
#define REG_IR 0x03
#define REG_IER 0x04
#define REG_BTR0 0x06
#define REG_BTR1 0x07
#define REG_OCR 0x08
#define REG_ALC 0x0b
#define REG_ECC 0x0c
#define REG_EWLR 0x0d
#define REG_RXERR 0x0e
#define REG_TXERR 0x0f
#define REG_SFF 0x10
#define REG_EFF 0x10
#define REG_ACRn(n) (0x10 + n)
#define REG_AMRn(n) (0x14 + n)
#define REG_CDR 0x1F
ESP32SJA1000Class::ESP32SJA1000Class() :
CANControllerClass(),
_rxPin(DEFAULT_CAN_RX_PIN),
_txPin(DEFAULT_CAN_TX_PIN),
_loopback(false),
_intrHandle(NULL)
{
}
ESP32SJA1000Class::~ESP32SJA1000Class()
{
}
int ESP32SJA1000Class::begin(long baudRate)
{
CANControllerClass::begin(baudRate);
_loopback = false;
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST);
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_CAN_CLK_EN);
// RX pin
gpio_set_direction(_rxPin, GPIO_MODE_INPUT);
gpio_matrix_in(_rxPin, CAN_RX_IDX, 0);
gpio_pad_select_gpio(_rxPin);
// TX pin
gpio_set_direction(_txPin, GPIO_MODE_OUTPUT);
gpio_matrix_out(_txPin, CAN_TX_IDX, 0, 0);
gpio_pad_select_gpio(_txPin);
modifyRegister(REG_CDR, 0x80, 0x80); // pelican mode
modifyRegister(REG_BTR0, 0xc0, 0x40); // SJW = 1
modifyRegister(REG_BTR1, 0x70, 0x10); // TSEG2 = 1
switch (baudRate) {
case (long)1000E3:
modifyRegister(REG_BTR1, 0x0f, 0x04);
modifyRegister(REG_BTR0, 0x3f, 4);
break;
case (long)500E3:
modifyRegister(REG_BTR1, 0x0f, 0x0c);
modifyRegister(REG_BTR0, 0x3f, 4);
break;
case (long)250E3:
modifyRegister(REG_BTR1, 0x0f, 0x0c);
modifyRegister(REG_BTR0, 0x3f, 9);
break;
case (long)200E3:
modifyRegister(REG_BTR1, 0x0f, 0x0c);
modifyRegister(REG_BTR0, 0x3f, 12);
break;
case (long)125E3:
modifyRegister(REG_BTR1, 0x0f, 0x0c);
modifyRegister(REG_BTR0, 0x3f, 19);
break;
case (long)100E3:
modifyRegister(REG_BTR1, 0x0f, 0x0c);
modifyRegister(REG_BTR0, 0x3f, 24);
break;
case (long)80E3:
modifyRegister(REG_BTR1, 0x0f, 0x0c);
modifyRegister(REG_BTR0, 0x3f, 30);
break;
case (long)50E3:
modifyRegister(REG_BTR1, 0x0f, 0x0c);
modifyRegister(REG_BTR0, 0x3f, 49);
break;
case (long)40E3:
modifyRegister(REG_BTR1, 0x0f, 0x0c);
modifyRegister(REG_BTR0, 0x3f, 62);
break;
case (long)20E3:
modifyRegister(REG_BTR1, 0x0f, 0x0c);
modifyRegister(REG_BTR0, 0x3f, 124);
break;
case (long)10E3:
modifyRegister(REG_BTR1, 0x0f, 0x0c);
modifyRegister(REG_BTR0, 0x3f, 249);
break;
default:
return 0;
break;
}
modifyRegister(REG_BTR1, 0x80, 0x80); // SAM = 1
writeRegister(REG_IER, 0xff); // enable all interrupts
// set filter to allow anything
writeRegister(REG_ACRn(0), 0x00);
writeRegister(REG_ACRn(1), 0x00);
writeRegister(REG_ACRn(2), 0x00);
writeRegister(REG_ACRn(3), 0x00);
writeRegister(REG_AMRn(0), 0xff);
writeRegister(REG_AMRn(1), 0xff);
writeRegister(REG_AMRn(2), 0xff);
writeRegister(REG_AMRn(3), 0xff);
modifyRegister(REG_OCR, 0x03, 0x02); // normal output mode
// reset error counters
writeRegister(REG_TXERR, 0x00);
writeRegister(REG_RXERR, 0x00);
// clear errors and interrupts
readRegister(REG_ECC);
readRegister(REG_IR);
// normal mode
modifyRegister(REG_MOD, 0x08, 0x08);
modifyRegister(REG_MOD, 0x17, 0x00);
return 1;
}
void ESP32SJA1000Class::end()
{
if (_intrHandle) {
esp_intr_free(_intrHandle);
_intrHandle = NULL;
}
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_CAN_CLK_EN);
CANControllerClass::end();
}
int ESP32SJA1000Class::endPacket()
{
if (!CANControllerClass::endPacket()) {
return 0;
}
// wait for TX buffer to free
while ((readRegister(REG_SR) & 0x04) != 0x04) {
yield();
}
int dataReg;
if (_txExtended) {
writeRegister(REG_EFF, 0x80 | (_txRtr ? 0x40 : 0x00) | (0x0f & _txLength));
writeRegister(REG_EFF + 1, _txId >> 21);
writeRegister(REG_EFF + 2, _txId >> 13);
writeRegister(REG_EFF + 3, _txId >> 5);
writeRegister(REG_EFF + 4, _txId << 3);
dataReg = REG_EFF + 5;
} else {
writeRegister(REG_SFF, (_txRtr ? 0x40 : 0x00) | (0x0f & _txLength));
writeRegister(REG_SFF + 1, _txId >> 3);
writeRegister(REG_SFF + 2, _txId << 5);
dataReg = REG_SFF + 3;
}
for (int i = 0; i < _txLength; i++) {
writeRegister(dataReg + i, _txData[i]);
}
if ( _loopback) {
// self reception request
modifyRegister(REG_CMR, 0x1f, 0x10);
} else {
// transmit request
modifyRegister(REG_CMR, 0x1f, 0x01);
}
// wait for TX complete
while ((readRegister(REG_SR) & 0x08) != 0x08) {
if (readRegister(REG_ECC) == 0xd9) {
modifyRegister(REG_CMR, 0x1f, 0x02); // error, abort
return 0;
}
yield();
}
return 1;
}
int ESP32SJA1000Class::parsePacket()
{
if ((readRegister(REG_SR) & 0x01) != 0x01) {
// no packet
return 0;
}
_rxExtended = (readRegister(REG_SFF) & 0x80) ? true : false;
_rxRtr = (readRegister(REG_SFF) & 0x40) ? true : false;
_rxDlc = (readRegister(REG_SFF) & 0x0f);
_rxIndex = 0;
int dataReg;
if (_rxExtended) {
_rxId = (readRegister(REG_EFF + 1) << 21) |
(readRegister(REG_EFF + 2) << 13) |
(readRegister(REG_EFF + 3) << 5) |
(readRegister(REG_EFF + 4) >> 3);
dataReg = REG_EFF + 5;
} else {
_rxId = (readRegister(REG_SFF + 1) << 3) | ((readRegister(REG_SFF + 2) >> 5) & 0x07);
dataReg = REG_SFF + 3;
}
if (_rxRtr) {
_rxLength = 0;
} else {
_rxLength = _rxDlc;
for (int i = 0; i < _rxLength; i++) {
_rxData[i] = readRegister(dataReg + i);
}
}
// release RX buffer
modifyRegister(REG_CMR, 0x04, 0x04);
return _rxDlc;
}
void ESP32SJA1000Class::onReceive(void(*callback)(int))
{
CANControllerClass::onReceive(callback);
if (_intrHandle) {
esp_intr_free(_intrHandle);
_intrHandle = NULL;
}
if (callback) {
esp_intr_alloc(ETS_CAN_INTR_SOURCE, 0, ESP32SJA1000Class::onInterrupt, this, &_intrHandle);
}
}
int ESP32SJA1000Class::filter(int id, int mask)
{
id &= 0x7ff;
mask = ~(mask & 0x7ff);
modifyRegister(REG_MOD, 0x17, 0x01); // reset
writeRegister(REG_ACRn(0), id >> 3);
writeRegister(REG_ACRn(1), id << 5);
writeRegister(REG_ACRn(2), 0x00);
writeRegister(REG_ACRn(3), 0x00);
writeRegister(REG_AMRn(0), mask >> 3);
writeRegister(REG_AMRn(1), (mask << 5) | 0x1f);
writeRegister(REG_AMRn(2), 0xff);
writeRegister(REG_AMRn(3), 0xff);
modifyRegister(REG_MOD, 0x17, 0x00); // normal
return 1;
}
int ESP32SJA1000Class::filterExtended(long id, long mask)
{
id &= 0x1FFFFFFF;
mask &= ~(mask & 0x1FFFFFFF);
modifyRegister(REG_MOD, 0x17, 0x01); // reset
writeRegister(REG_ACRn(0), id >> 21);
writeRegister(REG_ACRn(1), id >> 13);
writeRegister(REG_ACRn(2), id >> 5);
writeRegister(REG_ACRn(3), id << 5);
writeRegister(REG_AMRn(0), mask >> 21);
writeRegister(REG_AMRn(1), mask >> 13);
writeRegister(REG_AMRn(2), mask >> 5);
writeRegister(REG_AMRn(3), (mask << 5) | 0x1f);
modifyRegister(REG_MOD, 0x17, 0x00); // normal
return 1;
}
int ESP32SJA1000Class::observe()
{
modifyRegister(REG_MOD, 0x17, 0x01); // reset
modifyRegister(REG_MOD, 0x17, 0x02); // observe
return 1;
}
int ESP32SJA1000Class::loopback()
{
_loopback = true;
modifyRegister(REG_MOD, 0x17, 0x01); // reset
modifyRegister(REG_MOD, 0x17, 0x04); // self test mode
return 1;
}
int ESP32SJA1000Class::sleep()
{
modifyRegister(REG_MOD, 0x1f, 0x10);
return 1;
}
int ESP32SJA1000Class::wakeup()
{
modifyRegister(REG_MOD, 0x1f, 0x00);
return 1;
}
void ESP32SJA1000Class::setPins(int rx, int tx)
{
_rxPin = (gpio_num_t)rx;
_txPin = (gpio_num_t)tx;
}
void ESP32SJA1000Class::dumpRegisters(Stream& out)
{
for (int i = 0; i < 32; i++) {
byte b = readRegister(i);
out.print("0x");
if (i < 16) {
out.print('0');
}
out.print(i, HEX);
out.print(": 0x");
if (b < 16) {
out.print('0');
}
out.println(b, HEX);
}
}
void ESP32SJA1000Class::handleInterrupt()
{
uint8_t ir = readRegister(REG_IR);
if (ir & 0x01) {
// received packet, parse and call callback
parsePacket();
_onReceive(available());
}
}
uint8_t ESP32SJA1000Class::readRegister(uint8_t address)
{
volatile uint32_t* reg = (volatile uint32_t*)(REG_BASE + address * 4);
return *reg;
}
void ESP32SJA1000Class::modifyRegister(uint8_t address, uint8_t mask, uint8_t value)
{
volatile uint32_t* reg = (volatile uint32_t*)(REG_BASE + address * 4);
*reg = (*reg & ~mask) | value;
}
void ESP32SJA1000Class::writeRegister(uint8_t address, uint8_t value)
{
volatile uint32_t* reg = (volatile uint32_t*)(REG_BASE + address * 4);
*reg = value;
}
void ESP32SJA1000Class::onInterrupt(void* arg)
{
((ESP32SJA1000Class*)arg)->handleInterrupt();
}
ESP32SJA1000Class CAN;
#endif

@ -0,0 +1,65 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifdef ARDUINO_ARCH_ESP32
#ifndef ESP32_SJA1000_H
#define ESP32_SJA1000_H
#include "CANController.h"
#define DEFAULT_CAN_RX_PIN GPIO_NUM_4
#define DEFAULT_CAN_TX_PIN GPIO_NUM_5
class ESP32SJA1000Class : public CANControllerClass {
public:
ESP32SJA1000Class();
virtual ~ESP32SJA1000Class();
virtual int begin(long baudRate);
virtual void end();
virtual int endPacket();
virtual int parsePacket();
virtual void onReceive(void(*callback)(int));
using CANControllerClass::filter;
virtual int filter(int id, int mask);
using CANControllerClass::filterExtended;
virtual int filterExtended(long id, long mask);
virtual int observe();
virtual int loopback();
virtual int sleep();
virtual int wakeup();
void setPins(int rx, int tx);
void dumpRegisters(Stream& out);
private:
void reset();
void handleInterrupt();
uint8_t readRegister(uint8_t address);
void modifyRegister(uint8_t address, uint8_t mask, uint8_t value);
void writeRegister(uint8_t address, uint8_t value);
static void onInterrupt(void* arg);
private:
gpio_num_t _rxPin;
gpio_num_t _txPin;
bool _loopback;
intr_handle_t _intrHandle;
};
extern ESP32SJA1000Class CAN;
#endif
#endif

@ -0,0 +1,495 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef ARDUINO_ARCH_ESP32
#include "MCP2515.h"
#define REG_BFPCTRL 0x0c
#define REG_TXRTSCTRL 0x0d
#define REG_CANCTRL 0x0f
#define REG_CNF3 0x28
#define REG_CNF2 0x29
#define REG_CNF1 0x2a
#define REG_CANINTE 0x2b
#define REG_CANINTF 0x2c
#define FLAG_RXnIE(n) (0x01 << n)
#define FLAG_RXnIF(n) (0x01 << n)
#define FLAG_TXnIF(n) (0x04 << n)
#define REG_RXFnSIDH(n) (0x00 + (n * 4))
#define REG_RXFnSIDL(n) (0x01 + (n * 4))
#define REG_RXFnEID8(n) (0x02 + (n * 4))
#define REG_RXFnEID0(n) (0x03 + (n * 4))
#define REG_RXMnSIDH(n) (0x20 + (n * 0x04))
#define REG_RXMnSIDL(n) (0x21 + (n * 0x04))
#define REG_RXMnEID8(n) (0x22 + (n * 0x04))
#define REG_RXMnEID0(n) (0x23 + (n * 0x04))
#define REG_TXBnCTRL(n) (0x30 + (n * 0x10))
#define REG_TXBnSIDH(n) (0x31 + (n * 0x10))
#define REG_TXBnSIDL(n) (0x32 + (n * 0x10))
#define REG_TXBnEID8(n) (0x33 + (n * 0x10))
#define REG_TXBnEID0(n) (0x34 + (n * 0x10))
#define REG_TXBnDLC(n) (0x35 + (n * 0x10))
#define REG_TXBnD0(n) (0x36 + (n * 0x10))
#define REG_RXBnCTRL(n) (0x60 + (n * 0x10))
#define REG_RXBnSIDH(n) (0x61 + (n * 0x10))
#define REG_RXBnSIDL(n) (0x62 + (n * 0x10))
#define REG_RXBnEID8(n) (0x63 + (n * 0x10))
#define REG_RXBnEID0(n) (0x64 + (n * 0x10))
#define REG_RXBnDLC(n) (0x65 + (n * 0x10))
#define REG_RXBnD0(n) (0x66 + (n * 0x10))
#define FLAG_IDE 0x08
#define FLAG_SRR 0x10
#define FLAG_RTR 0x40
#define FLAG_EXIDE 0x08
#define FLAG_RXM0 0x20
#define FLAG_RXM1 0x40
MCP2515Class::MCP2515Class() :
CANControllerClass(),
_spiSettings(10E6, MSBFIRST, SPI_MODE0),
_csPin(MCP2515_DEFAULT_CS_PIN),
_intPin(MCP2515_DEFAULT_INT_PIN),
_clockFrequency(MCP2515_DEFAULT_CLOCK_FREQUENCY)
{
}
MCP2515Class::~MCP2515Class()
{
}
int MCP2515Class::begin(long baudRate)
{
CANControllerClass::begin(baudRate);
pinMode(_csPin, OUTPUT);
// start SPI
SPI.begin();
reset();
writeRegister(REG_CANCTRL, 0x80);
if (readRegister(REG_CANCTRL) != 0x80) {
return 0;
}
const struct {
long clockFrequency;
long baudRate;
uint8_t cnf[3];
} CNF_MAPPER[] = {
{ (long)8E6, (long)1000E3, { 0x00, 0x80, 0x00 } },
{ (long)8E6, (long)500E3, { 0x00, 0x90, 0x02 } },
{ (long)8E6, (long)250E3, { 0x00, 0xb1, 0x05 } },
{ (long)8E6, (long)200E3, { 0x00, 0xb4, 0x06 } },
{ (long)8E6, (long)125E3, { 0x01, 0xb1, 0x05 } },
{ (long)8E6, (long)100E3, { 0x01, 0xb4, 0x06 } },
{ (long)8E6, (long)80E3, { 0x01, 0xbf, 0x07 } },
{ (long)8E6, (long)50E3, { 0x03, 0xb4, 0x06 } },
{ (long)8E6, (long)40E3, { 0x03, 0xbf, 0x07 } },
{ (long)8E6, (long)20E3, { 0x07, 0xbf, 0x07 } },
{ (long)8E6, (long)10E3, { 0x0f, 0xbf, 0x07 } },
{ (long)8E6, (long)5E3, { 0x1f, 0xbf, 0x07 } },
{ (long)16E6, (long)1000E3, { 0x00, 0xd0, 0x82 } },
{ (long)16E6, (long)500E3, { 0x00, 0xf0, 0x86 } },
{ (long)16E6, (long)250E3, { 0x41, 0xf1, 0x85 } },
{ (long)16E6, (long)200E3, { 0x01, 0xfa, 0x87 } },
{ (long)16E6, (long)125E3, { 0x03, 0xf0, 0x86 } },
{ (long)16E6, (long)100E3, { 0x03, 0xfa, 0x87 } },
{ (long)16E6, (long)80E3, { 0x03, 0xff, 0x87 } },
{ (long)16E6, (long)50E3, { 0x07, 0xfa, 0x87 } },
{ (long)16E6, (long)40E3, { 0x07, 0xff, 0x87 } },
{ (long)16E6, (long)20E3, { 0x0f, 0xff, 0x87 } },
{ (long)16E6, (long)10E3, { 0x1f, 0xff, 0x87 } },
{ (long)16E6, (long)5E3, { 0x3f, 0xff, 0x87 } },
};
const uint8_t* cnf = NULL;
for (unsigned int i = 0; i < (sizeof(CNF_MAPPER) / sizeof(CNF_MAPPER[0])); i++) {
if (CNF_MAPPER[i].clockFrequency == _clockFrequency && CNF_MAPPER[i].baudRate == baudRate) {
cnf = CNF_MAPPER[i].cnf;
break;
}
}
if (cnf == NULL) {
return 0;
}
writeRegister(REG_CNF1, cnf[0]);
writeRegister(REG_CNF2, cnf[1]);
writeRegister(REG_CNF3, cnf[2]);
writeRegister(REG_CANINTE, FLAG_RXnIE(1) | FLAG_RXnIE(0));
writeRegister(REG_BFPCTRL, 0x00);
writeRegister(REG_TXRTSCTRL, 0x00);
writeRegister(REG_RXBnCTRL(0), FLAG_RXM1 | FLAG_RXM0);
writeRegister(REG_RXBnCTRL(1), FLAG_RXM1 | FLAG_RXM0);
writeRegister(REG_CANCTRL, 0x00);
if (readRegister(REG_CANCTRL) != 0x00) {
return 0;
}
return 1;
}
void MCP2515Class::end()
{
SPI.end();
CANControllerClass::end();
}
int MCP2515Class::endPacket()
{
if (!CANControllerClass::endPacket()) {
return 0;
}
int n = 0;
if (_txExtended) {
writeRegister(REG_TXBnSIDH(n), _txId >> 21);
writeRegister(REG_TXBnSIDL(n), (((_txId >> 18) & 0x07) << 5) | FLAG_EXIDE | ((_txId >> 16) & 0x03));
writeRegister(REG_TXBnEID8(n), (_txId >> 8) & 0xff);
writeRegister(REG_TXBnEID0(n), _txId & 0xff);
} else {
writeRegister(REG_TXBnSIDH(n), _txId >> 3);
writeRegister(REG_TXBnSIDL(n), _txId << 5);
writeRegister(REG_TXBnEID8(n), 0x00);
writeRegister(REG_TXBnEID0(n), 0x00);
}
if (_txRtr) {
writeRegister(REG_TXBnDLC(n), 0x40 | _txLength);
} else {
writeRegister(REG_TXBnDLC(n), _txLength);
for (int i = 0; i < _txLength; i++) {
writeRegister(REG_TXBnD0(n) + i, _txData[i]);
}
}
writeRegister(REG_TXBnCTRL(n), 0x08);
bool aborted = false;
while (readRegister(REG_TXBnCTRL(n)) & 0x08) {
if (readRegister(REG_TXBnCTRL(n)) & 0x10) {
// abort
aborted = true;
modifyRegister(REG_CANCTRL, 0x10, 0x10);
}
yield();
}
if (aborted) {
// clear abort command
modifyRegister(REG_CANCTRL, 0x10, 0x00);
}
modifyRegister(REG_CANINTF, FLAG_TXnIF(n), 0x00);
return (readRegister(REG_TXBnCTRL(n)) & 0x70) ? 0 : 1;
}
int MCP2515Class::parsePacket()
{
int n;
uint8_t intf = readRegister(REG_CANINTF);
if (intf & FLAG_RXnIF(0)) {
n = 0;
} else if (intf & FLAG_RXnIF(1)) {
n = 1;
} else {
_rxId = -1;
_rxExtended = false;
_rxRtr = false;
_rxLength = 0;
return 0;
}
_rxExtended = (readRegister(REG_RXBnSIDL(n)) & FLAG_IDE) ? true : false;
uint32_t idA = ((readRegister(REG_RXBnSIDH(n)) << 3) & 0x07f8) | ((readRegister(REG_RXBnSIDL(n)) >> 5) & 0x07);
if (_rxExtended) {
uint32_t idB = (((uint32_t)(readRegister(REG_RXBnSIDL(n)) & 0x03) << 16) & 0x30000) | ((readRegister(REG_RXBnEID8(n)) << 8) & 0xff00) | readRegister(REG_RXBnEID0(n));
_rxId = (idA << 18) | idB;
_rxRtr = (readRegister(REG_RXBnDLC(n)) & FLAG_RTR) ? true : false;
} else {
_rxId = idA;
_rxRtr = (readRegister(REG_RXBnSIDL(n)) & FLAG_SRR) ? true : false;
}
_rxDlc = readRegister(REG_RXBnDLC(n)) & 0x0f;
_rxIndex = 0;
if (_rxRtr) {
_rxLength = 0;
} else {
_rxLength = _rxDlc;
for (int i = 0; i < _rxLength; i++) {
_rxData[i] = readRegister(REG_RXBnD0(n) + i);
}
}
modifyRegister(REG_CANINTF, FLAG_RXnIF(n), 0x00);
return _rxDlc;
}
void MCP2515Class::onReceive(void(*callback)(int))
{
CANControllerClass::onReceive(callback);
pinMode(_intPin, INPUT);
if (callback) {
SPI.usingInterrupt(digitalPinToInterrupt(_intPin));
attachInterrupt(digitalPinToInterrupt(_intPin), MCP2515Class::onInterrupt, LOW);
} else {
detachInterrupt(digitalPinToInterrupt(_intPin));
#ifdef SPI_HAS_NOTUSINGINTERRUPT
SPI.notUsingInterrupt(digitalPinToInterrupt(_intPin));
#endif
}
}
int MCP2515Class::filter(int id, int mask)
{
id &= 0x7ff;
mask &= 0x7ff;
// config mode
writeRegister(REG_CANCTRL, 0x80);
if (readRegister(REG_CANCTRL) != 0x80) {
return 0;
}
for (int n = 0; n < 2; n++) {
// standard only
writeRegister(REG_RXBnCTRL(n), FLAG_RXM0);
writeRegister(REG_RXBnCTRL(n), FLAG_RXM0);
writeRegister(REG_RXMnSIDH(n), mask >> 3);
writeRegister(REG_RXMnSIDL(n), mask << 5);
writeRegister(REG_RXMnEID8(n), 0);
writeRegister(REG_RXMnEID0(n), 0);
}
for (int n = 0; n < 6; n++) {
writeRegister(REG_RXFnSIDH(n), id >> 3);
writeRegister(REG_RXFnSIDL(n), id << 5);
writeRegister(REG_RXFnEID8(n), 0);
writeRegister(REG_RXFnEID0(n), 0);
}
// normal mode
writeRegister(REG_CANCTRL, 0x00);
if (readRegister(REG_CANCTRL) != 0x00) {
return 0;
}
return 1;
}
int MCP2515Class::filterExtended(long id, long mask)
{
id &= 0x1FFFFFFF;
mask &= 0x1FFFFFFF;
// config mode
writeRegister(REG_CANCTRL, 0x80);
if (readRegister(REG_CANCTRL) != 0x80) {
return 0;
}
for (int n = 0; n < 2; n++) {
// extended only
writeRegister(REG_RXBnCTRL(n), FLAG_RXM1);
writeRegister(REG_RXBnCTRL(n), FLAG_RXM1);
writeRegister(REG_RXMnSIDH(n), mask >> 21);
writeRegister(REG_RXMnSIDL(n), (((mask >> 18) & 0x03) << 5) | FLAG_EXIDE | ((mask >> 16) & 0x03));
writeRegister(REG_RXMnEID8(n), (mask >> 8) & 0xff);
writeRegister(REG_RXMnEID0(n), mask & 0xff);
}
for (int n = 0; n < 6; n++) {
writeRegister(REG_RXFnSIDH(n), id >> 21);
writeRegister(REG_RXFnSIDL(n), (((id >> 18) & 0x03) << 5) | FLAG_EXIDE | ((id >> 16) & 0x03));
writeRegister(REG_RXFnEID8(n), (id >> 8) & 0xff);
writeRegister(REG_RXFnEID0(n), id & 0xff);
}
// normal mode
writeRegister(REG_CANCTRL, 0x00);
if (readRegister(REG_CANCTRL) != 0x00) {
return 0;
}
return 1;
}
int MCP2515Class::observe()
{
writeRegister(REG_CANCTRL, 0x80);
if (readRegister(REG_CANCTRL) != 0x80) {
return 0;
}
return 1;
}
int MCP2515Class::loopback()
{
writeRegister(REG_CANCTRL, 0x40);
if (readRegister(REG_CANCTRL) != 0x40) {
return 0;
}
return 1;
}
int MCP2515Class::sleep()
{
writeRegister(REG_CANCTRL, 0x01);
if (readRegister(REG_CANCTRL) != 0x01) {
return 0;
}
return 1;
}
int MCP2515Class::wakeup()
{
writeRegister(REG_CANCTRL, 0x00);
if (readRegister(REG_CANCTRL) != 0x00) {
return 0;
}
return 1;
}
void MCP2515Class::setPins(int cs, int irq)
{
_csPin = cs;
_intPin = irq;
}
void MCP2515Class::setSPIFrequency(uint32_t frequency)
{
_spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0);
}
void MCP2515Class::setClockFrequency(long clockFrequency)
{
_clockFrequency = clockFrequency;
}
void MCP2515Class::dumpRegisters(Stream& out)
{
for (int i = 0; i < 128; i++) {
byte b = readRegister(i);
out.print("0x");
if (i < 16) {
out.print('0');
}
out.print(i, HEX);
out.print(": 0x");
if (b < 16) {
out.print('0');
}
out.println(b, HEX);
}
}
void MCP2515Class::reset()
{
SPI.beginTransaction(_spiSettings);
digitalWrite(_csPin, LOW);
SPI.transfer(0xc0);
digitalWrite(_csPin, HIGH);
SPI.endTransaction();
delayMicroseconds(10);
}
void MCP2515Class::handleInterrupt()
{
if (readRegister(REG_CANINTF) == 0) {
return;
}
while (parsePacket()) {
_onReceive(available());
}
}
uint8_t MCP2515Class::readRegister(uint8_t address)
{
uint8_t value;
SPI.beginTransaction(_spiSettings);
digitalWrite(_csPin, LOW);
SPI.transfer(0x03);
SPI.transfer(address);
value = SPI.transfer(0x00);
digitalWrite(_csPin, HIGH);
SPI.endTransaction();
return value;
}
void MCP2515Class::modifyRegister(uint8_t address, uint8_t mask, uint8_t value)
{
SPI.beginTransaction(_spiSettings);
digitalWrite(_csPin, LOW);
SPI.transfer(0x05);
SPI.transfer(address);
SPI.transfer(mask);
SPI.transfer(value);
digitalWrite(_csPin, HIGH);
SPI.endTransaction();
}
void MCP2515Class::writeRegister(uint8_t address, uint8_t value)
{
SPI.beginTransaction(_spiSettings);
digitalWrite(_csPin, LOW);
SPI.transfer(0x02);
SPI.transfer(address);
SPI.transfer(value);
digitalWrite(_csPin, HIGH);
SPI.endTransaction();
}
void MCP2515Class::onInterrupt()
{
CAN.handleInterrupt();
}
MCP2515Class CAN;
#endif

@ -0,0 +1,77 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef ARDUINO_ARCH_ESP32
#ifndef MCP2515_H
#define MCP2515_H
#include <SPI.h>
#include "CANController.h"
#define MCP2515_DEFAULT_CLOCK_FREQUENCY 16e6
#if defined(ARDUINO_ARCH_SAMD) && defined(PIN_SPI_MISO) && defined(PIN_SPI_MOSI) && defined(PIN_SPI_SCK) && (PIN_SPI_MISO == 10) && (PIN_SPI_MOSI == 8) && (PIN_SPI_SCK == 9)
// Arduino MKR board: MKR CAN shield CS is pin 3, INT is pin 7
#define MCP2515_DEFAULT_CS_PIN 3
#define MCP2515_DEFAULT_INT_PIN 7
#else
#define MCP2515_DEFAULT_CS_PIN 10
#define MCP2515_DEFAULT_INT_PIN 2
#endif
class MCP2515Class : public CANControllerClass {
public:
MCP2515Class();
virtual ~MCP2515Class();
virtual int begin(long baudRate);
virtual void end();
virtual int endPacket();
virtual int parsePacket();
virtual void onReceive(void(*callback)(int));
using CANControllerClass::filter;
virtual int filter(int id, int mask);
using CANControllerClass::filterExtended;
virtual int filterExtended(long id, long mask);
virtual int observe();
virtual int loopback();
virtual int sleep();
virtual int wakeup();
void setPins(int cs = MCP2515_DEFAULT_CS_PIN, int irq = MCP2515_DEFAULT_INT_PIN);
void setSPIFrequency(uint32_t frequency);
void setClockFrequency(long clockFrequency);
void dumpRegisters(Stream& out);
private:
void reset();
void handleInterrupt();
uint8_t readRegister(uint8_t address);
void modifyRegister(uint8_t address, uint8_t mask, uint8_t value);
void writeRegister(uint8_t address, uint8_t value);
static void onInterrupt();
private:
SPISettings _spiSettings;
int _csPin;
int _intPin;
long _clockFrequency;
};
extern MCP2515Class CAN;
#endif
#endif

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

@ -0,0 +1,14 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:nanoatmega328]
platform = atmelavr
board = nanoatmega328
framework = arduino

@ -0,0 +1,219 @@
// Id 280,288,488,380,320,520,420
#include <CAN.h>
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
const long intervalRashod = 1000;
const float coefficentRashod = 0.9;
int previousDataRashodLH = 0; //для вычисления расхода
int currentDataRashodLH = 0; //для вычисления расхода
int raznica = 0; //для вычисления расхода
float rashodLH; // расход литров в час
float rashodL100current; // расход литров на 100 текущий
unsigned long time; // время для подсчета среднего расхода
float fuel, rashodL100AVG; // средний расход
float potrFuel; // потрачение топливо
float odo; // пробег за поездку
float zapasHoda;
int8_t coolant; // температура двигателя
uint8_t speed; // скорость по эбу
uint8_t fuelLevel; // уровень топлива
int16_t RPM; // обороты
int16_t odometr; // одометр
int torqueMoment; // крутящий момент
int temp; // температура за бортом
int oilTemp; // температура масла
int LightDash; // уровень подсветки
int pedalGas; // Нажатие на педаль газа
int air; // температура на впуске
bool cruise; // круиз контроль
bool handBrake; // ручник
bool antifrizeLow; // датчик уровня антифриза
void setup()
{
Serial.begin(9600);
while (!Serial);
Serial.println("CAN Receiver");
CAN.setClockFrequency(8e6);
// start the CAN bus at 500 kbps
if (!CAN.begin(500E3))
{
Serial.println("Starting CAN failed!");
while (1);
}
}
void loop()
{
// Чтение сообщений из CAN шины
if (CAN.parsePacket())
{
// Чтение идентификатора сообщения
uint32_t canId = CAN.packetId();
// Чтение данных из сообщения CAN (данные могут быть разной длины)
uint8_t len = CAN.packetDlc();
uint8_t data[8];
for (uint8_t i = 0; i < len; i++)
{
data[i] = CAN.read();
}
if (canId != 648 and canId != 800 and canId != 640 and canId != 1312 and canId != 896 and canId != 1160 and canId != 904 and canId != 1056 and canId != 1152)
{
Serial.println(canId);
}
// hex 1312-520,648-288,800-320,640-280,896-380,1160-488,904-388,1056-420,1152-480
// 1 640 -2 и 1
switch (canId)
{
case 1056:
//Serial.println(data[3] - 60);
temp = (data[2] - 100) / 2;
oilTemp = data[3] - 60;
LightDash = data[5];
break;
case 896:
air = data[1] * 0.75 - 48;
pedalGas = data[2];
break;
case 648:
coolant = data[1] * 0.75 - 48;
speed = data[3] / 0.72;
cruise = data[2];
break;
case 640:
RPM = (data[3] * 256 + data[4]) / 4;
torqueMoment = data[1] * 2;
break;
case 1152:
// Serial.println(data[3]);
currentMillis = millis();
if(currentMillis - previousMillis >= intervalRashod){
float timeInterval = (currentMillis - previousMillis)/1000;
//Serial.println(timeInterval);
time++;
previousMillis = currentMillis;
currentDataRashodLH = data[3];
if(currentDataRashodLH<previousDataRashodLH){
rashodLH = (currentDataRashodLH+raznica-127)*coefficentRashod/timeInterval;
} else {
rashodLH = (currentDataRashodLH-previousDataRashodLH)*coefficentRashod/timeInterval;
}
previousDataRashodLH = currentDataRashodLH;
raznica = 255-currentDataRashodLH;
if(speed > 0 ){
rashodL100current = rashodLH/speed*60;
fuel = fuel + rashodL100current;
} else {
}
rashodL100AVG = fuel/timeInterval;
potrFuel = potrFuel + rashodLH/3600;
odo = odo+speed/3600;
zapasHoda = fuelLevel/rashodL100AVG*100;
}
Serial.println((String)rashodLH+"/potr: "+potrFuel+"/zapas: "+zapasHoda+"/sr: "+rashodL100AVG);
break;
case 800:
//Serial.println(bitRead(data[1], 1)); // Ручник
// Serial.println(bitRead(data[1], 2)); // Ручник
// в 5 левый поворот 16 правый поворот 32 аварийка 48
// 0 -4 завел двигатель
// Serial.print("0: ");
// Serial.print(data[0]); // Дверь водителя
// Serial.print(" 1: ");
// Serial.print(data[1]); // Дверь водителя
// Serial.print(" 2: ");
// Serial.print(data[2]); // Дверь водителя
// Serial.print(" 3: ");
// Serial.print(data[3]); // Дверь водителя
// Serial.print(" 4: ");
// Serial.print(data[4]); // Дверь водителя
// Serial.print(" 5: ");
// Serial.print(data[5]); // Дверь водителя
// Serial.print(" 6: ");
// Serial.print(data[6]); // Дверь водителя
// Serial.print(" 7: ");
// Serial.print(data[7]); // Дверь водителя
// Serial.print(" 8: ");
// Serial.println(data[8]); // Дверь водителя
// Serial.println("1:"+bitRead(data[1], 1)); // Дверь водителя
// Serial.println("2:"+bitRead(data[1], 2)); // Дверь водителя
// Serial.println("3:"+bitRead(data[1], 3)); // Дверь водителя
// Serial.println("4:"+bitRead(data[1], 4)); // Дверь водителя
// Serial.println("5:"+bitRead(data[1], 5)); // Дверь водителя
// Serial.println("6:"+bitRead(data[1], 6)); // Дверь водителя
// Serial.println("7:"+bitRead(data[1], 7)); // Дверь водителя
// Serial.println("8:"+bitRead(data[1], 8)); // Дверь водителя
// Serial.println("1:"+bitRead(data[2], 1)); // Дверь водителя
// Serial.println("2:"+bitRead(data[2], 2)); // Дверь водителя
// Serial.println("3:"+bitRead(data[2], 3)); // Дверь водителя
// Serial.println("4:"+bitRead(data[2], 4)); // Дверь водителя
// Serial.println("5:"+bitRead(data[2], 5)); // Дверь водителя
// Serial.println("6:"+bitRead(data[2], 6)); // Дверь водителя
// Serial.println("7:"+bitRead(data[2], 7)); // Дверь водителя
// Serial.println("8:"+bitRead(data[2], 8)); // Дверь водителя
// Serial.println("1:"+bitRead(data[3], 1)); // Дверь водителя
// Serial.println("2:"+bitRead(data[3], 2)); // Дверь водителя
// Serial.println("3:"+bitRead(data[3], 3)); // Дверь водителя
// Serial.println("4:"+bitRead(data[3], 4)); // Дверь водителя
// Serial.println("5:"+bitRead(data[3], 5)); // Дверь водителя
// Serial.println("6:"+bitRead(data[3], 6)); // Дверь водителя
// Serial.println("7:"+bitRead(data[3], 7)); // Дверь водителя
// Serial.println("8:"+bitRead(data[3], 8)); // Дверь водителя
// Serial.println("1:"+bitRead(data[4], 1)); // Дверь водителя
// Serial.println("2:"+bitRead(data[4], 2)); // Дверь водителя
// Serial.println("3:"+bitRead(data[4], 3)); // Дверь водителя
// Serial.println("4:"+bitRead(data[4], 4)); // Дверь водителя
// Serial.println("5:"+bitRead(data[4], 5)); // Дверь водителя
// Serial.println("6:"+bitRead(data[4], 6)); // Дверь водителя
// Serial.println("7:"+bitRead(data[4], 7)); // Дверь водителя
// Serial.println("8:"+bitRead(data[4], 8)); // Дверь водителя
// Serial.println("1:"+bitRead(data[5], 1)); // Дверь водителя
// Serial.println("2:"+bitRead(data[5], 2)); // Дверь водителя
// Serial.println("3:"+bitRead(data[5], 3)); // Дверь водителя
// Serial.println("4:"+bitRead(data[5], 4)); // Дверь водителя
// Serial.println("5:"+bitRead(data[5], 5)); // Дверь водителя
// Serial.println("6:"+bitRead(data[5], 6)); // Дверь водителя
// Serial.println("7:"+bitRead(data[5], 7)); // Дверь водителя
// Serial.println("8:"+bitRead(data[5], 8)); // Дверь водителя
handBrake = bitRead(data[1], 1);
// Serial.print("Ручник: ");
// Serial.println(handBrake); // Ручник
fuelLevel = data[2];
// Serial.print("Уровень топлива: ");
// Serial.print(fuelLevel);
// Serial.println(" л.");
antifrizeLow = bitRead(data[0], 4);
// Serial.println(bitRead(data[5], 4)); // левый поворот
// Serial.println(bitRead(data[5], 5)); // правый поворот
// Serial.println(bitRead(data[1], 8)); // Ошибка тормозов
// Serial.println(bitRead(data[0], 3)); // Низкое давление масла
// Serial.println(bitRead(data[0], 4)); // Низкий уровень антифриза
break;
default:
// выполнить, если значение не совпадает ни с одним из case
break;
}
//Serial.println("Обороты:"+(String)RPM+" TEMP:"+coolant+" FuLev:"+fuelLevel+" Brake:"+handBrake+" Speed:"+speed+" Cruise:"+cruise+ " Расход:"+rashodLH+"l/h");
}
}

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
Loading…
Cancel
Save