From df19f10442892d17e68ac5179575c6b235e65da5 Mon Sep 17 00:00:00 2001 From: Bill1389 Date: Wed, 13 Dec 2023 22:46:41 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A7=D1=82=D0=B5=D0=BD=D0=B8=D0=B5=20can?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 + .vscode/extensions.json | 10 + include/README | 39 ++ lib/CAN/API.md | 272 ++++++++++ lib/CAN/LICENSE | 21 + lib/CAN/README.md | 69 +++ lib/CAN/examples/CANReceiver/CANReceiver.ino | 56 ++ .../CANReceiverCallback.ino | 59 +++ lib/CAN/examples/CANSender/CANSender.ino | 50 ++ .../examples/OBDII/EngineRPM/EngineRPM.ino | 57 ++ .../OBDII/SupportedPIDs/SupportedPIDs.ino | 83 +++ .../examples/OBDII/VINReader/VINReader.ino | 82 +++ lib/CAN/keywords.txt | 49 ++ lib/CAN/library.properties | 10 + lib/CAN/src/CAN.h | 13 + lib/CAN/src/CANController.cpp | 216 ++++++++ lib/CAN/src/CANController.h | 71 +++ lib/CAN/src/ESP32SJA1000.cpp | 427 +++++++++++++++ lib/CAN/src/ESP32SJA1000.h | 65 +++ lib/CAN/src/MCP2515.cpp | 495 ++++++++++++++++++ lib/CAN/src/MCP2515.h | 77 +++ lib/README | 46 ++ platformio.ini | 14 + src/main.cpp | 219 ++++++++ test/README | 11 + 25 files changed, 2516 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 include/README create mode 100644 lib/CAN/API.md create mode 100644 lib/CAN/LICENSE create mode 100644 lib/CAN/README.md create mode 100644 lib/CAN/examples/CANReceiver/CANReceiver.ino create mode 100644 lib/CAN/examples/CANReceiverCallback/CANReceiverCallback.ino create mode 100644 lib/CAN/examples/CANSender/CANSender.ino create mode 100644 lib/CAN/examples/OBDII/EngineRPM/EngineRPM.ino create mode 100644 lib/CAN/examples/OBDII/SupportedPIDs/SupportedPIDs.ino create mode 100644 lib/CAN/examples/OBDII/VINReader/VINReader.ino create mode 100644 lib/CAN/keywords.txt create mode 100644 lib/CAN/library.properties create mode 100644 lib/CAN/src/CAN.h create mode 100644 lib/CAN/src/CANController.cpp create mode 100644 lib/CAN/src/CANController.h create mode 100644 lib/CAN/src/ESP32SJA1000.cpp create mode 100644 lib/CAN/src/ESP32SJA1000.h create mode 100644 lib/CAN/src/MCP2515.cpp create mode 100644 lib/CAN/src/MCP2515.h create mode 100644 lib/README create mode 100644 platformio.ini create mode 100644 src/main.cpp create mode 100644 test/README diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/.vscode/extensions.json @@ -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" + ] +} diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -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 diff --git a/lib/CAN/API.md b/lib/CAN/API.md new file mode 100644 index 0000000..cbe9cac --- /dev/null +++ b/lib/CAN/API.md @@ -0,0 +1,272 @@ +# CAN API + +## Include Library + +```arduino +#include +``` + +## 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(); +``` diff --git a/lib/CAN/LICENSE b/lib/CAN/LICENSE new file mode 100644 index 0000000..97a0d8b --- /dev/null +++ b/lib/CAN/LICENSE @@ -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. diff --git a/lib/CAN/README.md b/lib/CAN/README.md new file mode 100644 index 0000000..4b534f1 --- /dev/null +++ b/lib/CAN/README.md @@ -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). diff --git a/lib/CAN/examples/CANReceiver/CANReceiver.ino b/lib/CAN/examples/CANReceiver/CANReceiver.ino new file mode 100644 index 0000000..fca49e4 --- /dev/null +++ b/lib/CAN/examples/CANReceiver/CANReceiver.ino @@ -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 + +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(); + } +} + diff --git a/lib/CAN/examples/CANReceiverCallback/CANReceiverCallback.ino b/lib/CAN/examples/CANReceiverCallback/CANReceiverCallback.ino new file mode 100644 index 0000000..cf2fa01 --- /dev/null +++ b/lib/CAN/examples/CANReceiverCallback/CANReceiverCallback.ino @@ -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 + +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(); +} + + diff --git a/lib/CAN/examples/CANSender/CANSender.ino b/lib/CAN/examples/CANSender/CANSender.ino new file mode 100644 index 0000000..92772cc --- /dev/null +++ b/lib/CAN/examples/CANSender/CANSender.ino @@ -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 + +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); +} + diff --git a/lib/CAN/examples/OBDII/EngineRPM/EngineRPM.ino b/lib/CAN/examples/OBDII/EngineRPM/EngineRPM.ino new file mode 100644 index 0000000..b1b8702 --- /dev/null +++ b/lib/CAN/examples/OBDII/EngineRPM/EngineRPM.ino @@ -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 + +// 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); +} diff --git a/lib/CAN/examples/OBDII/SupportedPIDs/SupportedPIDs.ino b/lib/CAN/examples/OBDII/SupportedPIDs/SupportedPIDs.ino new file mode 100644 index 0000000..94012b4 --- /dev/null +++ b/lib/CAN/examples/OBDII/SupportedPIDs/SupportedPIDs.ino @@ -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 + +// 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 +} diff --git a/lib/CAN/examples/OBDII/VINReader/VINReader.ino b/lib/CAN/examples/OBDII/VINReader/VINReader.ino new file mode 100644 index 0000000..e620b33 --- /dev/null +++ b/lib/CAN/examples/OBDII/VINReader/VINReader.ino @@ -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 + +// 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 +} diff --git a/lib/CAN/keywords.txt b/lib/CAN/keywords.txt new file mode 100644 index 0000000..985ce39 --- /dev/null +++ b/lib/CAN/keywords.txt @@ -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) +####################################### diff --git a/lib/CAN/library.properties b/lib/CAN/library.properties new file mode 100644 index 0000000..acf321f --- /dev/null +++ b/lib/CAN/library.properties @@ -0,0 +1,10 @@ +name=CAN +version=0.3.1 +author=Sandeep Mistry +maintainer=Sandeep Mistry +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 diff --git a/lib/CAN/src/CAN.h b/lib/CAN/src/CAN.h new file mode 100644 index 0000000..d8edc06 --- /dev/null +++ b/lib/CAN/src/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 diff --git a/lib/CAN/src/CANController.cpp b/lib/CAN/src/CANController.cpp new file mode 100644 index 0000000..0890eec --- /dev/null +++ b/lib/CAN/src/CANController.cpp @@ -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; +} diff --git a/lib/CAN/src/CANController.h b/lib/CAN/src/CANController.h new file mode 100644 index 0000000..cdaeb94 --- /dev/null +++ b/lib/CAN/src/CANController.h @@ -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 + +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 diff --git a/lib/CAN/src/ESP32SJA1000.cpp b/lib/CAN/src/ESP32SJA1000.cpp new file mode 100644 index 0000000..835628e --- /dev/null +++ b/lib/CAN/src/ESP32SJA1000.cpp @@ -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 diff --git a/lib/CAN/src/ESP32SJA1000.h b/lib/CAN/src/ESP32SJA1000.h new file mode 100644 index 0000000..b83cddc --- /dev/null +++ b/lib/CAN/src/ESP32SJA1000.h @@ -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 diff --git a/lib/CAN/src/MCP2515.cpp b/lib/CAN/src/MCP2515.cpp new file mode 100644 index 0000000..aa46051 --- /dev/null +++ b/lib/CAN/src/MCP2515.cpp @@ -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 diff --git a/lib/CAN/src/MCP2515.h b/lib/CAN/src/MCP2515.h new file mode 100644 index 0000000..2f0444f --- /dev/null +++ b/lib/CAN/src/MCP2515.h @@ -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 + +#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 diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/lib/README @@ -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 +#include + +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 diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..96febe7 --- /dev/null +++ b/platformio.ini @@ -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 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..8e2f210 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,219 @@ +// Id 280,288,488,380,320,520,420 + +#include + +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 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"); + } +} diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -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