Введение
В данной статье расскажу о реализации протокола CAN с использованием микроконтроллера ESP32 и модуля MCP2515. Задача продемонстрировать передачу показаний температурного датчика DS18B20 по шине CAN на заданную дистанцию. В предыдущей статье аналогичная задача выполнялась на базе платформы Arduino UNO.

Необходимые детали
| № | Компонент | Количество | Купить |
|---|---|---|---|
| 1 | Отладочная плата ESP32 | 2 шт | Авито | AliExpress | RobotChip |
| 2 | Модуль контроллера шины CAN на MCP2515 | 2 шт | Авито | AliExpress | RobotChip |
| 3 | Датчик DS18B20 в металлической гильзе | 1 шт | Авито | AliExpress | RobotChip |
| 4 | Провод DuPont | 18 шт | Авито | AliExpress | RobotChip |
| 6 | OLED-дисплей | 1 шт | Авито | AliExpress | RobotChip |
| 6 | Резистор 4.7 кОм | 1 шт | Авито | AliExpress | RobotChip |
Описание
В этой статье мы будем работать в среде Arduino IDE. Поддержку плат ESP32 легко добавить через встроенный Boards Manager (подробную инструкцию я давал в прошлой публикации).
Шина CAN — это надежный стандарт связи, который чаще всего применяется в автомобилях и промышленности. Главное ее преимущество перед привычными протоколами (UART, SPI, I2C) заключается в стабильной работе на больших расстояниях. Если стандартные интерфейсы начинают терять данные уже через несколько метров кабеля, то CAN сохраняет работоспособность линий длиной до километра. Скорость передачи составляет от 50 Кбит/с до 1 Мбит/с; чем она выше, тем короче может быть шина (от 1000 м при 50 Кбит/с до 40 м при 1 Мбит/с).
Сегодня соберем практическую схему обмена данными по шине CAN. Мы возьмем две платы ESP32 с модулями MCP2515: первая ESP32 будет считывать показания температуры с датчика DS18B20 и транслировать их в сеть, а вторая — принимать сигнал и выводить значения на небольшой экран OLED. Посмотрим, как подружить эти компоненты между собой.
Подключение
Давайте перейдем от теории к практике и соберем схему для теста протокола CAN. Наша задача — подключить модуль MCP2515 к ESP32 через интерфейс SPI и настроить отправку температуры с датчика DS18B20. Подключение компонентов не представляет сложности. Схема соединения ESP32 и модуля MCP2515 одинакова как для передатчика, так и для приёмника:
- Питание: VCC и GND подключаются к выводам 5V и GND на плате ESP32.
- Шина SPI: выводы CS, SO (MISO), SI (MOSI) и SCK подключаются к пинам GPIO 5, 19, 23 и 18 соответственно.
На стороне передатчика дополнительно подключается датчик DS18B20:
- Питание VCC и GND датчика подключаются к контактам 3.3V и GND.
- Сигнальный вывод (DATA) подключается к пину GPIO 21.
- Также необходимо установить подтягивающий резистор номиналом 4,7 кОм между линией DATA и питанием 3.3V.
На стороне приёмника остаётся подключить OLED-дисплей по шине I²C:
- Выводы питания VCC и GND дисплея подключаются к контактам 3.3V и GND.
- Сигнальные выводы шины I²C SCL и SDA OLED дисплея подключаем к пинам GPIO 22, а SDA к GPIO 21.

Для работы необходимо установить перемычку на одной из моделей MCP2515 (на разъеме J1). Так же, распивнока модулей ESP32 разная, учитывайте при подключении.
Установка библиотек
Для работы, будем использовать библиотеку «DallasTemperature», «MCP2515», «Adafruit_SSD1306» и «OneWireNg» скачать можно используя интегрированную среду разработки Arduino IDE», для этого откройте «Скетч → Подключение библиотеки → Менеджер библиотек…», так же выложу библиотеки в конце статьи.
В строке поиска введите «DallasTemperature» и нажмите «Установить »

Далее, установим следующую библиотеку, в строке поиска введите «MCP2515», найдите библиотеку «ACAN2515 от Pierre Molinaro» и нажмите «Установить »

Далее, установим следующую библиотеку, в строке поиска введите «Adafruit_SSD1306», найдите библиотеку «Adafruit SSD1306 от Adafruit » и нажмите «Установить »

Для работы библиотеки необходимо установить зависимые библиотеки, нажимаем «Установить все»

Так как библиотека «OneWire» на написание статьи не работала с ESP32 (03.09.2026), нужно установить дополнительную библиотеку «OneWireNg», найдите библиотеку и нажмите «Установить».

