Делаем звуки поворотников с помощью Arduino
Имеется автомобиль Ravon R4, он же Chevrolet Cobalt в детстве. И чтобы жизнь мёдом не казалась от владения столь прекрасным пепелацем, подкинули нам искусственную сложность: при замене штатной магнитолы исчезают звуки поворотников. В статье по ссылке подобрано разобрана суть проблемы, а здесь мы будем её решать несколько костыльным путём.
Содержание страницы
Требования
- Автомобиль Chevrolet Cobalt в комплектации LTZ или Ravon R4 любой комплектации;
- Любой программируемый микроконтроллер, я использую Arduino UNO;
- MCP2515 - CAN-контроллер с интерфейсом SPI;
- DFPlayer Mini - аппаратный проигрыватель MP3 файлов с последовательным интерфейсом управления;
- Любой динамик для воспроизведения звуков.
Стоимость такого удовольствия следующая: Arduino (250 руб.), MCP2515 (130 руб.), DFPlayer (60 руб.), динамик (50 руб.). Итого 490 рублей.
Подключение
Начнем с CAN-декодера. Прежде чем начать с ним работать, я по советам других пользователей сразу перепаял кварцевый резонатор c 8мГц на 16мГц. У платы MCP2515 в сторону шины два вывода: CAN High и CAN Low. Работать мы будем с шиной SWCAN, которая по своей природе является однопроводной. Значит, CAN High подключаем к шине, CAN Low на землю. Тут уже присутствуют варианты:
- Взять SWCAN с диагностической колодки, пин №5. Там же кинуть землю;
- Взять SWCAN с разъема для магнитолы, в нем он находится под номером №14, а земля №38:
Другой стороной MCP2515 подключаем к нашему микроконтроллеру - Arduino UNO. Делаем это как показано на схеме:
- INT => D2;
- SCK => D13;
- SI - D11;
- SO - D12;
- CS - D10;
- GND - GND;
- VCC - 5V.
MP3 проигрыватель управляется по последовательному порту через пины RX и TX. По факту RX не используется, управление одностороннее. В случае с Arduino можно использовать как физический интерфейс, размещенный на пинах D0 (RX) и D1 (TX) Ардуины, так и воспользовавшись библиотекой SoftwareSerial поднять программный интерфейс на любом цифровом пине. Я выбрал второй вариант и использовал для него пин D6. При первом подключении я столкнулся с помехами на динамик, решением оказалось TX пин подключить через 1кОм резистор. Подключаем проигрыватель так:
- VCC - 5V;
- GND - GND;
- TX - D6;
- SPK_1 и SPK_2 - на динамик.
На этом подключение заканчивается и мы переходим к теории о шине SWCAN.
SWCAN шина и используемые в ней коды
В подробные детали о шине вдаваться не буду, для нашей задачи достаточно о ней знать следующее: представлена одним проводом, работает на скорости 33.3kbps и в том числе содержит в себе команды для индикации системных сообщений.
В интернете мне удалось найти два кода соответствующих включению и выключению звука поворотников, но для меня так же критичны звуки незакрытых дверей и непристегнутого ремня. Потому пришлось прибегнуть к самостоятельному "отлову" необходимых кодов. Для этого я использовал программу Can Hacker и залитый соответствующий скетч в ардуино.
Проехавшись буквально пять минут, создавая ситуация для воспроизведения нужных мне звуков я вывел искомые значения. С радостью делюсь ими с вами (если вдруг, вы найдете еще какие-нибудь интересные значения, не поленитесь поделиться ими с нами :) ):
Поворотник (включить): | Extended ID: 0x10400060 DLC: 5 Data: 0x82 0x08 0x01 0xFF 0xD4 |
Поворотник (выключить): | Extended ID: 0x10400060 DLC: 5 Data: 0x81 0x08 0x01 0xFF 0xD5 |
Открытая водительская дверь на зажигании (включить): | Extended ID: 0x10400040 DLC: 5 Data: 0x86 0x3C 0xFF 0xFF 0x58 |
Открытая водительская дверь на зажигании (выключить): | Extended ID: 0x10400040 DLC: 5 Data: 0x86 0x3C 0x00 0xFF 0x58 |
Сброс одометра (единократный щелчок): | Extended ID: 0x10400060 DLC: 5 Data: 0x85 0x1E 0x01 0x33 0x38 |
Непристегнутый ремень на скорости выше 20км/ч (включить): | Extended ID: 0x10400058 DLC: 5 Data: 0x87 0x65 0x64 0xFF 0x05 |
Непристегнутый ремень на скорости выше 20км/ч (выключить): | Extended ID: 0x10400058 DLC: 5 Data: 0x87 0x65 0x00 0xFF 0x05 |
Тройной писк при открытой двери в движении (единократно): | Extended ID: 0x10400060 DLC: 5 Data: 0x86 0x28 0x04 0xFF 0x88 |
Движение на стояночном тормозе (включить): | Extended ID: 0x10400060 DLC: 5 Data: 0x86 0x1E 0xFF 0xFF 0x78 |
Движение на стояночном тормозе (выключить): | Extended ID: 0x10400060 DLC: 5 Data: 0x86 0x1E 0x00 0xFF 0x78 |
Полученных данных достаточно, а значит мы переходим к программированию микроконтроллера.
Скетч для Arduino
В Arduino IDE устанавливаем необходимые для работы библиотеки:
- https://github.com/coryjfowler/MCP_CAN_lib - библиотека для работы с CAN-декодером;
- https://github.com/Makuna/DFMiniMp3 - библиотека для работы с MP3 плеером.
Подготавливаем MicroSD флеш-карту для проигрывателя: форматируем в FAT32 и копируем в её корень файлы звуков строго под названием 0001.mp3, 0002.mp3, ..., 000n.mp3. Для начала можете воспользоваться звуками, которые подготовил я:
Скачать “Скетч декодера SWCAN-шины Chevrolet Cobalt и Ravon R4 вместе со звуками.” CANBus_decoder_cobalt_r4.zip – Загружено 1 раз – 1,27 МБНо делал я их на скорую руку и качество посредственное. Рекомендую заморочиться и найти самостоятельно, не забыв выравнять у них всех громкость к одному уровню.
Флешку вставляем в DFPlayer Mini и заливаем предложенный мною скетч для Arduino:
#include <mcp_can.h> #include <SPI.h> #include <SoftwareSerial.h> #include <DFMiniMp3.h> long unsigned int rxId; unsigned char len = 0; unsigned char rxBuf[8]; char msgString[128]; #define CAN0_INT 2 // Set INT to pin 2 MCP_CAN CAN0(10); // Set CS to pin 10 class Mp3Notify { public: static void OnError(uint16_t errorCode) { Serial.println(); Serial.print("Com Error "); Serial.println(errorCode); } static void OnPlayFinished(uint16_t globalTrack) { Serial.println(); Serial.print("Play finished for #"); Serial.println(globalTrack); } static void OnCardOnline(uint16_t code) { Serial.println(); Serial.print("Card online "); Serial.println(code); } static void OnCardInserted(uint16_t code) { Serial.println(); Serial.print("Card inserted "); Serial.println(code); } static void OnCardRemoved(uint16_t code) { Serial.println(); Serial.print("Card removed "); Serial.println(code); } }; SoftwareSerial secondarySerial(5, 6); // RX, TX DFMiniMp3<SoftwareSerial, Mp3Notify> mp3(secondarySerial); void setup() { Serial.begin(115200); mp3.begin(); mp3.setVolume(30); if(CAN0.begin(MCP_ANY, CAN_33K3BPS, MCP_16MHZ) == CAN_OK) Serial.println("MCP2515 Initialized Successfully!"); else Serial.println("Error Initializing MCP2515..."); CAN0.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP2515 sends acks to received data. pinMode(CAN0_INT, INPUT); // Configuring pin for /INT input Serial.println("Start detect codes for Cobalt/R4..."); } void loop() { if(!digitalRead(CAN0_INT)) // If CAN0_INT pin is low, read receive buffer { CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) if((rxId & 0x80000000) == 0x80000000) { // Determine if ID is standard (11 bits) or extended (29 bits) if ((rxId & 0x1FFFFFFF) == 272629856) { // Поворотники if (rxBuf[0] == 0x82) { mp3.stop(); mp3.playMp3FolderTrack(4); } if (rxBuf[0] == 0x81) { mp3.stop(); mp3.playMp3FolderTrack(5); } // Одометр if (rxBuf[0] == 0x85) { mp3.stop(); mp3.playMp3FolderTrack(7); } // Открытая дверь на драйве if (rxBuf[0] == 0x86 && rxBuf[1] == 0x28) { mp3.stop(); mp3.playMp3FolderTrack(9); } // Движение на ручнике if (rxBuf[0] == 0x86 && rxBuf[1] == 0x1E && rxBuf[2] == 0xFF) { mp3.stop(); mp3.playMp3FolderTrack(1); } if (rxBuf[0] == 0x86 && rxBuf[1] == 0x1E && rxBuf[2] == 0x00) { mp3.stop(); } } if ((rxId & 0x1FFFFFFF) == 272629824) { // Открытая водительская дверь на зажигании if (rxBuf[0] == 0x86 && rxBuf[2] == 0xFF) { mp3.stop(); mp3.playMp3FolderTrack(6); } if (rxBuf[0] == 0x86 && rxBuf[2] == 0x00) { mp3.stop(); } } if ((rxId & 0x1FFFFFFF) == 272629848) { // Ремень на скорости выше 20кмч if (rxBuf[0] == 0x87 && rxBuf[2] == 0x64) { mp3.stop(); mp3.playMp3FolderTrack(8); } if (rxBuf[0] == 0x87 && rxBuf[2] == 0x00) { mp3.stop(); } } } else sprintf(msgString, "Standard ID: 0x%.3lX DLC: %1d Data:", rxId, len); if((rxId & 0x40000000) == 0x40000000){ // Determine if message is a remote request frame. sprintf(msgString, " REMOTE REQUEST FRAME"); } else { for(byte i = 0; i<len; i++){ sprintf(msgString, " 0x%.2X", rxBuf[i]); } } } }
Запитываем Ардуину любым удобным способом (хоть спецификации и позволяют запитать её от 12 вольт, что совпадает с бортовой сетью автомобиля, делать этого не рекомендуется - рано или поздно она сгорит) и проверяем работу наших звуков.
Добрый день,
Решил осуществить данный проект на своем авто. Приобрел все необходимое, но компиляция скетча не проходит. Помогите решить проблему, может на связь с вами выйти хоть по переписке.
У вас получилось скомпилировать скетч?
Здравствуйте. Не совсем понимаю строки:
if((rxId & 0x80000000) == 0x80000000) { // Determine if ID is standard (11 bits) or extended (29 bits)
if ((rxId & 0x1FFFFFFF) == 272629856)
Откуда цифры 272629856?
Делаю подобный проект, только вот собираюсь ловить сигнал с кнопок руля и управлять кнопками на Блютуз модуле. Не подскажите в таком случае, у меня сигнал 3С4(ID) 2(DLC) 00 08 00(DATA), как написать этот кусок кода под мой сигнал? Спасибо.
ПС: автомобиль другой.
Переви из шеснацати ричнов в десятичную систему
Это цыфры 10400060
Уважаемый автор! Сохранился ли у Вас can dump с низко скоростной шины? Пытаюсь вкорячить в авто магнитолу с блютуз и CAN шиной. Блокировку снял, но магнитола отключается через 10 минут работы. Пытаюсь сделать CAN sender. Поделитесь если есть записанная трасса CAN сообщений. Нужно подать сигнал АСС в шину. Спасибо.
Люди подскажите как кан хакер на 33. Кгц переделать.
exit status 1
Ошибка компиляции для платы Arduino Uno.
?????
Did you find the problem??? I have a same error... Can you help me?
Подскажите как выставить в программе CanHacker 2.00.01 скорость 33.3kbps.
В ней есть настройка "User Def" но значение не могу вычесть калькулятором.
exit status 1
Error compiling for board Arduino Uno.
Скетч не рабочий ну совсем
Как починить?
In file included from E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:9:0:
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h: In instantiation of 'DFMiniMp3::reply_t DFMiniMp3::retryCommand(uint8_t, uint8_t, uint16_t, bool) [with T_SERIAL_METHOD = SoftwareSerial; T_NOTIFICATION_METHOD = Mp3Notify; T_CHIP_VARIANT = Mp3ChipOriginal; uint8_t = unsigned char; uint16_t = unsigned int]':
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:571:21: required from 'void DFMiniMp3::setCommand(uint8_t, uint16_t) [with T_SERIAL_METHOD = SoftwareSerial; T_NOTIFICATION_METHOD = Mp3Notify; T_CHIP_VARIANT = Mp3ChipOriginal; uint8_t = unsigned char; uint16_t = unsigned int]'
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:170:19: required from 'void DFMiniMp3::setVolume(uint8_t) [with T_SERIAL_METHOD = SoftwareSerial; T_NOTIFICATION_METHOD = Mp3Notify; T_CHIP_VARIANT = Mp3ChipOriginal; uint8_t = unsigned char]'
E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:66:19: required from here
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:557:43: error: no matching function for call to 'Mp3Notify::OnError(DFMiniMp3&, uint16_t&)'
T_NOTIFICATION_METHOD::OnError(*this, reply.arg);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:22:15: note: candidate: static void Mp3Notify::OnError(uint16_t)
E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:22:15: note: candidate expects 1 argument, 2 provided
In file included from E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:9:0:
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h: In instantiation of 'void DFMiniMp3::callNotification(DFMiniMp3::reply_t) [with T_SERIAL_METHOD = SoftwareSerial; T_NOTIFICATION_METHOD = Mp3Notify; T_CHIP_VARIANT = Mp3ChipOriginal]':
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:395:29: required from 'bool DFMiniMp3::abateNotification() [with T_SERIAL_METHOD = SoftwareSerial; T_NOTIFICATION_METHOD = Mp3Notify; T_CHIP_VARIANT = Mp3ChipOriginal]'
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:71:33: required from 'void DFMiniMp3::loop() [with T_SERIAL_METHOD = SoftwareSerial; T_NOTIFICATION_METHOD = Mp3Notify; T_CHIP_VARIANT = Mp3ChipOriginal]'
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:445:13: required from 'void DFMiniMp3::drainResponses() [with T_SERIAL_METHOD = SoftwareSerial; T_NOTIFICATION_METHOD = Mp3Notify; T_CHIP_VARIANT = Mp3ChipOriginal]'
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:539:27: required from 'DFMiniMp3::reply_t DFMiniMp3::retryCommand(uint8_t, uint8_t, uint16_t, bool) [with T_SERIAL_METHOD = SoftwareSerial; T_NOTIFICATION_METHOD = Mp3Notify; T_CHIP_VARIANT = Mp3ChipOriginal; uint8_t = unsigned char; uint16_t = unsigned int]'
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:571:21: required from 'void DFMiniMp3::setCommand(uint8_t, uint16_t) [with T_SERIAL_METHOD = SoftwareSerial; T_NOTIFICATION_METHOD = Mp3Notify; T_CHIP_VARIANT = Mp3ChipOriginal; uint8_t = unsigned char; uint16_t = unsigned int]'
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:170:19: required from 'void DFMiniMp3::setVolume(uint8_t) [with T_SERIAL_METHOD = SoftwareSerial; T_NOTIFICATION_METHOD = Mp3Notify; T_CHIP_VARIANT = Mp3ChipOriginal; uint8_t = unsigned char]'
E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:66:19: required from here
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:406:50: error: no matching function for call to 'Mp3Notify::OnPlayFinished(DFMiniMp3&, DfMp3_PlaySources, uint16_t&)'
T_NOTIFICATION_METHOD::OnPlayFinished(*this, DfMp3_PlaySources_Usb, reply.arg);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:29:15: note: candidate: static void Mp3Notify::OnPlayFinished(uint16_t)
E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:29:15: note: candidate expects 1 argument, 3 provided
In file included from E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:9:0:
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:410:50: error: no matching function for call to 'Mp3Notify::OnPlayFinished(DFMiniMp3&, DfMp3_PlaySources, uint16_t&)'
T_NOTIFICATION_METHOD::OnPlayFinished(*this, DfMp3_PlaySources_Sd, reply.arg);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:29:15: note: candidate: static void Mp3Notify::OnPlayFinished(uint16_t)
E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:29:15: note: candidate expects 1 argument, 3 provided
In file included from E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:9:0:
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:414:50: error: no matching function for call to 'Mp3Notify::OnPlayFinished(DFMiniMp3&, DfMp3_PlaySources, uint16_t&)'
T_NOTIFICATION_METHOD::OnPlayFinished(*this, DfMp3_PlaySources_Flash, reply.arg);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:29:15: note: candidate: static void Mp3Notify::OnPlayFinished(uint16_t)
E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:29:15: note: candidate expects 1 argument, 3 provided
In file included from E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:9:0:
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:418:54: error: 'OnPlaySourceOnline' is not a member of 'Mp3Notify'
T_NOTIFICATION_METHOD::OnPlaySourceOnline(*this, static_cast(reply.arg));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:422:56: error: 'OnPlaySourceInserted' is not a member of 'Mp3Notify'
T_NOTIFICATION_METHOD::OnPlaySourceInserted(*this, static_cast(reply.arg));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:426:55: error: 'OnPlaySourceRemoved' is not a member of 'Mp3Notify'
T_NOTIFICATION_METHOD::OnPlaySourceRemoved(*this, static_cast(reply.arg));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
c:\Users\valer\OneDrive\���������\Arduino\libraries\DFPlayer_Mini_Mp3_by_Makuna\src/DFMiniMp3.h:430:43: error: no matching function for call to 'Mp3Notify::OnError(DFMiniMp3&, uint16_t&)'
T_NOTIFICATION_METHOD::OnError(*this, reply.arg);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:22:15: note: candidate: static void Mp3Notify::OnError(uint16_t)
E:\adruino\поворотники\CANbus_reciever_cobalt_r4\CANbus_reciever_cobalt_r4.ino:22:15: note: candidate expects 1 argument, 2 provided
exit status 1
Compilation error: exit status 1