-
Masz rację, ale babol z D7, ale z drugiej strony nie jest ona niezbędna, po prostu to zabezpieczenie nie zadziała. Diody D2 i D3 wrzuciłem, ponieważ jak ich nie było to miałem problem z wgraniem programu do uC. Chodzi o to, że pozostałe elementy dostawały napięcie z arduino po podłączeniu do usb i wydaje mi się, że LM393 coś mieszał przy komunikacji. W sumie mogłaby zostać tylko D3, ale o spadkach napięć nie pomyślałem. Jutro sprawdzę rzeczywiste napięcie i pomyślę nad tym problemem. Co do kondensatora C5, czytałem, że powinien tam być kondensator elektrolityczny i ceramiczny, ale czytałem już o tym jak płytki były już zamówione. Myślisz, że sam elektrolit wystarczy, czy jednak zastosować 10uF + 100nF ceramiczny? (w ogóle wlutowałem tam ceramicznego 100nF :D) Pobór prądu to ok 80mA z załączonym przekaźnikiem. Wielkie dzięki za pomoc :thumbright:
-
Zostaw D7, tylko wepnij ją szeregowo. Generalnie tak jak masz teraz też powinno zadziałać, aczkolwiek skutkiem odwróconego napięcia zasilania będzie spalony bezpiecznik. I tutaj jest małe ale, dioda przez czas potrzebny do spalenia bezpiecznika musi przeżyć :) W radiach samochodowych stosuje się takie zabezpieczenie, z tym, że tam dioda jest znacznie większa.
Programujesz przez ISP? Jeśli tak to problemem może być wpięty FIS. Zauważ, że sygnały ze złącza programatora są równocześnie używane przez FIS jak i przez ISP.
Jeśli programujesz przez USB to sugeruję takie rozwiązanie. Wyrzuć diody D2 i D3. Pin nr 14 z Arduino podepnij bezpośrednio do GND, odepnij 5V od pinu nr 12. Podepnij natomiast 12V do pinu nr 15, 12V weź zza diody zabezpieczającej przed odwrotnym napięciem.
100nF dajesz blisko nóżki zasilania procka czy op-ampu. 100 uF daj obok stabilizatora, przy takiej pojemności odległość od filtrowanego urządzenia nie jest już tak krytyczna.
-
Zmierzyłem napięcie jakie dochodziło do uC - 3,6V! ...i jakoś działało :D Ale dostosowałem się do Twoich uwag, zmieniłem kondensatory, przerobiłem zasilanie arduino, usunąłem diodę D7, D3 i D2 (D2-teraz jest tam zworka), dodałem diodę D9 (zabezpieczającą przed odwrotnym podłączeniem) i D10 (bez niej nadal miałem problemy z programowaniem, lecz spadek 1,2V przy zasilaniu 12V jest nieistotny) teraz układ jest zgodny ze schematem:
https://images84.fotosik.pl/927/348f4dc2ec5ac260med.png
Bałem się trochę zasilać arduino z 12V, ponieważ naczytałem się opinii, że wbudowany stabilizator jest kiepski i mocno się nagrzewa, ale jednak daje radę w tym zastosowaniu. Pobór prądu 95mA przy 11,5V.
Teraz tak patrzę na schemat, w sumie mogłem dać D9 za bezpiecznikiem a nie przed, ale to szczegół, który łatwo można przerobić.
-
Od kogo można kupić obecne cudo? :)
-
Jak ktoś chce, może sam sobie takie coś złożyć. Schemat jest, program udostępnię, tylko to jeszcze nie było testowane w aucie na gotowo.
-
Każde LDO będzie się grzało :) Kwestia tylko czy zdoła wytracić ciepło, czy zabezpieczenie termiczne zadziała. Jeśli jest wbudowane oczywiście ;)
Tak w ogóle, to ten komparator można zastąpić TJA1021 -> są w tme. Linia K-line to tak naprawdę LIN z delikatnie zmodyfikowanym softem. Aplikacja jest banalnie prosta, mógłbyś skasować kilka elementów :) Dziwi mnie nieco obecność rezystora R6, montuje się go w przypadku, gdy Twoje urządzenie byłoby Masterem. Przypuszczam, że tak nie jest. Z drugiej strony brakuje kondensatora 220 pF podpiętego między pin K a GND.
-
Z tym masterem różnie bywa, raz ECU nim jest a raz uC https://www.blafusel.de/obd/obd2_kw1281.html#10
Wzorowałem się na tym schemacie: https://www.google.pl/search?q=obd2+...qb1cRGHSm7tPM:
Wersje na tranzystorach, transoptorach i z wykorzystaniem linii K i L nie chciały współpracować. Faktycznie nie próbowałem bardziej wyspecjalizowanych chipów, ponieważ jak mi zadziałał LM393 to nie drążyłem dalej tematu i po prostu przyjąłem ten schemat za odpowiedni. Dołożę kondensator, ale na razie nie zmienię układu do komunikacji ze względu na to, że musiałbym mocno przerobić płytkę, którą już mam fizycznie. Ale jak już w pełni uruchomię obecną wersję to wezmę TJA1021 pod uwagę. Dzięki za rady, są one dla mnie bardzo cenne, ponieważ ja się dopiero tego wszystkiego uczę.
Dorzucam program na arduino aby każdy zainteresowany mógł się pobawić, ulepszyć itp.
http://www37.zippyshare.com/v/RRYomYzL/file.html
#Edit
Korzystając z okazji wyjętych zegarów (były na wymianie wyświetlacza u mieto3) przygotowałem instalację do podłączenia układu:
U1.1 - zworka z U1.3
U1.2 - zworka z U1.4
U7.2(data) - szara wtyczka zegarów - pin 13 (szaro-czerwony),
U7.3(enable) - szara wtyczka zegarów - pin 11 (czerwono-żółty),
U7.4(clock) - szara wtyczka zegarów - pin 12 (czerwono-brązowy),
U8.1(12V) - niebieska wtyczka zegarów - pin 1 (fioletowo-czarny),
U8.2(K) - zielona wtyczka zegarów - pin 28 (szaro-biały) lub złącze OBD pin 7 (szaro-biały) <- są ze sobą połączone,
U8.4(GND) - niebieska wtyczka zegarów - pin 9 lub 24 (brązowy),
U10.1(dół wejście) - szara wtyczka zegarów - pin 18(niebieski) <- przewód przeciąć i podłączyć od strony manetki,
U10.2(góra wejście) - szara wtyczka zegarów - pin 17(zielony) <- przewód przeciąć i podłączyć od strony manetki,
U10.3(dół wyjście) - szara wtyczka zegarów - pin 18(niebieski) <- podłączyć od strony zegarów,
U10.5(góra wyjście) - szara wtyczka zegarów - pin 17(zielony) <- podłączyć od strony zegarów,
U10.6(reset) - szara wtyczka zegarów - pin 19(fioletowy),
Dobrałem jakąś obudowę (Z34B) i wyprowadziłem przewody z przyłączami. Dodatkowo wyprowadziłem bezpiecznik na zewnątrz, dla lepszego dostępu. Nie wygląda to jakoś wybitnie, ale tragedii też nie ma :D
https://images81.fotosik.pl/945/5b8fe6ea77ad5a46med.jpg
https://images83.fotosik.pl/944/296f0c2ff0581b5dmed.jpg
Zrobiłem pierwszy test gotowego urządzenia i wypadł pozytywnie, wszystko działa zgodnie z założeniami.
Dodatkowo dodałem tekst powitalny po uruchomieniu oraz zapis aktualnie wyświetlanej informacji aby po restarcie wyświetlał to samo i nie było trzeba za każdym razem ustawiać.
Aktualny kod:
Kod:
//v2.6.5.1
#include "A3OBD2.h"
#include "RDSFIS.h"
#include <EEPROM.h>
//*******************************wpisujemy co ma nam wyswietlac*******************************
String displayName[] = {"I.A.TEMP", "N75", "BOOST", "SUPPLY", "MAF"};//kolejne nazwy, max 8 znakow i tylko wielkie litery
byte groupNumber[] = {118, 118, 118, 4, 3}; //numer grupy, mozna sprawdzic np. VCDSem
byte fieldNumber[] = {2, 3, 4, 2, 2}; //numer okna j/w
byte maxPage = 5; //ilosc danych do wyboru (jak wyzej jest 5 wartosci to wpisujemy 5)
//********************************************************************************************
byte currPage = 0; // indeks wyswietlanej informacji na starcie (+1)
int baudrateECU = 10400; //szybkosc transmisji z ECU 10400 - a3
int refreshRateFis = 500; //co ile milisekund ma odswiezac informacje na fisie - min 500ms
long fisWriteLastRefresh = 0;
byte faultNumber = 0;
byte otherInscription = 0;
long resetLastPress = 0;
long resetRate = 500; //czas miedzy kliknieciami RESET na manetce do przelaczenia sterowania
int resetCounter = 0; //
boolean toggle = false;
byte IfWelcome = 1; //1 wyswietla tekst powitalny, 0 nie wyswietla
String fisWelcomeLine1 = "SIEMANO!"; // 1sza linia tekstu powitalnego
String fisWelcomeLine2 = "AUDI A3"; // 2ga linia tekstu powitalnego
long eepromLastUpdate = 0;
#define pin_BtnReset 2 //pin 2, przycisk Reset
#define pin_BtnUp 3 // pin 3, przycisk Gora
#define pin_BtnDown 6 // pin 6 i 3, przycisk Dol
#define pin_ChangeFIS 17 // 17/14, stan niski - sterowanie oryginalna czescia, stan wysoki - sterowanie gorna czescia
#define pinKLineRX 0 //RX - komunikacja z LM339
#define pinKLineTX 1 //TX - komunikacja z LM339
A3OBD2 a3obd2(pinKLineRX, pinKLineTX); // RX, TX
#define FIS_WRITE_ENA 11 //FIS ENA 15/11
#define FIS_WRITE_CLK 12 //FIS CLK 14/12
#define FIS_WRITE_DATA 10 //FIS DATA 16/10
RDSFIS rdsfis(FIS_WRITE_CLK, FIS_WRITE_DATA, FIS_WRITE_ENA);
void connectECU() {
rdsfis.FIS_WRITE_line1 = "DATA";
rdsfis.FIS_WRITE_line2 = "READING";
otherInscription = 2;
updateCluster();
delay(1000); // nie jestem pewny czy to potrzebne
a3obd2.connect(ADR_Engine, baudrateECU);
}
void updateCluster() {
if (otherInscription == 0) {
if ( (a3obd2.errorTimeout != 0) || (a3obd2.errorData != 0) ) { //jesli sa bledy w laczeniu lub pobieraniu danych to:
rdsfis.FIS_WRITE_line1 = "TO: ";
rdsfis.FIS_WRITE_line1 += String(a3obd2.errorTimeout);
rdsfis.FIS_WRITE_line2 = "DA: ";
rdsfis.FIS_WRITE_line2 += String(a3obd2.errorData);
//}
} else { //jesli wszystko jest ok to wyswietlaj na kolejnych stronach
if (currPage != 0) {
rdsfis.FIS_WRITE_line1 = displayName[currPage - 1]; //wyswietlaj nazwe
rdsfis.FIS_WRITE_line2 = a3obd2.readSensorsData[fieldNumber[currPage - 1] - 1]; //wyswietlaj wartosc
} else {
rdsfis.FIS_WRITE_line1 = "FAULTS:";
rdsfis.FIS_WRITE_line2 = "";
if (a3obd2.faultsCount > 0) {
if (faultNumber >= a3obd2.faultsCount) {
faultNumber = 0;
}
rdsfis.FIS_WRITE_line2 += a3obd2.readFaultsData[faultNumber];
faultNumber++;
} else {
rdsfis.FIS_WRITE_line2 = "NO FAULT";
}
}
}
} else {
otherInscription--;
}
if (millis() - fisWriteLastRefresh >= refreshRateFis) {
// tu mozna kombinowac z przewijaniem tekstu, lub zmiana wyswietlanyhch danych
rdsfis.FIS_WRITE_sendTEXT(rdsfis.FIS_WRITE_line1, rdsfis.FIS_WRITE_line2);
fisWriteLastRefresh = millis();
}
}
void BtnReset()
{
resetLastPress = millis();
resetCounter++;
if (resetCounter > 1) {
toggle = !toggle;
digitalWrite(pin_ChangeFIS, toggle);
resetCounter = 0;
}
}
void welcome()
{
delay(600);
rdsfis.FIS_WRITE_line1 = fisWelcomeLine1;
rdsfis.FIS_WRITE_line2 = fisWelcomeLine2;
otherInscription = 2;
updateCluster();
IfWelcome = 0;
}
void BtnUpDown()
{
//Down
if (digitalRead(pin_BtnDown) == HIGH)
{
if (currPage <= 0) currPage = maxPage + 1;
currPage--;
fisWriteLastRefresh = 0;
}
else
//Up
{
currPage++;
if (currPage > maxPage) currPage = 0;
fisWriteLastRefresh = 0;
}
}
void setup() {
pinMode(pin_BtnReset, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pin_BtnReset), BtnReset, FALLING);
pinMode(pin_BtnUp, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pin_BtnUp), BtnUpDown, FALLING);
pinMode(pin_BtnDown, INPUT_PULLUP);
pinMode(pin_ChangeFIS, OUTPUT);
//currPage = (EEPROM.read(1)<<8)+EEPROM.read(0);
currPage = EEPROM.read(255);
if (currPage < 0) {
currPage = 0;
}
}
void loop() {
if (IfWelcome == 1)
{
welcome();
}
//zeruje licznik zliczajacy wcisniecia przycisku reset w zadanym czasie
if (millis() - resetLastPress > resetRate) {
resetCounter = 0;
}
if (a3obd2.currAddr != ADR_Engine) {
connectECU();
} else {
if (currPage != 0) {
a3obd2.readSensors(groupNumber[currPage - 1]); //czytaj kanal wpisany na danej pozycji tablicy
} else {
a3obd2.readFaults(); //sprawdzaj bledy
}
updateCluster();
}
////aktualizacja wyświetlanego kanału (co ok 10s)
if (millis() - eepromLastUpdate >= 10000) {
EEPROM.update(255, currPage);
eepromLastUpdate = millis();
}
}
-
Lekka korekta programu.
Nowy setup():
Kod:
void setup() {
pinMode(pin_BtnReset, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pin_BtnReset), BtnReset, FALLING);
pinMode(pin_BtnUp, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pin_BtnUp), BtnUpDown, FALLING);
pinMode(pin_BtnDown, INPUT_PULLUP);
pinMode(pin_ChangeFIS, OUTPUT);
//currPage = (EEPROM.read(1)<<8)+EEPROM.read(0);
currPage = EEPROM.read(255);
if ((currPage < 0) || (currPage > maxPage)) {
currPage = 0;
}
}
-
Jak tam projekt, może jakieś fotki wrzucisz jak to wygląda na wyświetlaczu. No i trzeba by się zastanowić nad produkcją seryjną :) czekam w kolejce.
-
Trochę uporządkowałem kod, teraz funkcja updateCluster() służy tylko do wysyłania tekstu na wyświetlacz i nie zawiera niepotrzebnych i zdublowanych warunków. Dodałem też możliwość kasowania błędów (testowane na biurku i wygląda, że jest ok). Kasowanie odbywa się poprzez dwuklik "Res" podczas wyświetlania błędów, wtedy uC wysyła komendę kasowania, restartuje połączenie i sprawdza ponownie błędy. Jeżdżę już z tym od grudnia z wersją v2.6.5.1 i nie zauważyłem większych nieprawidłowości. Jutro zrobię jakiś filmik z wersją, którą zamieszczam:
A3OBD2.h
należy dodać w 33 linii:
Kod:
bool deleteFaults();
A3OBD2.cpp
należy dodać w 225 linii:
Kod:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//nie podawac zadnych parametrow, funkcja kasuje bledy w ECU silnika
//a3obd2.deleteFaults();
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool A3OBD2::deleteFaults() {
char s[64];
uint8_t fc = 0;
sprintf(s, "\x03%c\x05%c", blockCounter);
if (!KWPSendBlock(s, 4)) return false;
int size = 0;
KWPReceiveBlock(s, 64, size);
if (s[2] != '\xfc') {
disconnect();
errorData++;
return false;
}
disconnect();
return true;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//koniec A3OBD2::deleteFaults();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Odświeżony główny program:
Kod:
//v3.0.3
#include "A3OBD2.h"
#include "RDSFIS.h"
#include <EEPROM.h>
//*******************************wpisujemy co ma nam wyswietlac*******************************
String displayName[] = {"RPM", "I.A.TEMP", "N75", "BOOST", "SUPPLY", "COOLANT", "MAF"};//kolejne nazwy, max 8 znakow i tylko wielkie litery
byte groupNumber[] = {118, 118, 118, 118, 4, 4, 3}; //numer grupy, mozna sprawdzic np. VCDSem
byte fieldNumber[] = {1, 2, 3, 4, 2, 3, 2}; //numer okna j/w
byte maxPage = 7; //ilosc danych do wyboru (jak wyzej jest 5 wartosci to wpisujemy 5)
//********************************************************************************************
byte currPage = 0; // indeks wyswietlanej informacji na starcie (+1)
int baudrateECU = 10400; //szybkosc transmisji z ECU 10400 - a3
int refreshRateFis = 500; //co ile milisekund ma odswiezac informacje na fisie - min 500ms
long fisWriteLastRefresh = 0;
byte faultNumber = 0;
long resetLastPress = 0;
long resetRate = 500; //czas miedzy kliknieciami RESET na manetce do przelaczenia sterowania
int resetCounter = 0; //
boolean toggle = false;
boolean delete_faults = false;
//byte IfWelcome = 1; //1 wyswietla tekst powitalny, 0 nie wyswietla
String fisWelcomeLine1 = "SIEMANO!"; // 1sza linia tekstu powitalnego
String fisWelcomeLine2 = "AUDI A3"; // 2ga linia tekstu powitalnego
long eepromLastUpdate = 0;
#define pin_BtnReset 2 //pin 2, przycisk Reset
#define pin_BtnUp 3 // pin 3, przycisk Gora
#define pin_BtnDown 6 // pin 6 i 3, przycisk Dol
#define pin_ChangeFIS 17 // 17/14, stan niski - sterowanie oryginalna czescia, stan wysoki - sterowanie gorna czescia
#define pinKLineRX 0 //RX - komunikacja z LM339
#define pinKLineTX 1 //TX - komunikacja z LM339
A3OBD2 a3obd2(pinKLineRX, pinKLineTX); // RX, TX
#define FIS_WRITE_ENA 11 //FIS ENA 15/11
#define FIS_WRITE_CLK 12 //FIS CLK 14/12
#define FIS_WRITE_DATA 10 //FIS DATA 16/10
RDSFIS rdsfis(FIS_WRITE_CLK, FIS_WRITE_DATA, FIS_WRITE_ENA);
void connectECU() {
delay(1000); // nie jestem pewny czy to potrzebne
a3obd2.connect(ADR_Engine, baudrateECU);
}
void welcome()
{
delay(3000);
rdsfis.FIS_WRITE_line1 = fisWelcomeLine1;
rdsfis.FIS_WRITE_line2 = fisWelcomeLine2;
updateCluster();
}
void readSensors() {
a3obd2.readSensors(groupNumber[currPage - 1]); //czytaj kanal wpisany na danej pozycji tablicy
rdsfis.FIS_WRITE_line1 = displayName[currPage - 1]; //wyswietlaj nazwe
rdsfis.FIS_WRITE_line2 = a3obd2.readSensorsData[fieldNumber[currPage - 1] - 1]; //wyswietlaj wartosc
updateCluster();
}
void readFaults() {
rdsfis.FIS_WRITE_line1 = "FAULTS:";
rdsfis.FIS_WRITE_line2 = "";
a3obd2.readFaults(); //sprawdzaj bledy
if (a3obd2.faultsCount > 0) {
if (faultNumber >= a3obd2.faultsCount) {
faultNumber = 0;
}
rdsfis.FIS_WRITE_line2 += a3obd2.readFaultsData[faultNumber];
faultNumber++;
} else {
rdsfis.FIS_WRITE_line2 = "NO FAULT";
}
if (delete_faults == true) {
rdsfis.FIS_WRITE_line1 = "DELETING";
a3obd2.deleteFaults(); //kasuj bledy
delete_faults = false;
}
updateCluster();
}
void updateCluster() {
if (millis() - fisWriteLastRefresh >= refreshRateFis) {
if ( (a3obd2.errorTimeout != 0) || (a3obd2.errorData != 0) ) { //jesli sa bledy w laczeniu lub pobieraniu danych to:
rdsfis.FIS_WRITE_line1 = "TO: ";
rdsfis.FIS_WRITE_line1 += String(a3obd2.errorTimeout);
rdsfis.FIS_WRITE_line2 = "DA: ";
rdsfis.FIS_WRITE_line2 += String(a3obd2.errorData);
}
// tu mozna kombinowac z przewijaniem tekstu, lub zmiana wyswietlanyhch danych
rdsfis.FIS_WRITE_sendTEXT(rdsfis.FIS_WRITE_line1, rdsfis.FIS_WRITE_line2);
fisWriteLastRefresh = millis();
}
}
void BtnReset()
{
resetLastPress = millis();
resetCounter++;
if (resetCounter > 1) {
toggle = !toggle;
digitalWrite(pin_ChangeFIS, toggle);
resetCounter = 0;
if (currPage == 0 && toggle == false) {
delete_faults = true;
}
}
}
void BtnUpDown()
{
//Down
if (digitalRead(pin_BtnDown) == HIGH)
{
if (currPage <= 0) currPage = maxPage + 1;
currPage--;
fisWriteLastRefresh = 0;
}
else
//Up
{
currPage++;
if (currPage > maxPage) currPage = 0;
fisWriteLastRefresh = 0;
}
}
void setup() {
pinMode(pin_BtnReset, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pin_BtnReset), BtnReset, FALLING);
pinMode(pin_BtnUp, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pin_BtnUp), BtnUpDown, FALLING);
pinMode(pin_BtnDown, INPUT_PULLUP);
pinMode(pin_ChangeFIS, OUTPUT);
currPage = EEPROM.read(255);
if ((currPage < 1) || (currPage > maxPage)) {
currPage = 1;
}
welcome();
}
void loop() {
//zapis do eepromu nr aktualnego kanalu jesli sie zmienil (co ok 10s)
if (millis() - eepromLastUpdate >= 10000) {
EEPROM.update(255, currPage);
eepromLastUpdate = millis();
}
//zeruje licznik zliczajacy wcisniecia przycisku reset w zadanym czasie
if (millis() - resetLastPress > resetRate) {
resetCounter = 0;
}
if (a3obd2.currAddr != ADR_Engine) {
connectECU();
} else {
if (currPage != 0) {
readSensors(); //czytaj kanal
} else {
readFaults(); //czytaj bledy
}
}
}
Produkcji seryjnej nie przewiduję, mam kilka zapasowych płytek, jak będę miał więcej czasu to je polutuję i będą na sprzedaż. Jak ktoś czuje się na siłach to mogę takiej osobie również wysłać samą płytkę. Robię to jedynie hobbystycznie, dla tego wolałbym aby pierwszeństwo mieli ci, którzy również chcieliby się przyczynić w jakiś sposób do rozwoju projektu, np. taz, doceniam Twoją pmoc, dla tego masz pierwszeństwo, jeśli chciałbyś mieć to u siebie. Od razu zaznaczam, że nie gwarantuję działania układu w innych samochodach. Każdy podłącza go na swoją odpowiedzialność, jedynie mogę zagwarantować, że każdą płytkę będę testował w swoim aucie.
#edit
Sprawdzałem też sytuację, gdy chcę się połączyć laptopem z ECU - aby komunikacja działała trzeba odłączać linię K od uC. Wystarczy zwykły wyłącznik ukryty gdzieś koło bezpieczników.
#edit2
Zgodnie z obietnicą wrzucam filmik, jak aktualnie to wygląda. Niektóre wartości są trochę źle wyświetlane, tzn. obciążenie silnika wyrażone w % niepotrzebnie jest pokazywane w formie 00.00, już to poprawiłem, ale nie wgrywałem tego jeszcze bo to tylko kosmetyka.
https://youtu.be/2e3-u8RaYIA