Программа для передатчика
Ниже приведён исходный код передатчика, загрузите скетч в плату ESP32.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#include <SPI.h> // Подключаем библиотеку SPI #include <mcp2515.h> // Подключаем библиотеку MCP2515 #include <OneWireNg.h> // Подключаем библиотеку OneWire #include <DallasTemperature.h> // Подключаем библиотеку DallasTemperature #define ONE_WIRE_BUS 21 // Указываем которому выходу подключен датчика DS18B20 OneWire oneWire(ONE_WIRE_BUS); // Создаем объект OneWire DallasTemperature sensors(&oneWire); // Передаем управление шиной в библиотеку DallasTemperature struct can_frame canMsg; MCP2515 mcp2515(5); // Указываем какому пину подключен вывод CS #define CAN_ACK_ID 0x037 // Указываем номер ID void setup() { Serial.begin(115200); // Инициализируем последовательную связь SPI.begin(); // Запуск аппаратной шины SPI mcp2515.reset(); // Команда сброса MCP2515 mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ); // Указываем скорость обмена (500 кбит/с) и частоту кварца на плате модуля MCP2515. mcp2515.setNormalMode(); // Перевод контроллера в рабочий режим. sensors.begin(); // Инициализация датчиков температуры. } void loop() { sensors.requestTemperatures(); // Отправляем команду получения температуры float temperatureC = sensors.getTempCByIndex(0); // Чтение первого найденного датчика (индекс 0) int tempInt = (int)(temperatureC * 100); // Конвертируем показания в целое число canMsg.can_id = 0x036; // CAN ID canMsg.can_dlc = 2; // Передаем ровно 2 байта данных canMsg.data[0] = highByte(tempInt); // Температура: Старший байт canMsg.data[1] = lowByte(tempInt); // Температура: Младший байт if (mcp2515.sendMessage(&canMsg) == MCP2515::ERROR_OK) { Serial.print("Sent: "); // Отправка в последовательный порт данные Serial.println(temperatureC); // Отправка в последовательный порт температуру } else { Serial.println("CAN Send Error"); // Отправка в последовательный порт данные } mcp2515.sendMessage(&canMsg); // Отправка данных delay(1000); // Пауза перед следующим измерением } |
Программа для приемника
Ниже приведён исходный код приемника, загрузите скетч в плату ESP32.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#include <SPI.h> // Подключаем библиотеку SPI #include <mcp2515.h> // Подключаем библиотеку MCP2515 #include <Adafruit_SSD1306.h> // Подключаем библиотеку для работы с OLED-дисплеем #define SCREEN_WIDTH 128 // Ширина экрана в пикселях #define SCREEN_HEIGHT 64 // Высота экрана в пикселях Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT); // Инициализация объекта дисплея на шине I2C (&Wire) struct can_frame canMsg; MCP2515 mcp2515(5); // Указываем какому пину подключен вывод CS void setup() { Serial.begin(115200); // Инициализируем последовательную связь SPI.begin(); // Запуск аппаратной шины SPI mcp2515.reset(); // Команда сброса MCP2515 mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ); // Указываем скорость обмена (500 кбит/с) и частоту кварца на плате модуля MCP2515. mcp2515.setNormalMode(); // Перевод контроллера в рабочий режим. display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Инициализация дисплея display.clearDisplay(); // Очистка дисплея display.setTextColor(WHITE); // Установка цвета текста } void loop() { if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK) // Читаем данные из буфера. Если чтение успешно: { if (canMsg.can_id == 0x036) // Проверяем ID отправителя (должен совпадать с передатчиком) { int tempInt = (canMsg.data[0] << 8) | canMsg.data[1]; // Собираем два байта обратно в одно целое число (MSB сдвиг влево на 8 бит + LSB) float temperatureC = tempInt / 100.0; // Возвращаем исходный масштаб числа с плавающей точкой Serial.print("Temperature received: "); // Печать префикса в монитор порта Serial.print(temperatureC); Serial.println(" °C"); display.clearDisplay(); // Очистка локального буфера перед новой отрисовкой display.setTextSize(1); // Размер шрифта x1 (стандартный 5x8 пикселей на символ) display.setCursor(25, 10); // Позиция курсора: X=25px, Y=10px display.print("Temperature:"); // Текстовая подпись display.setTextSize(2); // Увеличенный размер шрифта x2 для цифр display.setCursor(25, 30); // Новая позиция курсора под цифры display.print(temperatureC); // Вывод значения температуры display.print("C"); // Единицы измерения display.display(); // Передача всего накопленного буфера по I2C на физический экран (только эта команда обновляет матрицу) } } } |
Если все правильно сделали при открытия мониторинга порта увидим отправка и получения команд.


