Трансивер превращает логические уровни контроллера CAN в ESP32 в дифференциальную пару CANH/CANL, которой разговаривают автомобили и промышленные блоки. Работает от 3,3 В, поэтому подходит ESP32 без согласования уровней.
В ESP32 есть контроллер CAN, который называется TWAI: он сам собирает кадры, считает контрольную сумму и разбирается с арбитражем, но наружу выдаёт только логические уровни на двух выводах. Шине нужна дифференциальная пара CANH/CANL — её формирует трансивер SN65HVD230.
На стороне платы у модуля два сигнальных вывода: CTX (то, что плата передаёт) и CRX (то, что плата принимает). Их подключают к выводам, назначенным в twai_general_config_t. Скорость шины в скетче должна совпадать со скоростью сети: у легковых автомобилей по OBD-II чаще всего 500 кбит/с, иногда 250.
#include "driver/twai.h"
// Запрос оборотов двигателя по OBD-II.
//
// Контроллер CAN внутри ESP32 (он называется TWAI) выдаёт обычные
// логические уровни на TX и RX. Шине нужна дифференциальная пара
// CANH/CANL — её даёт трансивер SN65HVD230. Он питается от 3,3 В и
// стоит между выводами платы и разъёмом.
//
// Диагностика OBD-II устроена как вопрос и ответ:
// вопрос — кадр 0x7DF (всем блокам): 02 01 0C («служба 01, параметр 0C»)
// ответ — кадр 0x7E8 (двигатель): 04 41 0C A B
// обороты = (256 * A + B) / 4
const gpio_num_t CAN_TX = GPIO_NUM_5; // к CTX трансивера
const gpio_num_t CAN_RX = GPIO_NUM_4; // к CRX трансивера
void setup() {
Serial.begin(115200);
twai_general_config_t general = TWAI_GENERAL_CONFIG_DEFAULT(CAN_TX, CAN_RX, TWAI_MODE_NORMAL);
twai_timing_config_t timing = TWAI_TIMING_CONFIG_500KBITS(); // скорость легковой шины
twai_filter_config_t filter = TWAI_FILTER_CONFIG_ACCEPT_ALL();
if (twai_driver_install(&general, &timing, &filter) != ESP_OK) {
Serial.println("CAN: драйвер не установился");
return;
}
if (twai_start() != ESP_OK) {
Serial.println("CAN: драйвер не запустился");
return;
}
Serial.println("CAN запущен, 500 кбит/с");
}
void loop() {
twai_message_t request = {};
request.identifier = 0x7DF;
request.data_length_code = 8;
request.data[0] = 0x02; // длина полезных данных
request.data[1] = 0x01; // служба 01: текущие данные
request.data[2] = 0x0C; // параметр 0C: обороты
twai_transmit(&request, 0);
// Ответ ждём не дольше четверти секунды: если автомобиль молчит, это
// тоже результат, а не повод зависнуть.
unsigned long start = millis();
bool answered = false;
while (millis() - start < 250 && !answered) {
twai_message_t reply;
while (twai_receive(&reply, 0) == ESP_OK) {
if (reply.identifier == 0x7E8 && reply.data[1] == 0x41 && reply.data[2] == 0x0C) {
int rpm = (256 * reply.data[3] + reply.data[4]) / 4;
Serial.printf("обороты: %d об/мин\n", rpm);
answered = true;
break;
}
}
delay(5);
}
if (!answered) Serial.println("автомобиль не ответил: проверьте CANH, CANL и скорость шины");
delay(1000);
}
В железе
У многих модулей на плате стоит нагрузочный резистор 120 Ом между CANH и CANL. На шине их должно быть два — по одному на каждом конце. Если вы подключаетесь к готовой сети (автомобиль, станок), где концы уже нагружены, лишний резистор лучше отключить. Земля модуля и земля шины должны быть общими.
Частые ошибки
TX и RX перепутаны местами: CTX идёт на вывод, назначенный на RX, и наоборот. Драйвер запускается без ошибок, но кадры не ходят.
Скорость шины не совпадает с сетью: контроллер видит ошибки и не принимает ни одного кадра.
CANH и CANL поменяны местами: шина работает с ошибками или молчит.
Нет общей земли между модулем и шиной: приём нестабилен.
Модуль, рассчитанный на 5 В (например, на TJA1050), питается от 3,3 В или наоборот: подключайте модуль, который рассчитан на ваше напряжение логики.