Przeszukaj forum
Pokazywanie wyników dla tagów 'simracing'.
Znaleziono 2 wyniki
-
simracing Licznik samochodowy współpracujący z grami - projekt CAN bus
MattechPC opublikował temat w Projekty - DIY
Witam wszystkich! Po dłuższej nieobecności na formu chciałbym zaprezentować Wam kilka z moich najnowszych projektów przy okazji po krótce omawiając magistralę CAN bus. *** Wstęp *** Do tej pory opublikowałem dwa wpisy na forum opisujące budowę analogowych liczników samochodowych od Forda Galaxy oraz Audi A3 8L. Są to projekty niezwykle proste w budowie, wystarczy na określony pin podać 12V, masę lub sygnał z Arduino o określonej częstotliwości aby ożywić cały licznik i wszystkie wskazówki. Jednak ze względu na swoją prostotę projekty te posiadają liczne ograniczenia - przede wszystkim nie jesteśmy w stanie idealnie symulować zmiennej rezystancji czujnika poiomu paliwa oraz temperatury cieczy. Dodatkowo większosć kontrolek działa w logice 12V przez co nie można nimi sterować bezpośrednio z Arduino. Wszystkie te ograniczenia obchodzą liczniki, które do komunikacji wykorzystują magistralę CAN bus. *** Założenia projektu *** Projekt licznika miał być przede wszystkim uniwersalny i działać z szerokim wachlarzem gier - od ETS 2 gdzie prędkości obrotowe silnika wynoszą do 2500 RPM a prędkość rzadko kiedy przekracza 120km/h przez Framingi, BeamNG, Asseto Corsa na F1 kończąc gdzie silnik wkręca się powyżej 10.000 RPM a prędkości znacznie wykraczają za maksymalne wychylenie prędkościomierza licznika samochodowego. Licznik umożliwić ma nie tylko odczyt podstawowych parametrów prowadzonego pojazdu tj. obroty silnika, prędkość, temperatura cieczy, oleju czy poziomu paliwa ale także informować o stanie świateł, silnika czy nawet opon. *** Potrzebne materiały *** Arduino MCP2515 Zasilacz 12V (1A w zupełności starczy) Przewody połączeniowe *** Jak to działa? *** Do budowy projektu wykorzystałem dobrze znane większości z Was Arduino NANO wyposażone w USB-C. Dialog z licznikiem prowadzony jest poprzez moduł CAN MCP-2515 a całość zasila 12V zasilacz sieciowy. Całość umieściłem w małej obudowie wydrukowanej na drukarce 3D. Nie wgryzając się za bardzo w szczegóły techniczne - CAN bus to szeregowa magistrala danych wykorzystująca do komunikacji 2 przewody opisywane jako CAN-H oraz CAN-L. Jak łatwo się domyślić na przewodzie CAN-High panuje nieco wyższe napięcie niż na CAN-Low. CAN Bus jest magistralą działającą w sposób różnicowy czyli mierzy i porównuje napięcia na CAN-H oraz CAN-L. W ten sposób odczytywane są bity dominujące i recesywne tworąc ciąg zer i jedynek. Dla ułatwienia projektu nie oparłem o liczby binarne tylko znacznie przyjemniejsze dla oka - heksadecymalne. Ramka CAN wysyłana do licznika składa się z unikalnego identyfikatora (ID) oraz określonej ilości bajtów danych. Każdy bajt danych może przyjąć wartość od 0 do FF czyli decymalnych 255. Dla przykładu aby zapalić kontrolkę hamulca ręcznego w liczniku od Peugeota 407 muszę nadać ramkę o ID 128 a bicie zerowym nadać wartość 24HEX. Wygląda to mniej więcej tak: [ ID 128 0x24 0x00 0x00 0x00 0x00 0x00 0x00 0x00 ]. Pod pozostałymi bitami kryją się kolejne kontrolki tj. kierunkowskazy, światła mijania, drogowe, przeciwmgielne oraz nielubiany przez kierowców check engine. Pod różnymi ID ramek można odnaleźć pozostałe funkcje licznika - sterowanie wskazówkami, podświetleniem, ustawianie przebiegu itd. Warto pamiętać, że niektóre liczniki po otrzymaniu zasilania będą pozornie wyglądać na martwe. Takie konstrukcje potrzebują tak zwanego sygnału przebudzenia, który "obudzi" licznik. Jeśli taki sygnał występuje to jest to pierwszy krok aby zacząć zabawę z licznikiem. *** Projekt licznika *** Do moich projektów wykorzystuję liczniki z samochodów Peugeot oraz Citroen. Magistrala CAN w obu markach jest bliźniaczo podobna i różnią się jedynie małymi szczegółami. Najważniejsze, że obie marki wykorzystują taką samą szybkość magistrali wynosząca 125Kbps. Do kontroli modułu MCP2515 wykorzystałem bibliotekę MCP2515.h. W pierwszej kolejności łączę moduł CAN z Arduino oraz podłączam magistralę wraz z zasilaniem do licznika. Należy pamiętać, że każdy licznik posiadać może swój własny indywidualny pinout. Powyżej można zobaczyć pinout licznika Citroen C3 I FL. Po podłączeniu wszystkich 4 przewodów wgrywam przygotowany wcześniej kod na Arduino. Warto zatrzymać się na chwilę przy kodzie i zastanowić się jak projektant tworzył licznik. Zespół projektujący licznik miał surowe dane - maksymalne obroty silnika 5200 RPM, maksymalna temp. cieczy 130℃, maksymalna temp. oleju 150℃, maksymalna prędkość 260km/h. Licznik przygotowany jest oczywiście na taki zakres danych z minimalnym zapasem jednak po przekroczeniu skali obrotomierza lub wskaźnika temperatury zaczną dziać się różne dziwne rzeczy. Wartości out of range są różnie traktowane przez liczniki - jedne ustawiają wskaźnik na "0", drugie wyłączają się a trzecie mogą się zawiesić. Dane z gier praktycznie zawsze będą wykraczać poza zakresy ustalone przez producenta więc trzeba przeanalizowaćkażdą grę z osobna i zastosować zabezpieczenia oraz skalowanie tak, aby wartości zawsze znajdowały się w akceptowalnym przez licznik zakresie. Kod musi brać także poprawkę na nieliniowość wskaźników temperatury - rzadko kiedy 1° obrotu silnika krokowgo będzie odpowiadał dokładnie takiemu samemu przyrostowi temperatury. Do przykładu - wskazówka temp. cieczy "zastyga" na 90℃ gdy faktyczna temperatura cieczy waha się od ~85-93℃. Dalszy wzrost temperatury będzie powodował niewielki wzrost wychylenia wskazówki. Jednak gdy temperatura niebezpiecznie wzrośnie to wskazówka momentalnie zwiększy swoją czułość i będzie wykonywać znaczny ruch co każdy 1-2℃. *** Dane z gier *** Po przetestowaniu kodu na "suchych" danych wpisanych "z palca" do kodu wypadałoby przestawić się na dane przesyłane z gier do licznika w czasie rzeczywistym. Za przygotowanie danych odpowiada aplikacja SimHUB współpracująca ze specjalnie napisaną przeze mnie formułą NCalc "obrabiającą" surowe dane z gier na zakresy liczbowe zaimplementowane w kodzie. Wygląd danych, którymi "karmię" kod Arduino przedstawiam poniżej. Jest to oczywiście wycinek danych ponieważ ogranicza mnie pole wyświetlające Raw result. *** Efekt końcowy *** Po wielu próbach i testach udało mi się ujarzmić prawie cały licznik. Wszytskie wskazania licznika: prędkość obrotowa silnika prędkość pojazdu poziom paliwa temp. cieczy temp. oleju wskaźnik biegów Kontrolki: hamulec ręczny kierunkowskazy światła mijania, drogowe, przeciwmgielne brak ładowania akumulatora niskie ciśnienie oleju rezerwa przegrzanie cieczy przebita opona przebieg nastawa tempomatu / aktualna prędkość pojazdu niskie ciśnienie w układzie hamulcowym (ETS2/ATS wykorzystując kontrolkę wody w filtrze paliwa) podniesienie osi (ETS2/ATS wykorzystując kontrolkę wyłączonej poduszki powietrznej pasażera) kończącego się czasu pracy (ETS2/ATS wykorzystując kontrolkę uszkodzenia wspomagania kierownicy oraz kontrolkę pasów bezpieczeństwa) przekroczonego limitu prędkości na drodze (ETS2/ATS wykorzystując kontrolkę usterki tempomatu (tylko Peugeot 407)) zbyt wysokich obrotów (ETS2/ATS wykorzystując kontrolkę trójkąta ostrzegawczego) retardera (ETS2/ATS wykorzystując kontrolkę świec żarowych) ekonomicznej jazdy (wykorzystując kontrolkę ECO) działanie ESP działanie ABS Działanie licznika w grach można zobaczyć na poniższych zdjęciach. *** Zbuduj sam własny projekt i odkrywaj tajniki CAN BUS! *** Z tego miejsca chciałbym gorąco zachęcić wszystkich zainteresowanych do spróbowania swoich sił i budowy własnego projektu z licznikiem opartym na magistrali CAN Bus. Elementy niezbędne do budowy projektu oraz same liczniki są bardzo tanie i projekt powinien spokojnie zamknąć sie w 100-150zł. Magistrala CAN grupy PSA jest bardzo dobrze opisana, obszerną dokumentację stanowiącą fundamentalny filar wiedzy można znaleźć pod TYM linkiem. Jeśli do wybranego przez Ciebie licznika nie ma żadnej dokumentacji to nic straconego, wystarczy napisać bardzo prosty generator losowych wartości bajtówki. Metoda ta nazywa się brute force i jest bardzo efektywna. Należy jednak korzystać z niej ostrożnie ponieważ istnieje niewielka szansa na zbrickowanie licznika. Eksperymentowanie z licznikiem oraz CAN Bus'em pozwoli Tobie także lepiej zrozumieć cały proces komunikacyjny, który finalnie zapala nielubiany check engine na liczniku. Za pomocą modułu MCP2515 możesz także podsłuchać co "szumi" w magistrali CAN Twojego samochodu. Jeśli rozszyfrujesz te dane to nic nie stoi na przeszkodzie abyś zaprojektował swój własny wyświetlacz doładowania, cyfrowy prędkościomierz czy wyświetlacz temperatury oleju w silniku. *** Wykorzystaj mój kod! *** Jeśli jednak nie czujesz się na siłach aby zbudować własny licznik to możesz bez problemu wykorzystać opracowane przeze mnie liczniki wraz z kodem na Arduino. Wszelkie informacje odnośnie budowy takiego projektu znajdziesz w poradniku na moim kanale:- 1 odpowiedź
-
- 12
-
-
Miesiąc po skończeniu wstępnego kursu na Forbocie o arduino (w grudniu 2023), zacząłem myśleć jakim projektem się zająć. Na początku próbowałem robić różne proste rzeczy, ale gdy stwierdziłem, że kupię drukarkę 3D to pójdę o krok dalej i wrzucę się na głęboką wodę. I tak zaczęła się przygoda z tworzeniem kierownicy do simracingu na bazie kirownicy F1 z 2023 roku z bolidu mercedesa. W tym też momencie zacząłem się uczyć programów projektowych (fusion 360 itp) i szukać potrzebnych części. Na początku miała to byc prosta kierownica z 12 przyciskami. Ale gdy odkryłem bibliotekę joystick.h to stwierdziłem że idę na całość. Całość zajęła jakieś 1,5 mc. Lącznie z ogarnięciem drukarki która nie chciała współpracować. Znając podstawy dowiedziałem się że da się to zrobić na dowolnych arduino z ATmega32U4. Poniżej znajdziecie też trochę kodu. Na początku (co było złym pomysłem) próbowałem nauczyć się tworzenia płytek PCB nie wiedząc jeszcze jak połącze wszystko. (VCC pomyliłem z GND :D) Stwierdizłem, że zaprojektuje całość, ale nie wiedziałem dokładnie ile miejsca potrzebuje. Wersja miała zawierac tylko przyciski, joystick i 2-3 enkodery. testowo podpięte pod arduino. użyłem większych buttonów podświetlanych. Zostałem przy niebieskich i białych, bo pobierały najmniej pradu. Po przymiarkach stwierdziłem że robie full kierę z F1. Projekt to H005. całość robiona według instrukcji, ale z dużymi zmianami. Większe buttony, enkodery które miałem nie mieściły się. Inne pokrętła, no i całość na jednym arduino. Dodatkowo dokupiłem ekran Vocore 4,3 cala. Projekt był pod 4,0 więc przerobiłem obudowę pod niego. Na początku wpięte wszystkie buttony i enkodery, tak by skręcić całość bez tylnej pokrywy. wszystko sprawdzane 10 razy by nie musieć rozkręcać wszystkiego po złożeniu. Całość obklejona "carbonem" i opaskami do tenisa + tasma izolacyjna. Jeśli chodzi o okablowanie, użyłem płytek by moc na nich ogarnąć buttony i GND. Po podpięciu wszystkiego kabli było na tyle dużo że całość nie zamknęła się w obudowie. Musiałem ją pogrubić. Dodatkowo dodałem 7 way joystick, ale używam tylko 4 kierunkowy + przycisk. Nie starczyło wejść w arduino na więcej. - 12 wejść pod rotary enkodery, - 1 wejście pod 11 buttonów + joystick + manetki, - 2 wejścia pod 2x 12 pos switch, łącznie 52 buttony + ekran Manetki według projektu. Całość po skręceniu. Jedno pokrętło środkowe to potencjometr podpiety do VCC i rezystorem 330ohm by regulować podświetlenie. dodatkowo ekran podpięty na osobnym kablu usb. Same kable usb w środku wpięte do 7 din avaitor kabla, wszystko na jednym kablu z koncowka na 2x usb wpięte do PC. Sam ekran sterowany za pomocą SimHuba. i po naklejeniu naklejek. Po złożeniu całości wyszły problemy z bouncingiem enkoderów. Po dwóch dniach udało mi się rozwiązać problem bez użycia interrupt pinów. pierwsza zmiana to sposób sprawdzania 6 enkoderów. void read_encoder_1() { // Encoder interrupt routine for both pins. // if they are valid and have rotated a full indent Old_AB_1 <<=2; // Remember previous state if (digitalRead(PIN_A_1)) Old_AB_1 |= 0x02; // Add current state of pin A if (digitalRead(PIN_B_1)) Old_AB_1 |= 0x01; // Add current state of pin B encval_1 += enc_states[( Old_AB_1 & 0x0f )]; // Update counter_1 if encoder has rotated a full indent, that is at least 4 steps if( encval_1 > 3 ) { // Four steps forward Joystick.pressButton(12); set_rotary = 12; encval_1 = 0; } else if( encval_1 < -3 ) { // Four steps backward Joystick.pressButton(13); set_rotary = 13; // Update counter_1 encval_1 = 0; } } // Encoder value static const int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0}; // Lookup table Same buttony działają według tego schematu, podpiete pod jednym pinem sprawdzam wartość ograniczając każdy następny 1k rezystorem. Nie bawiłem sie w wyliczenia by było idealnie podzielone na 1023/ ilość buttonów. Po prostu sprawdzałem odczyt i wpisywałem zakresy z marginesem kilku %. Niby jest ograniczenie że nie możemy kliknąć więcej niz jednego przycisku, ale w moim przypadku jeśli klikniemy "wyzszy wartością" przycisk i pozniej kolejny, to dostajemy wieloklik, więc częściowo działa. Dodatkowo kod starałem się tak napisać by buttony do mommentu puszczenia były na stałe wciśnięte i by nie "migotały". //BUTTONS value_buttons = analogRead(A3); rotary12pos2 = analogRead(A1); rotary12pos = analogRead(A2); //Serial.println(value_buttons); if (value_buttons > 815 && value_buttons < 820) { // Joystick.pressButton(0); // delay(button_delay); setbutton = 0; //Serial.println("Button1"); //Serial.println(value_buttons); } else if (value_buttons > 764 && value_buttons < 770) { // Joystick.pressButton(1); // delay(button_delay); setbutton = 1; //Serial.println("Button2"); //Serial.println(value_buttons); } else if (value_buttons > 677 && value_buttons < 683) { // Joystick.pressButton(2); // delay(button_delay); setbutton = 2; //Serial.println("Button3"); //Serial.println(value_buttons); } else if (value_buttons > 507 && value_buttons < 515) { // Joystick.pressButton(3); // delay(button_delay); setbutton = 3; //Serial.println("Button4"); //Serial.println(value_buttons); } else if (value_buttons < 2) { //Joystick.pressButton(4); // delay(button_delay); setbutton = 4; //Serial.println("Button5"); //Serial.println(value_buttons); } else if (value_buttons > 849 && value_buttons < 855) { //Joystick.pressButton(5); // delay(button_delay); setbutton = 5; //Serial.println("Button4"); //Serial.println(value_buttons); } else if (value_buttons > 873 && value_buttons < 880) { // Joystick.pressButton(6); // delay(button_delay); setbutton = 6; //Serial.println("Button4"); //Serial.println(value_buttons); } else if (value_buttons > 891 && value_buttons < 898) { // Joystick.pressButton(7); // delay(button_delay); setbutton = 7; //Serial.println("Button4"); //Serial.println(value_buttons); } else if (value_buttons > 904 && value_buttons < 912) { // Joystick.pressButton(8); // delay(button_delay); setbutton = 8; //Serial.println("Button4"); //Serial.println(value_buttons); } else if (value_buttons > 956 && value_buttons < 960) { // Joystick.pressButton(9); // delay(button_delay); setbutton = 9; //Serial.println("Button4"); //Serial.println(value_buttons); } else if (value_buttons > 918 && value_buttons < 923) { //Joystick.pressButton(10); // delay(button_delay); setbutton = 10; //Serial.println("Button4"); //Serial.println(value_buttons); } else if (value_buttons > 951 && value_buttons < 958) { //Joystick.pressButton(11); // delay(button_delay); setbutton = 11; //Serial.println("Button4"); } else if (value_buttons > 968 && value_buttons < 971) { //Joystick.pressButton(50); // delay(button_delay); setbutton = 50; } else if (value_buttons > 964 && value_buttons < 968) { //Joystick.pressButton(51); // delay(button_delay); setbutton = 51; } else if (value_buttons > 933 && value_buttons < 942) { setbutton = -1; Joystick.setHatSwitch(0, 0); delay(button_delay); setbutton = -1; } else if (value_buttons > 927 && value_buttons < 932) { setbutton = -1; Joystick.setHatSwitch(0, 90); delay(button_delay); } else if (value_buttons > 945 && value_buttons < 952) { setbutton = -1; Joystick.setHatSwitch(0, 180); delay(button_delay); } else if (value_buttons > 940 && value_buttons < 949) { setbutton = -1; Joystick.setHatSwitch(0, 270); delay(button_delay); } //Serial.println(value_buttons); if(value_buttons == 1023){ Joystick.setHatSwitch(0, -1); Joystick.setHatSwitch(0, -1); Joystick.setHatSwitch(0, -1); Joystick.setHatSwitch(0, -1); Joystick.setButton(setbutton,0); for(int i =0; i<12; i++){ Joystick.releaseButton(i); } Joystick.releaseButton(50); Joystick.releaseButton(51); setbutton = -1; } Joystick.setButton(setbutton,1); Niestety przy stałym sprawdzaniu enkoderów i buttonów było to za wolne by enkodery wyłapywały każdy obrót. Stwierdziłem że przez 90% czasu działania kodu bede sprawdzał enkodery, a przez reszte buttony które nie potrzebują aż takiej precyzji. unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; static int counter = 0; static bool flag = false; if (counter < firstPartDuration / 1) { // rotary encoders read_encoder_1(); read_encoder_2(); read_encoder_3(); read_encoder_4(); read_encoder_5(); read_encoder_6(); // If count has changed print the new value to serial if(set_rotary != -1){ delay(70); Joystick.setButton(set_rotary, 0); set_rotary = -1; } } else if (counter < (firstPartDuration + secondPartDuration) / 1) { // CHECK BUTTONS // CHECK ROT SWITCH POS } Dzięki temu w dowolnym momencie poruszając enkoderami, działają idealnie, bez debouncingu, i za każdym razem gdy je przekręce. Co bym zmienił w kolejnej wersji? Na pewno zrobił płytkę PCB z wszystkimi komponentami by podpiąć tylko kable. Projekt który wykonałem jest poniżej. Sama płytka jest dość uniwersalna. I w zalezności co podepniemy to zadziała. Dodatkowo dodałbym rev light za jeden pin do którego jest podpięty 12 rot pos Porównujac z kupną DIY którą miałem do tej pory, jest to spoooory przeskok 😄 Sama kierownica jest bardzo wygodna. Wytrzymała już 10h jeżdzenia na 6Nm kierownicy TS-PC. Nic sie nie rozpada. Jest lekki flex bardziej przez problemy z projektem który w ogóle nie polecam składać bo jest to mordęga. Nowa Stara, kupiona na OLX