Описание программы передатчика
В начале программы подключаем библиотеки и объявление объектов
1 2 3 4 |
#include <SPI.h> #include <mcp2515.h> #include <OneWireNG.h> #include <DallasTemperature.h> |
Далее настраиваем датчик DS18B20, указываем вывод GPIO21, создаем объект управления шиной OneWire и передает его библиотеке DallasTemperature.
1 2 3 |
#define ONE_WIRE_BUS 21 DS18B20 OneWire oneWire(ONE_WIRE_BUS); OneWire DallasTemperature sensors(&oneWire); |
Указываем библиотеке, что вывод CS модуля MCP2515 подключен к пину GPIO5.
1 2 |
struct can_frame canMsg; MCP2515 mcp2515(5); |
В функции setup() запускается последовательный порт на скорости 115200 бод, инициализируется шина SPI и настраивается модуль MCP2515 (сброс, установка скорости 500 кбит/с для кварца 8 МГц и перевод в режим Normal Mode), после чего производится инициализация датчиков OneWire.
1 2 3 4 5 6 |
Serial.begin(115200); SPI.begin(); mcp2515.reset(); mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ); mcp2515.setNormalMode(); sensors.begin(); |
В функции loop() запускается преобразование температуры на всех датчиках OneWire, считывается значение первого устройства (индекс 0) в градусах Цельсия ( float), после чего число умножается на 100 и приводится к целому типу для передачи по CAN-шине/
1 2 3 |
sensors.requestTemperatures(); float temperatureC = sensors.getTempCByIndex(0); int tempInt = (int)(temperatureC * 100); |
CAN формируется с идентификатором 0x036, длиной данных (DLC) 2 байта, где в массив data упаковывается целое число через старший ( highByte) и младший ( lowByte) байты.
1 2 3 4 |
canMsg.can_id = 0x036; canMsg.can_dlc = 2; canMsg.data[0] = highByte(tempInt); canMsg.data[1] = lowByte(tempInt); |
Отправляем данные с паузой 1000 мкс
1 2 3 4 5 6 7 8 9 10 |
if (mcp2515.sendMessage(&canMsg) == MCP2515::ERROR_OK) { Serial.print("Sent: "); Serial.println(temperatureC); } else { Serial.println("CAN Send Error"); } delay(1000); |
Описание программы приемника
Первым делом подключаем библиотеки для работы с модулем CAN и работы с OLED дисплеем.
1 2 3 |
#include <SPI.h> #include <mcp2515.h> #include <Adafruit_SSD1306.h> |
Указываем разрешение экрана и создает объект.
1 2 3 |
#define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT); |
Указываем библиотеке, что вывод CS модуля MCP2515 подключен к пину GPIO5.
1 2 |
struct can_frame canMsg; MCP2515 mcp2515(5); |
В функции setup() запускается последовательный порт на скорости 115200 бод, инициализируется шина SPI и настраивается модуль MCP2515 (сброс, установка скорости 500 кбит/с для кварца 8 МГц и перевод в режим Normal Mode).
1 2 3 4 5 |
Serial.begin(115200); SPI.begin(); mcp2515.reset(); mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ); mcp2515.setNormalMode(); |
Инициализирует дисплей по адресу 0x3C, очищает видеобуфер микроконтроллера и устанавливает белый цвет текста.
1 2 3 |
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.clearDisplay(); display.setTextColor(WHITE); |
В функции loop() выполняется постоянная проверка наличия данных в приемном буфере MCP2515; если сообщение получено, оно копируется в переменную canMsg.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
void loop() { if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK) { if (canMsg.can_id == 0x036) { int tempInt = (canMsg.data[0] << 8) | canMsg.data[1]; float temperatureC = tempInt / 100.0; Serial.print("Temperature received: "); Serial.print(temperatureC); Serial.println(" °C"); display.clearDisplay(); display.setTextSize(1); display.setCursor(25, 10); display.print("Temperature:"); display.setTextSize(2); display.setCursor(25, 30); display.print(temperatureC); display.print("C"); display.display(); } } } |
Полезные ссылки
| № | Описание | Скачать |
|---|---|---|
| 1 | Библиотека DallasTemperature | DallasTemperature v.1.1.15.zip |
| 2 | Библиотека ACAN2515 | ACAN2515 v.2.1.5.zip |
| 3 | Библиотека Adafruit_SSD1306 | Adafruit_SSD1306 v.2.5.17.zip |
| 4 | Библиотека OneWireNg | OneWireNg v.0.14.1 |
| 5 | Библиотека Adafruit GFX Library | Adafruit GFX Library v.1.12.6 |
| 6 | Библиотека Adafruit BusIO | Adafruit BusIO v.1.14.7 |