├── .gitignore ├── images ├── AVR diagram.png ├── ESP diagram with level-shifter.png └── ESP diagram without level-shifter.png ├── test └── README ├── platformio.ini ├── lib └── README ├── include └── README ├── README.md ├── src ├── main.h └── main.cpp └── ino └── CAN-Sniffer.ino /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode 3 | -------------------------------------------------------------------------------- /images/AVR diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KruFFT/CAN-Sniffer/HEAD/images/AVR diagram.png -------------------------------------------------------------------------------- /images/ESP diagram with level-shifter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KruFFT/CAN-Sniffer/HEAD/images/ESP diagram with level-shifter.png -------------------------------------------------------------------------------- /images/ESP diagram without level-shifter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KruFFT/CAN-Sniffer/HEAD/images/ESP diagram without level-shifter.png -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Test Runner and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/en/latest/advanced/unit-testing/index.html 12 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | default_envs = nanoatmega328 13 | 14 | [env:nanoatmega328] 15 | platform = atmelavr 16 | board = nanoatmega328 17 | framework = arduino 18 | lib_deps = 19 | SPI 20 | https://github.com/Seeed-Studio/Seeed_Arduino_CAN.git#v2.3.3 21 | build_flags = 22 | -D MODE_AVR 23 | -D MODE_SERIAL 24 | ;-D MODE_SIMULATOR 25 | 26 | [env:nodemcuv2] 27 | platform = espressif8266 28 | board = nodemcuv2 29 | framework = arduino 30 | upload_speed = 921600 31 | lib_deps = 32 | SPI 33 | https://github.com/Seeed-Studio/Seeed_Arduino_CAN.git#v2.3.3 34 | https://github.com/esp8266/Arduino.git#3.1.2 35 | build_flags = 36 | -D MODE_ESP 37 | -D MODE_SERIAL 38 | ;-D MODE_WIRELESS 39 | ;-D MODE_SIMULATOR 40 | -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into the executable file. 4 | 5 | The source code of each library should be placed in a separate directory 6 | ("lib/your_library_name/[Code]"). 7 | 8 | For example, see the structure of the following example libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | Example contents of `src/main.c` using Foo and Bar: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | The PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries by scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the convention is to give header files names that end with `.h'. 29 | 30 | Read more about using header files in official GCC documentation: 31 | 32 | * Include Syntax 33 | * Include Operation 34 | * Once-Only Headers 35 | * Computed Includes 36 | 37 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CAN Sniffer 2 | 3 | [![Translate](https://img.shields.io/badge/Translate_to-ENGLISH-blue.svg?style=plastic)](https://github-com.translate.goog/KruFFT/CAN-Sniffer?_x_tr_sl=ru&_x_tr_tl=en) 4 | 5 | Код для **AVR (Arduino)** и **ESP8266** с использованием **PlatformIO**. 6 | 7 | [Статья о проекте](https://habr.com/ru/post/479672) 8 | 9 | [Приложение-компаньон для компьютера](https://github.com/KruFFT/wxCAN-Sniffer) 10 | 11 | ### Возможности 12 | - Изменение скорости подключения к CAN-шине во время работы 13 | - Передача данных на компьютер через последовательный порт или Wi-Fi 14 | - Встроенный симулятор пакетов для отладки (умеет отвечать на запросы) 15 | 16 | ### Описание 17 | Используется **VSCode** + **PlatformIO** - более гибкая, удобная и современная среда разработки по сравнению с **Arduino IDE**. 18 | 19 | Настройки платформ, используемых библиотек и параметры компиляции находятся в файле ```platformio.ini```. Формат простой и понятный. 20 | 21 | Для AVR работает передача через последовательный порт. Для ESP работает передача данных через последовательный порт и Wi-Fi. 22 | 23 | В ESP8266 крайне медленно работает передача данных через Wi-Fi маленькими пакетами, поэтому данные собираются по несколько пакетов в буфер и только после этого отправляются в сеть. 24 | 25 | Схемы подключения находятся в папке ```./images```. На практике проверялись варианты подключения AVR и ESP с микросхемой сдвига уровней сигналов 5В ↔ 3,3В. 26 | 27 | PS: Но если, всё же, необходим исходник для Arduino IDE, то INO-файл находится в папке ```./ino```. 28 | 29 | ### Проверялась работа 30 | - Arduino UNO 31 | - Arduino Nano 32 | - ESP-12F (D1 mini) 33 | - ESP8266 (совместимый с NodeMCU 1.0) 34 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | // Версия 2.3.1 2 | 3 | // 2024.05.12 версия 2.3.1 - добавление команд управления в симулятор 4 | // - исправлена потеря одного CAN-пакета при отправки данных в сеть 5 | // 2024.05.08 версия 2.3 - перевод статистики на little-endian 6 | // - команды управления подключением 7 | // - переход на VSCode + PlatformIO 8 | // - рефакторинг кода 9 | // 2024.08.19 версия 2.2 - отказ от использования ArduinoSTL, переход на обычный массив 10 | // 2024.06.15 версия 2.1 - используется библиотека CAN-BUS Shield by Seeed Studio версии 2.3.3 11 | // - используется библиотека ArduinoSTL by Mike Matera 1.3.3 (необходимо исправление: https://github.com/mike-matera/ArduinoSTL/issues/95) 12 | // - добавлен интервал времени между пакетами в мс 13 | // 2022.07.02 версия 2.0 - добавлена поддержка ESP и Wi-Fi 14 | // 2022.03.12 версия 1.2 - обновление библиотеки CAN-BUS Shield by Seeed Studio до версии 2.3.1 15 | // 2020.06.13 версия 1.1 - добавил очистку фильтров в setup() 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | // Параметры подключения к AVR 22 | #ifdef MODE_AVR 23 | // Для AVR шилд подключен следующим образом: 24 | // INT - D2 25 | // CS - D10 26 | // SCK - D13 27 | // MISO - D12 28 | // MOSI - D11 29 | #define CAN_INT 2 // сигнал INT на D2 30 | #define CAN_CS 10 // сигнал CS на D10 31 | #endif // MODE_AVR 32 | 33 | // Параметры подключения к ESP 34 | #ifdef MODE_ESP 35 | // Для ESP шилд подключен следующим образом: 36 | // INT - GPIO5 (D1) 37 | // CS - GPIO15 (D8) 38 | // SCK - GPIO14 (D5) 39 | // MISO - GPIO12 (D6) 40 | // MOSI - GPIO13 (D7) 41 | #define CAN_INT 5 // сигнал INT на GPIO5 (D1) 42 | #define CAN_CS 15 // сигнал CS на GPIO15 (D8) 43 | // Используется микросхема сдвига логических уровней 5В <-> 3,3В, сигнал управления EN на GPIO4 (D2) 44 | #define LEVEL_SHIFTER 4 45 | #endif // MODE_ESP 46 | 47 | #ifdef MODE_SERIAL 48 | #define SERIAL_SPEED 500000 // скорость последовательного порта 49 | #define PARSING_TIMEOUT 1000 // длительность разбора пакета до тайм-аута 50 | uint32_t parsingTime = 0; // время начала разбора пакета от компьютера 51 | #endif // MODE_SERIAL 52 | 53 | // Настройки беспроводного режима работы 54 | #ifdef MODE_WIRELESS 55 | #include 56 | #include 57 | 58 | // настройки точки доступа Wi-Fi 59 | const char* ssid = "CAR"; // название беспроводной сети 60 | const char* password = "1357924680"; // пароль к сети 61 | const IPAddress pcIp(192, 168, 1, 100); // IP-адрес компьютера 62 | const uint16_t udpPort = 0xAA55; // номер порта для обмена данными 63 | WiFiUDP UDP; // UDP-сокет 64 | 65 | #define UDP_BUFFER_SIZE 200 // размер буфера пакетов 66 | #define UDP_BUFFER_MINIMUM 19 // минимальный остаток буфера 67 | uint8_t udpOutBuffer[UDP_BUFFER_SIZE] = { 0 }; // буфер накопления пакетов на отправку на компьютер 68 | uint8_t* udpOutBufferPosition = udpOutBuffer; // указатель на этот буфер 69 | uint8_t udpInBuffer[UDP_BUFFER_SIZE] = { 0 }; // буфер приёма пакета от компьютера 70 | #endif // MODE_WIRELESS 71 | 72 | // Сигнатура и различные структуры, буферы и счётчики 73 | #define SIGNATURE 0xAA55AA55u // сигнатура передаваемых пакетов 74 | #define SERVICE_ID 0x7FFu // идентификатор пакета статистики и управления 75 | #define FRAME_INTERVALS_SIZE 100 // размер массива подсчёта интервалов между CAN-пакетами 76 | #define PARSING_COMPLETE 19 // состояние: пакет получен и удачно декодирован 77 | #define SWAP_BYTES_UINT32(value) ((((value) >> 24) & 0x000000FF) | (((value) >> 8) & 0x0000FF00) | (((value) << 8) & 0x00FF0000) | (((value) << 24) & 0xFF000000)) 78 | 79 | // Структура данных CAN-пакета 80 | struct CANFrame 81 | { 82 | uint32_t id; // идентификатор пакета 83 | uint16_t interval; // интервал между пакетами 84 | uint8_t length; // длина данных 85 | uint8_t data[8]; // данные пакета 86 | }; 87 | 88 | // Структура хранения данных отправляемых в последовательный порт или через Wi-Fi 89 | struct OutCANFrame 90 | { 91 | uint32_t signature = SWAP_BYTES_UINT32(SIGNATURE); 92 | CANFrame frame; 93 | }; 94 | 95 | uint8_t parsingStep = 0; // номер стадии разбора пришедших из последовательного порта данных 96 | CANFrame inCANFrame = { 0 }; // принятый из последовательного порта пакет для отправки в CAN-шину 97 | OutCANFrame outCANFrame; // буфер отправки данных в последовательный порт 98 | 99 | // Переменные для замера различных скоростных параметров 100 | uint16_t counterFPS = 0; // CAN-пакеты в секунду 101 | uint16_t counterBPS = 0; // байты в секунду 102 | uint32_t counterTime = millis(); // счётчик времени для подсчёта количества CAN-пакетов и байтов в секунду 103 | 104 | // Описание номера пакета и его времени для массива интервалов между пакетами 105 | struct FrameTime 106 | { 107 | uint32_t ID; 108 | uint32_t ms; 109 | }; 110 | 111 | // Массив интервалов между CAN-пакетами 112 | FrameTime frameTimes[FRAME_INTERVALS_SIZE] = {{ 0, 0 }}; 113 | uint16_t frameTimesAmount = 0; 114 | 115 | // Флаг для обработки прерывания поступления данных из CAN-шины и инициализация объекта CAN 116 | volatile bool canDataReceived = false; 117 | mcp2515_can CAN(CAN_CS); 118 | 119 | // Объявление всех функций 120 | void setup(); 121 | void loop(); 122 | void calculateSpeed(uint32_t currentTime); 123 | uint16_t getAndSaveIdInterval(uint32_t id, uint32_t currentTime); 124 | void sendPacketToPC(); 125 | void receivePacketFromPC(); 126 | void processCommand(); 127 | void CANConnect(uint16_t speed); 128 | void CANDisconnect(); 129 | void CANInterrupt(); 130 | 131 | // Параметры симулятора 132 | #ifdef MODE_SIMULATOR 133 | #define SIMULATOR_INTERVAL 5 134 | #define SIMULATOR_LONG_INTERVAL 10000 135 | uint32_t simulatorTime = millis(); 136 | uint32_t simulatorLongTime = millis(); 137 | uint8_t simulatorLongInc = 0x00; 138 | uint8_t simulatorInc = 0x00; 139 | uint8_t simulatorLongDec = 0xFF; 140 | uint8_t simulatorDec = 0xFF; 141 | bool simulatorExtended = false; 142 | bool simulatorConnected = false; 143 | #endif // MODE_SIMULATOR 144 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | // Инициализация 4 | void setup() 5 | { 6 | pinMode(CAN_INT, INPUT); 7 | 8 | #ifdef MODE_SERIAL 9 | Serial.begin(SERIAL_SPEED); 10 | #endif // MODE_SERIAL 11 | 12 | #ifdef MODE_WIRELESS 13 | // включение точки доступа 14 | WiFi.mode(WIFI_AP); 15 | WiFi.softAPConfig(IPAddress(192, 168, 1, 1), IPAddress(0, 0, 0, 0), IPAddress(255, 255, 255, 0)); 16 | WiFi.setSleep(false); 17 | while (!WiFi.softAP(ssid, password)) 18 | { 19 | delay(100); 20 | } 21 | 22 | // параметры DHCP - блок не работает 23 | /*auto& dhcp = WiFi.softAPDhcpServer(); 24 | dhcp.end(); 25 | dhcps_lease dhcpParameters; 26 | dhcpParameters.enable = true; 27 | IP4_ADDR(&dhcpParameters.start_ip, 192, 168, 1, 100); 28 | IP4_ADDR(&dhcpParameters.end_ip, 192, 168, 1, 254); 29 | dhcp.set_dhcps_lease(&dhcpParameters); 30 | dhcp.begin();*/ 31 | 32 | UDP.begin(udpPort); 33 | #endif // MODE_WIRELESS 34 | 35 | // включение микросхемы сдвига логических уровней 5В <-> 3,3В 36 | #ifdef LEVEL_SHIFTER 37 | pinMode(LEVEL_SHIFTER, OUTPUT); 38 | delay(100); 39 | digitalWrite(LEVEL_SHIFTER, HIGH); 40 | #endif // LEVEL_SHIFTER 41 | } 42 | 43 | // Основной цикл программы 44 | void loop() 45 | { 46 | #ifdef MODE_SIMULATOR 47 | if (simulatorConnected) 48 | { 49 | uint32_t currentTime = millis(); 50 | 51 | if (currentTime - simulatorLongTime >= SIMULATOR_LONG_INTERVAL) 52 | { 53 | simulatorLongTime = currentTime; 54 | simulatorLongInc++; 55 | simulatorLongDec--; 56 | } 57 | 58 | if (currentTime - simulatorTime >= SIMULATOR_INTERVAL) 59 | { 60 | simulatorTime = currentTime; 61 | simulatorInc++; 62 | simulatorDec--; 63 | // заполнение пакета сгенерированными данными 64 | outCANFrame.frame.id = 0xABCu; 65 | if (simulatorExtended) 66 | { 67 | outCANFrame.frame.id |= 0x80000000u; 68 | } 69 | outCANFrame.frame.interval = SIMULATOR_INTERVAL; 70 | outCANFrame.frame.length = 8; 71 | outCANFrame.frame.data[0] = simulatorLongInc; 72 | outCANFrame.frame.data[1] = simulatorInc; 73 | outCANFrame.frame.data[2] = simulatorLongDec; 74 | outCANFrame.frame.data[3] = simulatorDec; 75 | outCANFrame.frame.data[4] = simulatorLongInc; 76 | outCANFrame.frame.data[5] = simulatorInc; 77 | outCANFrame.frame.data[6] = simulatorLongDec; 78 | outCANFrame.frame.data[7] = simulatorDec; 79 | sendPacketToPC(); 80 | 81 | // замеры скоростных параметров 82 | calculateSpeed(currentTime); 83 | 84 | simulatorExtended = !simulatorExtended; 85 | } 86 | } 87 | #else // MODE_SIMULATOR 88 | // если в CAN-буфере есть данные... 89 | if (canDataReceived) 90 | { 91 | // сбросить флаг данных в буфере 92 | canDataReceived = false; 93 | 94 | while (CAN.checkReceive() == CAN_MSGAVAIL) 95 | { 96 | // чтение данных из буфера 97 | if (CAN.readMsgBuf(&outCANFrame.frame.length, outCANFrame.frame.data) == CAN_OK) 98 | { 99 | // получение идентификатора пакета (достоверный только после чтения сообщения из буфера) 100 | outCANFrame.frame.id = CAN.getCanId(); 101 | // обработка интервала между CAN-пакетами 102 | uint32_t currentTime = millis(); 103 | outCANFrame.frame.interval = getAndSaveIdInterval(outCANFrame.frame.id, currentTime); 104 | // для расширенного ID установить старший бит 105 | if (CAN.isExtendedFrame()) 106 | { 107 | outCANFrame.frame.id |= 0x80000000u; 108 | } 109 | 110 | sendPacketToPC(); 111 | 112 | // замеры скоростных параметров 113 | calculateSpeed(currentTime); 114 | } 115 | } 116 | } 117 | #endif 118 | 119 | receivePacketFromPC(); 120 | } 121 | 122 | // Вычисление и получение интервала для ID 123 | uint16_t getAndSaveIdInterval(uint32_t id, uint32_t currentTime) 124 | { 125 | // поиск по массиву 126 | for (size_t index = 0; index < frameTimesAmount; index++) 127 | { 128 | if (frameTimes[index].ID == id) 129 | { 130 | uint32_t interval = currentTime - frameTimes[index].ms; 131 | frameTimes[index].ms = currentTime; 132 | return (interval < 65536) ? (uint16_t)interval : 0; 133 | } 134 | } 135 | // пакет не найден - надо добавить 136 | if (frameTimesAmount < FRAME_INTERVALS_SIZE) 137 | { 138 | frameTimes[frameTimesAmount].ID = id; 139 | frameTimes[frameTimesAmount].ms = currentTime; 140 | frameTimesAmount++; 141 | } 142 | return 0; 143 | } 144 | 145 | // Подсчёт скоростных показателей CAN-пакетов 146 | void calculateSpeed(uint32_t currentTime) 147 | { 148 | if (currentTime - counterTime >= 1000) 149 | { 150 | counterTime = currentTime; 151 | outCANFrame.frame.id = SERVICE_ID; 152 | outCANFrame.frame.length = 4; 153 | // количество пакетов в секунду, примерная скорость около 745 пакетов в секунду 154 | *(uint16_t*)(&(outCANFrame.frame.data[0])) = counterFPS; 155 | // количество байтов в секунду, примерная скорость около 9200 байтов в секунду 156 | *(uint16_t*)(&(outCANFrame.frame.data[2])) = counterBPS; 157 | 158 | sendPacketToPC(); 159 | 160 | counterFPS = 0; 161 | counterBPS = 0; 162 | } 163 | 164 | counterFPS++; 165 | // ID пакета (4 байта) + длина (1 байт) + данные 166 | counterBPS += (5 + outCANFrame.frame.length); 167 | } 168 | 169 | // Отправка полученного CAN-пакета в последовательный порт или Wi-Fi 170 | void sendPacketToPC() 171 | { 172 | // сигнатура (4 байта) + ID пакета (4 байта) + интервал (2 байта) + длина (1 байт) = 11 байт 173 | size_t dataLength = 11 + outCANFrame.frame.length; 174 | 175 | #ifdef MODE_SERIAL 176 | Serial.write((uint8_t*)&outCANFrame, dataLength); 177 | #endif // MODE_SERIAL 178 | 179 | #ifdef MODE_WIRELESS 180 | // накопление данных в буфере, это необходимо для увеличения пропускной способности 181 | memcpy(udpOutBufferPosition, &outCANFrame, dataLength); 182 | udpOutBufferPosition += dataLength; 183 | // если буфер заполнен - отправить порцию данных в сеть 184 | if (UDP_BUFFER_SIZE - (udpOutBufferPosition - udpOutBuffer) < UDP_BUFFER_MINIMUM) 185 | { 186 | // отправить данные 187 | UDP.beginPacket(pcIp, udpPort); 188 | UDP.write(udpOutBuffer, udpOutBufferPosition - udpOutBuffer); 189 | UDP.endPacket(); 190 | // сброс буфера отправки пакетов 191 | udpOutBufferPosition = udpOutBuffer; 192 | } 193 | #endif // MODE_WIRELESS 194 | } 195 | 196 | // Приём и разбор данных из последовательного порта или Wi-Fi 197 | void receivePacketFromPC() 198 | { 199 | #ifdef MODE_SERIAL 200 | if (Serial.available()) 201 | { 202 | if (parsingStep >= 0 && parsingStep <= 18) 203 | { 204 | int nextByte = Serial.read(); 205 | // сделан поочерёдный побайтовый разбор данных из последовательного порта, 206 | // так как парсинг всего буфера сразу после .readBytes(..) работал нестабильно. 207 | // идея такая: последовательно читая каждый байт, найти сигнатуру 208 | // потом заполнить ID-пакета и далее длину пакета 209 | // далее заполнить входной массив количеством байтов до длины пакета 210 | switch (parsingStep) 211 | { 212 | // поиск сигнатуры с учётом того, что придёт она с обратным порядком байтов (big-endian) 213 | case 0: 214 | parsingTime = millis(); 215 | if (nextByte == ((SIGNATURE >> 24) & 0xFFu)) parsingStep++; else parsingStep = 0; 216 | break; 217 | 218 | // поиск сигнатуры 219 | case 1: 220 | if (nextByte == ((SIGNATURE >> 16) & 0xFFu)) parsingStep++; else parsingStep = 0; 221 | break; 222 | 223 | // поиск сигнатуры 224 | case 2: 225 | if (nextByte == ((SIGNATURE >> 8) & 0xFFu)) parsingStep++; else parsingStep = 0; 226 | break; 227 | 228 | // поиск сигнатуры 229 | case 3: 230 | if (nextByte == ((SIGNATURE) & 0xFFu)) parsingStep++; else parsingStep = 0; 231 | break; 232 | 233 | // получение ID-пакета 234 | case 4 ... 7: 235 | *((uint8_t*)&inCANFrame.id + parsingStep - 4) = (uint8_t)nextByte; 236 | parsingStep++; 237 | break; 238 | 239 | // пропуск зарезервированного слова 240 | case 8 ... 9: 241 | parsingStep++; 242 | break; 243 | 244 | // получение длины данных 245 | case 10: 246 | if (nextByte >= 0 && nextByte <= 8) 247 | { 248 | inCANFrame.length = (uint8_t)nextByte; 249 | parsingStep++; 250 | } 251 | else 252 | parsingStep = 0; 253 | break; 254 | 255 | // получение непосредственно самих данных пакета 256 | case 11 ... 18: 257 | if (parsingStep - 11 < inCANFrame.length) 258 | { 259 | inCANFrame.data[parsingStep - 11] = (uint8_t)nextByte; 260 | parsingStep++; 261 | } 262 | else 263 | parsingStep = PARSING_COMPLETE; 264 | break; 265 | } 266 | } 267 | } 268 | else 269 | { 270 | // если шла одна из стадий разбора пакета от компьютера, данных нет и время истекло - прекратить разбор 271 | if (parsingStep && (millis() - parsingTime) > PARSING_TIMEOUT) 272 | { 273 | parsingStep = 0; 274 | } 275 | } 276 | #endif //MODE_SERIAL 277 | 278 | #ifdef MODE_WIRELESS 279 | int packetSize = UDP.parsePacket(); 280 | if (packetSize)// && parsingStep == 0) 281 | { 282 | int length = UDP.read(udpInBuffer, UDP_BUFFER_SIZE); 283 | if (length >= 11 && length <= 19) 284 | { 285 | uint8_t* receivedData = udpInBuffer; 286 | // проверка сигнатуры 287 | uint32_t signature = *(uint32_t*)receivedData; 288 | if (signature == SWAP_BYTES_UINT32(SIGNATURE)) 289 | { 290 | // пропустить сигнатуру и скопировать все остальные данные пакета 291 | receivedData += 4; 292 | memcpy(&inCANFrame, receivedData, 15); 293 | parsingStep = PARSING_COMPLETE; 294 | } 295 | } 296 | } 297 | #endif // MODE_WIRELESS 298 | 299 | // есть удачно разобранный пакет 300 | if (parsingStep == PARSING_COMPLETE) 301 | { 302 | // получен CAN-пакет от компьютера 303 | parsingStep = 0; 304 | // проверка управляющих пакетов 305 | if (inCANFrame.id == SERVICE_ID) 306 | { 307 | processCommand(); 308 | } 309 | else 310 | { 311 | #ifdef MODE_SIMULATOR 312 | // немного изенить данные и отправить обратно в компьютер 313 | outCANFrame.frame = inCANFrame; 314 | outCANFrame.frame.id += 8; 315 | outCANFrame.frame.interval = 123; 316 | for (size_t index = 0; index < inCANFrame.length; index++) 317 | { 318 | outCANFrame.frame.data[index]++; 319 | } 320 | sendPacketToPC(); 321 | #else // MODE_SIMULATOR 322 | // отправка пакета в CAN-шину 323 | CAN.sendMsgBuf(inCANFrame.id, (inCANFrame.id & 0x80000000u) ? 1 : 0, inCANFrame.length, inCANFrame.data); 324 | #endif 325 | } 326 | } 327 | } 328 | 329 | // Обработка управляющих пакетов 330 | void processCommand() 331 | { 332 | switch (inCANFrame.data[0]) 333 | { 334 | // отключиться от CAN-шины 335 | case 0: 336 | CANDisconnect(); 337 | break; 338 | 339 | // подключиться к CAN-шине на указанной скорости 340 | case 1: 341 | uint16_t speed = *(uint16_t*)(&(inCANFrame.data[1])); 342 | CANConnect(speed); 343 | break; 344 | } 345 | } 346 | 347 | // Подключиться к CAN-шине 348 | void CANConnect(uint16_t speed) 349 | { 350 | #ifdef MODE_SIMULATOR 351 | simulatorConnected = true; 352 | #else // MODE_SIMULATOR 353 | // подключить обработчик прерывания о поступлении данных по спадающему уровню сигнала 354 | attachInterrupt(digitalPinToInterrupt(CAN_INT), CANInterrupt, FALLING); 355 | 356 | MCP_BITTIME_SETUP canSpeed; 357 | switch (speed) 358 | { 359 | case 100: canSpeed = CAN_100KBPS; break; 360 | case 125: canSpeed = CAN_125KBPS; break; 361 | case 200: canSpeed = CAN_200KBPS; break; 362 | case 250: canSpeed = CAN_250KBPS; break; 363 | case 500: canSpeed = CAN_500KBPS; break; 364 | case 1000: canSpeed = CAN_1000KBPS; break; 365 | default: canSpeed = CAN_500KBPS; break; 366 | } 367 | 368 | // настройка скорости обмена и частоты кварца 369 | while (CAN.begin(canSpeed, MCP_8MHz) != CAN_OK) 370 | { 371 | delay(100); 372 | } 373 | 374 | // типы фильтров 375 | #define STD 0 376 | #define EXT 1 377 | 378 | // сброс фильтров 379 | CAN.init_Filt(0, STD, 0); 380 | CAN.init_Filt(1, STD, 0); 381 | CAN.init_Filt(2, STD, 0); 382 | CAN.init_Filt(3, STD, 0); 383 | CAN.init_Filt(4, STD, 0); 384 | CAN.init_Filt(5, STD, 0); 385 | 386 | // очистка масок 387 | CAN.init_Mask(0, STD, 0); 388 | CAN.init_Mask(1, STD, 0); 389 | #endif 390 | } 391 | 392 | // Отключиться от CAN-шины 393 | void CANDisconnect() 394 | { 395 | #ifdef MODE_SIMULATOR 396 | simulatorConnected = false; 397 | #else // MODE_SIMULATOR 398 | detachInterrupt(digitalPinToInterrupt(CAN_INT)); 399 | #endif 400 | } 401 | 402 | // Обработчик прерывания поступивших данных 403 | #ifdef MODE_ESP 404 | IRAM_ATTR 405 | #endif 406 | void CANInterrupt() 407 | { 408 | canDataReceived = true; // установить флаг наличия данных в буфере 409 | } 410 | -------------------------------------------------------------------------------- /ino/CAN-Sniffer.ino: -------------------------------------------------------------------------------- 1 | // Версия 2.3.1 2 | 3 | // 2024.05.12 версия 2.3.1 - добавление команд управления в симулятор 4 | // - исправлена потеря одного CAN-пакета при отправки данных в сеть 5 | // 2024.05.08 версия 2.3 - перевод статистики на little-endian 6 | // - команды управления подключением 7 | // - переход на VSCode + PlatformIO 8 | // - рефакторинг кода 9 | // 2024.08.19 версия 2.2 - отказ от использования ArduinoSTL, переход на обычный массив 10 | // 2024.06.15 версия 2.1 - используется библиотека CAN-BUS Shield by Seeed Studio версии 2.3.3 11 | // - используется библиотека ArduinoSTL by Mike Matera 1.3.3 (необходимо исправление: https://github.com/mike-matera/ArduinoSTL/issues/95) 12 | // - добавлен интервал времени между пакетами в мс 13 | // 2022.07.02 версия 2.0 - добавлена поддержка ESP и Wi-Fi 14 | // 2022.03.12 версия 1.2 - обновление библиотеки CAN-BUS Shield by Seeed Studio до версии 2.3.1 15 | // 2020.06.13 версия 1.1 - добавил очистку фильтров в setup() 16 | 17 | // AVR или ESP 18 | #define MODE_AVR 19 | //#define MODE_ESP 20 | 21 | // режим работы последовательный, Wi-Fi 22 | #define MODE_SERIAL 23 | //#define MODE_WIRELESS 24 | 25 | // включение симулятора для отладки 26 | //#define MODE_SIMULATOR 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | // Параметры подключения к AVR 33 | #ifdef MODE_AVR 34 | // Для AVR шилд подключен следующим образом: 35 | // INT - D2 36 | // CS - D10 37 | // SCK - D13 38 | // MISO - D12 39 | // MOSI - D11 40 | #define CAN_INT 2 // сигнал INT на D2 41 | #define CAN_CS 10 // сигнал CS на D10 42 | #endif // MODE_AVR 43 | 44 | // Параметры подключения к ESP 45 | #ifdef MODE_ESP 46 | // Для ESP шилд подключен следующим образом: 47 | // INT - GPIO5 (D1) 48 | // CS - GPIO15 (D8) 49 | // SCK - GPIO14 (D5) 50 | // MISO - GPIO12 (D6) 51 | // MOSI - GPIO13 (D7) 52 | #define CAN_INT 5 // сигнал INT на GPIO5 (D1) 53 | #define CAN_CS 15 // сигнал CS на GPIO15 (D8) 54 | // Используется микросхема сдвига логических уровней 5В <-> 3,3В, сигнал управления EN на GPIO4 (D2) 55 | #define LEVEL_SHIFTER 4 56 | #endif // MODE_ESP 57 | 58 | #ifdef MODE_SERIAL 59 | #define SERIAL_SPEED 500000 // скорость последовательного порта 60 | #define PARSING_TIMEOUT 1000 // длительность разбора пакета до тайм-аута 61 | uint32_t parsingTime = 0; // время начала разбора пакета от компьютера 62 | #endif // MODE_SERIAL 63 | 64 | // Настройки беспроводного режима работы 65 | #ifdef MODE_WIRELESS 66 | #include 67 | #include 68 | 69 | // настройки точки доступа Wi-Fi 70 | const char* ssid = "CAR"; // название беспроводной сети 71 | const char* password = "1357924680"; // пароль к сети 72 | const IPAddress pcIp(192, 168, 1, 100); // IP-адрес компьютера 73 | const uint16_t udpPort = 0xAA55; // номер порта для обмена данными 74 | WiFiUDP UDP; // UDP-сокет 75 | 76 | #define UDP_BUFFER_SIZE 200 // размер буфера пакетов 77 | #define UDP_BUFFER_MINIMUM 19 // минимальный остаток буфера 78 | uint8_t udpOutBuffer[UDP_BUFFER_SIZE] = { 0 }; // буфер накопления пакетов на отправку на компьютер 79 | uint8_t* udpOutBufferPosition = udpOutBuffer; // указатель на этот буфер 80 | uint8_t udpInBuffer[UDP_BUFFER_SIZE] = { 0 }; // буфер приёма пакета от компьютера 81 | #endif // MODE_WIRELESS 82 | 83 | // Сигнатура и различные структуры, буферы и счётчики 84 | #define SIGNATURE 0xAA55AA55u // сигнатура передаваемых пакетов 85 | #define SERVICE_ID 0x7FFu // идентификатор пакета статистики и управления 86 | #define FRAME_INTERVALS_SIZE 100 // размер массива подсчёта интервалов между CAN-пакетами 87 | #define PARSING_COMPLETE 19 // состояние: пакет получен и удачно декодирован 88 | #define SWAP_BYTES_UINT32(value) ((((value) >> 24) & 0x000000FF) | (((value) >> 8) & 0x0000FF00) | (((value) << 8) & 0x00FF0000) | (((value) << 24) & 0xFF000000)) 89 | 90 | // Структура данных CAN-пакета 91 | struct CANFrame 92 | { 93 | uint32_t id; // идентификатор пакета 94 | uint16_t interval; // интервал между пакетами 95 | uint8_t length; // длина данных 96 | uint8_t data[8]; // данные пакета 97 | }; 98 | 99 | // Структура хранения данных отправляемых в последовательный порт или через Wi-Fi 100 | struct OutCANFrame 101 | { 102 | uint32_t signature = SWAP_BYTES_UINT32(SIGNATURE); 103 | CANFrame frame; 104 | }; 105 | 106 | uint8_t parsingStep = 0; // номер стадии разбора пришедших из последовательного порта данных 107 | CANFrame inCANFrame = { 0 }; // принятый из последовательного порта пакет для отправки в CAN-шину 108 | OutCANFrame outCANFrame; // буфер отправки данных в последовательный порт 109 | 110 | // Переменные для замера различных скоростных параметров 111 | uint16_t counterFPS = 0; // CAN-пакеты в секунду 112 | uint16_t counterBPS = 0; // байты в секунду 113 | uint32_t counterTime = millis(); // счётчик времени для подсчёта количества CAN-пакетов и байтов в секунду 114 | 115 | // Описание номера пакета и его времени для массива интервалов между пакетами 116 | struct FrameTime 117 | { 118 | uint32_t ID; 119 | uint32_t ms; 120 | }; 121 | 122 | // Массив интервалов между CAN-пакетами 123 | FrameTime frameTimes[FRAME_INTERVALS_SIZE] = {{ 0, 0 }}; 124 | uint16_t frameTimesAmount = 0; 125 | 126 | // Флаг для обработки прерывания поступления данных из CAN-шины и инициализация объекта CAN 127 | volatile bool canDataReceived = false; 128 | mcp2515_can CAN(CAN_CS); 129 | 130 | // Параметры симулятора 131 | #ifdef MODE_SIMULATOR 132 | #define SIMULATOR_INTERVAL 5 133 | #define SIMULATOR_LONG_INTERVAL 10000 134 | uint32_t simulatorTime = millis(); 135 | uint32_t simulatorLongTime = millis(); 136 | uint8_t simulatorLongInc = 0x00; 137 | uint8_t simulatorInc = 0x00; 138 | uint8_t simulatorLongDec = 0xFF; 139 | uint8_t simulatorDec = 0xFF; 140 | bool simulatorExtended = false; 141 | bool simulatorConnected = false; 142 | #endif // MODE_SIMULATOR 143 | 144 | // Инициализация 145 | void setup() 146 | { 147 | pinMode(CAN_INT, INPUT); 148 | 149 | #ifdef MODE_SERIAL 150 | Serial.begin(SERIAL_SPEED); 151 | #endif // MODE_SERIAL 152 | 153 | #ifdef MODE_WIRELESS 154 | // включение точки доступа 155 | WiFi.mode(WIFI_AP); 156 | WiFi.softAPConfig(IPAddress(192, 168, 1, 1), IPAddress(0, 0, 0, 0), IPAddress(255, 255, 255, 0)); 157 | WiFi.setSleep(false); 158 | while (!WiFi.softAP(ssid, password)) 159 | { 160 | delay(100); 161 | } 162 | 163 | // параметры DHCP - блок не работает 164 | /*auto& dhcp = WiFi.softAPDhcpServer(); 165 | dhcp.end(); 166 | dhcps_lease dhcpParameters; 167 | dhcpParameters.enable = true; 168 | IP4_ADDR(&dhcpParameters.start_ip, 192, 168, 1, 100); 169 | IP4_ADDR(&dhcpParameters.end_ip, 192, 168, 1, 254); 170 | dhcp.set_dhcps_lease(&dhcpParameters); 171 | dhcp.begin();*/ 172 | 173 | UDP.begin(udpPort); 174 | #endif // MODE_WIRELESS 175 | 176 | // включение микросхемы сдвига логических уровней 5В <-> 3,3В 177 | #ifdef LEVEL_SHIFTER 178 | pinMode(LEVEL_SHIFTER, OUTPUT); 179 | delay(100); 180 | digitalWrite(LEVEL_SHIFTER, HIGH); 181 | #endif // LEVEL_SHIFTER 182 | } 183 | 184 | // Основной цикл программы 185 | void loop() 186 | { 187 | #ifdef MODE_SIMULATOR 188 | if (simulatorConnected) 189 | { 190 | uint32_t currentTime = millis(); 191 | 192 | if (currentTime - simulatorLongTime >= SIMULATOR_LONG_INTERVAL) 193 | { 194 | simulatorLongTime = currentTime; 195 | simulatorLongInc++; 196 | simulatorLongDec--; 197 | } 198 | 199 | if (currentTime - simulatorTime >= SIMULATOR_INTERVAL) 200 | { 201 | simulatorTime = currentTime; 202 | simulatorInc++; 203 | simulatorDec--; 204 | // заполнение пакета сгенерированными данными 205 | outCANFrame.frame.id = 0xABCu; 206 | if (simulatorExtended) 207 | { 208 | outCANFrame.frame.id |= 0x80000000u; 209 | } 210 | outCANFrame.frame.interval = SIMULATOR_INTERVAL; 211 | outCANFrame.frame.length = 8; 212 | outCANFrame.frame.data[0] = simulatorLongInc; 213 | outCANFrame.frame.data[1] = simulatorInc; 214 | outCANFrame.frame.data[2] = simulatorLongDec; 215 | outCANFrame.frame.data[3] = simulatorDec; 216 | outCANFrame.frame.data[4] = simulatorLongInc; 217 | outCANFrame.frame.data[5] = simulatorInc; 218 | outCANFrame.frame.data[6] = simulatorLongDec; 219 | outCANFrame.frame.data[7] = simulatorDec; 220 | sendPacketToPC(); 221 | 222 | // замеры скоростных параметров 223 | calculateSpeed(currentTime); 224 | 225 | simulatorExtended = !simulatorExtended; 226 | } 227 | } 228 | #else // MODE_SIMULATOR 229 | // если в CAN-буфере есть данные... 230 | if (canDataReceived) 231 | { 232 | // сбросить флаг данных в буфере 233 | canDataReceived = false; 234 | 235 | while (CAN.checkReceive() == CAN_MSGAVAIL) 236 | { 237 | // чтение данных из буфера 238 | if (CAN.readMsgBuf(&outCANFrame.frame.length, outCANFrame.frame.data) == CAN_OK) 239 | { 240 | // получение идентификатора пакета (достоверный только после чтения сообщения из буфера) 241 | outCANFrame.frame.id = CAN.getCanId(); 242 | // обработка интервала между CAN-пакетами 243 | uint32_t currentTime = millis(); 244 | outCANFrame.frame.interval = getAndSaveIdInterval(outCANFrame.frame.id, currentTime); 245 | // для расширенного ID установить старший бит 246 | if (CAN.isExtendedFrame()) 247 | { 248 | outCANFrame.frame.id |= 0x80000000u; 249 | } 250 | 251 | sendPacketToPC(); 252 | 253 | // замеры скоростных параметров 254 | calculateSpeed(currentTime); 255 | } 256 | } 257 | } 258 | #endif 259 | 260 | receivePacketFromPC(); 261 | } 262 | 263 | // Вычисление и получение интервала для ID 264 | uint16_t getAndSaveIdInterval(uint32_t id, uint32_t currentTime) 265 | { 266 | // поиск по массиву 267 | for (size_t index = 0; index < frameTimesAmount; index++) 268 | { 269 | if (frameTimes[index].ID == id) 270 | { 271 | uint32_t interval = currentTime - frameTimes[index].ms; 272 | frameTimes[index].ms = currentTime; 273 | return (interval < 65536) ? (uint16_t)interval : 0; 274 | } 275 | } 276 | // пакет не найден - надо добавить 277 | if (frameTimesAmount < FRAME_INTERVALS_SIZE) 278 | { 279 | frameTimes[frameTimesAmount].ID = id; 280 | frameTimes[frameTimesAmount].ms = currentTime; 281 | frameTimesAmount++; 282 | } 283 | return 0; 284 | } 285 | 286 | // Подсчёт скоростных показателей CAN-пакетов 287 | void calculateSpeed(uint32_t currentTime) 288 | { 289 | if (currentTime - counterTime >= 1000) 290 | { 291 | counterTime = currentTime; 292 | outCANFrame.frame.id = SERVICE_ID; 293 | outCANFrame.frame.length = 4; 294 | // количество пакетов в секунду, примерная скорость около 745 пакетов в секунду 295 | *(uint16_t*)(&(outCANFrame.frame.data[0])) = counterFPS; 296 | // количество байтов в секунду, примерная скорость около 9200 байтов в секунду 297 | *(uint16_t*)(&(outCANFrame.frame.data[2])) = counterBPS; 298 | 299 | sendPacketToPC(); 300 | 301 | counterFPS = 0; 302 | counterBPS = 0; 303 | } 304 | 305 | counterFPS++; 306 | // ID пакета (4 байта) + длина (1 байт) + данные 307 | counterBPS += (5 + outCANFrame.frame.length); 308 | } 309 | 310 | // Отправка полученного CAN-пакета в последовательный порт или Wi-Fi 311 | void sendPacketToPC() 312 | { 313 | // сигнатура (4 байта) + ID пакета (4 байта) + интервал (2 байта) + длина (1 байт) = 11 байт 314 | size_t dataLength = 11 + outCANFrame.frame.length; 315 | 316 | #ifdef MODE_SERIAL 317 | Serial.write((uint8_t*)&outCANFrame, dataLength); 318 | #endif // MODE_SERIAL 319 | 320 | #ifdef MODE_WIRELESS 321 | // накопление данных в буфере, это необходимо для увеличения пропускной способности 322 | memcpy(udpOutBufferPosition, &outCANFrame, dataLength); 323 | udpOutBufferPosition += dataLength; 324 | // если буфер заполнен - отправить порцию данных в сеть 325 | if (UDP_BUFFER_SIZE - (udpOutBufferPosition - udpOutBuffer) < UDP_BUFFER_MINIMUM) 326 | { 327 | // отправить данные 328 | UDP.beginPacket(pcIp, udpPort); 329 | UDP.write(udpOutBuffer, udpOutBufferPosition - udpOutBuffer); 330 | UDP.endPacket(); 331 | // сброс буфера отправки пакетов 332 | udpOutBufferPosition = udpOutBuffer; 333 | } 334 | #endif // MODE_WIRELESS 335 | } 336 | 337 | // Приём и разбор данных из последовательного порта или Wi-Fi 338 | void receivePacketFromPC() 339 | { 340 | #ifdef MODE_SERIAL 341 | if (Serial.available()) 342 | { 343 | if (parsingStep >= 0 && parsingStep <= 18) 344 | { 345 | int nextByte = Serial.read(); 346 | // сделан поочерёдный побайтовый разбор данных из последовательного порта, 347 | // так как парсинг всего буфера сразу после .readBytes(..) работал нестабильно. 348 | // идея такая: последовательно читая каждый байт, найти сигнатуру 349 | // потом заполнить ID-пакета и далее длину пакета 350 | // далее заполнить входной массив количеством байтов до длины пакета 351 | switch (parsingStep) 352 | { 353 | // поиск сигнатуры с учётом того, что придёт она с обратным порядком байтов (big-endian) 354 | case 0: 355 | parsingTime = millis(); 356 | if (nextByte == ((SIGNATURE >> 24) & 0xFFu)) parsingStep++; else parsingStep = 0; 357 | break; 358 | 359 | // поиск сигнатуры 360 | case 1: 361 | if (nextByte == ((SIGNATURE >> 16) & 0xFFu)) parsingStep++; else parsingStep = 0; 362 | break; 363 | 364 | // поиск сигнатуры 365 | case 2: 366 | if (nextByte == ((SIGNATURE >> 8) & 0xFFu)) parsingStep++; else parsingStep = 0; 367 | break; 368 | 369 | // поиск сигнатуры 370 | case 3: 371 | if (nextByte == ((SIGNATURE) & 0xFFu)) parsingStep++; else parsingStep = 0; 372 | break; 373 | 374 | // получение ID-пакета 375 | case 4 ... 7: 376 | *((uint8_t*)&inCANFrame.id + parsingStep - 4) = (uint8_t)nextByte; 377 | parsingStep++; 378 | break; 379 | 380 | // пропуск зарезервированного слова 381 | case 8 ... 9: 382 | parsingStep++; 383 | break; 384 | 385 | // получение длины данных 386 | case 10: 387 | if (nextByte >= 0 && nextByte <= 8) 388 | { 389 | inCANFrame.length = (uint8_t)nextByte; 390 | parsingStep++; 391 | } 392 | else 393 | parsingStep = 0; 394 | break; 395 | 396 | // получение непосредственно самих данных пакета 397 | case 11 ... 18: 398 | if (parsingStep - 11 < inCANFrame.length) 399 | { 400 | inCANFrame.data[parsingStep - 11] = (uint8_t)nextByte; 401 | parsingStep++; 402 | } 403 | else 404 | parsingStep = PARSING_COMPLETE; 405 | break; 406 | } 407 | } 408 | } 409 | else 410 | { 411 | // если шла одна из стадий разбора пакета от компьютера, данных нет и время истекло - прекратить разбор 412 | if (parsingStep && (millis() - parsingTime) > PARSING_TIMEOUT) 413 | { 414 | parsingStep = 0; 415 | } 416 | } 417 | #endif //MODE_SERIAL 418 | 419 | #ifdef MODE_WIRELESS 420 | int packetSize = UDP.parsePacket(); 421 | if (packetSize)// && parsingStep == 0) 422 | { 423 | int length = UDP.read(udpInBuffer, UDP_BUFFER_SIZE); 424 | if (length >= 11 && length <= 19) 425 | { 426 | uint8_t* receivedData = udpInBuffer; 427 | // проверка сигнатуры 428 | uint32_t signature = *(uint32_t*)receivedData; 429 | if (signature == SWAP_BYTES_UINT32(SIGNATURE)) 430 | { 431 | // пропустить сигнатуру и скопировать все остальные данные пакета 432 | receivedData += 4; 433 | memcpy(&inCANFrame, receivedData, 15); 434 | parsingStep = PARSING_COMPLETE; 435 | } 436 | } 437 | } 438 | #endif // MODE_WIRELESS 439 | 440 | // есть удачно разобранный пакет 441 | if (parsingStep == PARSING_COMPLETE) 442 | { 443 | // получен CAN-пакет от компьютера 444 | parsingStep = 0; 445 | // проверка управляющих пакетов 446 | if (inCANFrame.id == SERVICE_ID) 447 | { 448 | processCommand(); 449 | } 450 | else 451 | { 452 | #ifdef MODE_SIMULATOR 453 | // немного изенить данные и отправить обратно в компьютер 454 | outCANFrame.frame = inCANFrame; 455 | outCANFrame.frame.id += 8; 456 | outCANFrame.frame.interval = 123; 457 | for (size_t index = 0; index < inCANFrame.length; index++) 458 | { 459 | outCANFrame.frame.data[index]++; 460 | } 461 | sendPacketToPC(); 462 | #else // MODE_SIMULATOR 463 | // отправка пакета в CAN-шину 464 | CAN.sendMsgBuf(inCANFrame.id, (inCANFrame.id & 0x80000000u) ? 1 : 0, inCANFrame.length, inCANFrame.data); 465 | #endif 466 | } 467 | } 468 | } 469 | 470 | // Обработка управляющих пакетов 471 | void processCommand() 472 | { 473 | switch (inCANFrame.data[0]) 474 | { 475 | // отключиться от CAN-шины 476 | case 0: 477 | CANDisconnect(); 478 | break; 479 | 480 | // подключиться к CAN-шине на указанной скорости 481 | case 1: 482 | uint16_t speed = *(uint16_t*)(&(inCANFrame.data[1])); 483 | CANConnect(speed); 484 | break; 485 | } 486 | } 487 | 488 | // Подключиться к CAN-шине 489 | void CANConnect(uint16_t speed) 490 | { 491 | #ifdef MODE_SIMULATOR 492 | simulatorConnected = true; 493 | #else // MODE_SIMULATOR 494 | // подключить обработчик прерывания о поступлении данных по спадающему уровню сигнала 495 | attachInterrupt(digitalPinToInterrupt(CAN_INT), CANInterrupt, FALLING); 496 | 497 | MCP_BITTIME_SETUP canSpeed; 498 | switch (speed) 499 | { 500 | case 100: canSpeed = CAN_100KBPS; break; 501 | case 125: canSpeed = CAN_125KBPS; break; 502 | case 200: canSpeed = CAN_200KBPS; break; 503 | case 250: canSpeed = CAN_250KBPS; break; 504 | case 500: canSpeed = CAN_500KBPS; break; 505 | case 1000: canSpeed = CAN_1000KBPS; break; 506 | default: canSpeed = CAN_500KBPS; break; 507 | } 508 | 509 | // настройка скорости обмена и частоты кварца 510 | while (CAN.begin(canSpeed, MCP_8MHz) != CAN_OK) 511 | { 512 | delay(100); 513 | } 514 | 515 | // типы фильтров 516 | #define STD 0 517 | #define EXT 1 518 | 519 | // сброс фильтров 520 | CAN.init_Filt(0, STD, 0); 521 | CAN.init_Filt(1, STD, 0); 522 | CAN.init_Filt(2, STD, 0); 523 | CAN.init_Filt(3, STD, 0); 524 | CAN.init_Filt(4, STD, 0); 525 | CAN.init_Filt(5, STD, 0); 526 | 527 | // очистка масок 528 | CAN.init_Mask(0, STD, 0); 529 | CAN.init_Mask(1, STD, 0); 530 | #endif 531 | } 532 | 533 | // Отключиться от CAN-шины 534 | void CANDisconnect() 535 | { 536 | #ifdef MODE_SIMULATOR 537 | simulatorConnected = false; 538 | #else // MODE_SIMULATOR 539 | detachInterrupt(digitalPinToInterrupt(CAN_INT)); 540 | #endif 541 | } 542 | 543 | // Обработчик прерывания поступивших данных 544 | #ifdef MODE_ESP 545 | IRAM_ATTR 546 | #endif 547 | void CANInterrupt() 548 | { 549 | canDataReceived = true; // установить флаг наличия данных в буфере 550 | } 551 | --------------------------------------------------------------------------------