Hallo erstmal an alle, hoffe das ihr mir helfen könnt.
Zu meinem Problem:
Ich betreiben ein Android Tablet im Auto als Car PC, funktioniert auch alles super bis auf die Multifunktionstasten am Lenkrad um z.B. ein Lied vor oder zurückschalten. Hab lange im Internet nach der Lösung des Problems gesucht und bin auch fündig geworden.
Die Lösung ist: ein Arduino + CANdiy-Shield von Watterott, den passenden Code gabs auch dazu. Leider war der nur für den UNO gedacht, ich habe mir aber einen Leonardo geholt, weil er angeblich besser sein sollte und bereits als eine USB tastaur erkannt wird. Weniger Arbeit hieß es und unkompliziert sollte das sein,..von wegen.
Es gab zum Glück auch eine angepasste Version für den Leonardo, diese hat mir aber nichts gebracht die Lenkradtasten werden nicht erkannt. Die Verbindung zum CAN BUS besteht, wenn ich den Schlüssel reinstecke dann meldet er es sofort. Hier noch der Code, leider ist der zu lang und ich muss ihn in seperaten Post´s schicken:
/* BMW Interface
K-CAN-Interface für 1er BMWs - Lenkradfernbedienung, Zündungsplus etc.
Hardware: Arduino Uno R3, CanDIY Shield (http://www.watterott.com)
Bibliotheken:
- HIDKeyboard (https://github.com/SFE-Chris/UNO-HIDKeyboard-Library)
- MCP2515 (https://github.com/watterott/Arduino-Libs/tree/master/MCP2515)
(c) 2014 by Christoph Rasim, christoph@rasim.net.
Die Veröffentlichung veränderter Versionen ist gestattet, sofern mein Name genannt wird.
Jegliche kommerzielle Nutzung ohne mein Einverständnis ist untersagt.
*/
#include <HIDKeyboard.h>
#include <SPI.h>
#include <MCP2515.h>
// Pins für die Signalenausgaben
#define STATUS_LED 8
#define SIGNAL_REVERSE 4 // Rückwärtsgang
#define SIGNAL_IGNITION 5 // Zündung
#define SIGNAL_KEY 6 // Schlüssel gesteckt
#define SIGNAL_LIGHT 7 // Licht an?
#define NUM_KEYS 8
#define PRELL_TIMEOUT 110 // Delay für Entprellen der Tasten
#define REPEAT_TIMEOUT 220 // Timeout für Tastenwiederholung
enum BMW_Keys {Key_Up = 1, Key_Down, Key_VolumeUp, Key_VolumeDown, Key_Telephone, Key_Voice, Key_Rotate, Key_Disc};
enum Key_States {NotPressed = 0, Pressed};
enum Status_States {WaitForCan = 0, CanIdle, CanSignal};
CANMSG msg;
MCP2515 can;
// check welches Board eingestellt ist (Arduino Uno oder Arduino Leonardo)
#ifndef USBCON
// Arduino Uno mit HIDKeyboard FW auf zweitem uC
#include <HIDKeyboard.h>
HIDKeyboard keyboard;
#else
// Arduino Leonardo
#define keyboard Keyboard
#define pressKey(x) press((x))
#define pressSpecialKey(x) press((x))
#define releaseKey() releaseAll()
#define F1 KEY_F1
#define F2 KEY_F2
#define F3 KEY_F3
#endif /* if defined(USBCON) */
// ---------------------------------
// Einstellungen
// ---------------------------------
//#define SERIAL_DEBUG // Aktivieren, um serielle Debugausgaben zu erhalten
// Gleiche Lenkradtaste zweimal schnell hintereinander als eigenes Ereignis behandeln -> weitgehend ungetestet
boolean enable_double_keys = false;
// Langes Drücken einer Lenkradtaste eigenes Ereignis behandeln -> weitgehend ungetestet
boolean enable_long_keys = false;
// Repeat-Ereignis beim gedrückt halten einer Lenkradtaste als eigenes Ereignis behandeln -> weitgehend ungetestet
boolean enable_repeat_keys = false;
// ---------------------------------
long last_timer = 0;
boolean reverse = false, light_on = false, ignition = false, key_in = false;
boolean heartbeat = false;
int heartbeat_counter = 0;
char status = WaitForCan;
int status_timer = 0;
boolean track_keys_enabled = false;
int key_status[NUM_KEYS];
int key_timer[NUM_KEYS];
char key_count[NUM_KEYS];
bool key_sent[NUM_KEYS];
// Fehler auf dem CAN-Bus -> StatusLED an, Endlosschleife
void can_error() {
digitalWrite(STATUS_LED, HIGH);
while (1);
}
// Initialisierung
void setup()
{
for (int i=0;i<NUM_KEYS;i++) key_status[i]=0;
pinMode(STATUS_LED, OUTPUT);
pinMode(SIGNAL_REVERSE, OUTPUT);
pinMode(SIGNAL_IGNITION, OUTPUT);
pinMode(SIGNAL_KEY, OUTPUT);
pinMode(SIGNAL_LIGHT, OUTPUT);
#ifdef SERIAL_DEBUG
Serial.begin(115200);
while(!Serial);
Serial.println("Go...");
#else
keyboard.begin();
#endif
// SPI Init
SPI.setClockDivider(SPI_CLOCK_DIV8);
// CAN Init
if(can.initCAN(CAN_BAUD_100K) == 0) can_error();
if(can.setCANNormalMode(LOW) == 0) can_error();
}
// Status-LED kurz blinken lassen.
void led_keypress() {
status = CanSignal;
digitalWrite(STATUS_LED, HIGH);
status_timer=0;
}
// Periodic 1ms timer function
void process_timer()
{
// Heartbeat solange auf den Bus gewartet wird.
if (status == WaitForCan) {
heartbeat_counter++;
if (heartbeat_counter==1000) {
heartbeat_counter=0;
heartbeat = !heartbeat;
if (heartbeat) digitalWrite(STATUS_LED, HIGH); else digitalWrite(STATUS_LED, LOW);
}
} // if (status ==...
// LED nach Tastendruck für 300 ms anlassen
if (status == CanSignal) {
status_timer++;
if (status_timer==500) {
digitalWrite(STATUS_LED, LOW);
status = CanIdle;
}
} // if (status == CanSignal)
// Tastenstatus periodisch aktualisieren
for (int key=0;key<NUM_KEYS;key++) {
if (key_status[key]!=NotPressed) {
key_timer[key]++;
if ((key_status[key] == Pressed) && (!key_sent[key]) && (key_timer[key] >REPEAT_TIMEOUT)) {
// Keine Repeat oder Long Press erkannt -> einfacher Tastendruck
#ifdef SERIAL_DEBUG
Serial.print("Key ");
Serial.print(key);
Serial.println(" single press.");
#endif
ReactOnSingleKey(key);
key_status[key] = NotPressed;
}
if ((key_status[key] == Pressed) && (key_sent[key]) && (key_timer[key] >PRELL_TIMEOUT)) {
// Long press und keine weitere Taste oder Lange Tastendrücke deaktiviert -> Abbruch
key_status[key] = NotPressed;
}
} // if
} // for
}
// Auf einen einfachen Druck einer Lenkradtaste reagieren
void ReactOnSingleKey(int key) {
if (key==Key_Telephone) track_keys_enabled = !track_keys_enabled;
if ((key==Key_Up) && (track_keys_enabled)) {
#ifdef SERIAL_DEBUG
Serial.println("Key F1 sent");
#else
keyboard.pressSpecialKey(F1);
keyboard.releaseKey();
#endif
}
if ((key==Key_Down) && (track_keys_enabled)) {
#ifdef SERIAL_DEBUG
Serial.println("Key F2 sent");
#else
keyboard.pressSpecialKey(F2);
keyboard.releaseKey();
#endif
}
if (key==Key_Voice) {
#ifdef SERIAL_DEBUG
Serial.println("Key F3 sent");
#else
keyboard.pressSpecialKey(F3);
keyboard.releaseKey();
#endif
}
}
// Event der Lenkradfernbedienung verarbeiten
void process_key(int key) {
int value;
// Status LED blinken
led_keypress();
switch (key_status[key]) {
case NotPressed: key_status[key] = Pressed;
key_timer[key]=0;
key_count[key]=0;
key_sent[key]=false;
break;
case Pressed: if (key_timer[key]>PRELL_TIMEOUT) {
key_timer[key]=0;
key_sent[key]=true;
if (enable_double_keys) {
#ifdef SERIAL_DEBUG
Serial.print("Key ");
Serial.print(key);
Serial.println(" double press.");
#endif
} else {
#ifdef SERIAL_DEBUG
Serial.print("Key ");
Serial.print(key);
Serial.println(" single press.");
#endif
ReactOnSingleKey(key);
}
} else {
key_count[key]++;
key_timer[key]=0;
if (enable_long_keys) {
if (key_count[key]==4) {
// die ersten drei zählen wir als Prellen. Der vierte ist ein langer Druck.
key_sent[key]=true;
#ifdef SERIAL_DEBUG
Serial.print("Key ");
Serial.print(key);
Serial.println(" long press.");
#endif
}
if ((key_count[key]>8) && (enable_repeat_keys)) {
#ifdef SERIAL_DEBUG
Serial.print("Key ");
Serial.print(key);
Serial.println(" repeated press.");
#endif
}
} else {
if (key_count[key]==2) {
// die ersten zwei zählen wir als Prellen. Der dritte ist ein langer Druck.
key_sent[key]=true;
#ifdef SERIAL_DEBUG
Serial.print("Key ");
Serial.print(key);
Serial.println(" single press.");
#endif
ReactOnSingleKey(key);
}
if ((key_count[key]>5) && (enable_repeat_keys)) {
#ifdef SERIAL_DEBUG
Serial.print("Key ");
Serial.print(key);
Serial.println(" repeated press.");
#endif
}
} // else
}
} // switch
}
void loop()
{
und hier gehts weiter
:
[code] // CAN Empfang
int can_received = can.receiveCANMessage(&msg, 1);
if(can_received)
{
switch(msg.adrsValue) { // Interessante Nachrichten auswerden
case 0x1D6: // Lenkradtasten
if ((msg.data[0] == 0xC8) && (msg.data[1] == 0x0C)) process_key(Key_VolumeUp);
if ((msg.data[0] == 0xC4) && (msg.data[1] == 0x0C)) process_key(Key_VolumeDown);
if ((msg.data[0] == 0xE0) && (msg.data[1] == 0x0C)) process_key(Key_Up);
if ((msg.data[0] == 0xD0) && (msg.data[1] == 0x0C)) process_key(Key_Down);
if ((msg.data[0] == 0xC1) && (msg.data[1] == 0x0C)) process_key(Key_Telephone);
if ((msg.data[0] == 0xC0) && (msg.data[1] == 0x0D)) process_key(Key_Voice);
if ((msg.data[0] == 0xC0) && (msg.data[1] == 0x1C)) process_key(Key_Rotate);
if ((msg.data[0] == 0xC0) && (msg.data[1] == 0x4C)) process_key(Key_Disc);
break;
case 0x3B0: // Rückwärtsgang
if (reverse && (msg.data[0] == 0xFD)) {
reverse = false;
digitalWrite(SIGNAL_REVERSE, LOW);
#ifdef SERIAL_DEBUG
Serial.println("Leave reverse");
#else
#endif
} else if ((!reverse) && (msg.data[0] == 0xFE)) {
reverse = true;
digitalWrite(SIGNAL_REVERSE, HIGH);
#ifdef SERIAL_DEBUG
Serial.println("Enter reverse");
#else
#endif
}
break;
case 0x21A: // Lichtstatus
if (light_on && (msg.data[0]<4)) {
light_on = false;
digitalWrite(SIGNAL_LIGHT, LOW);
#ifdef SERIAL_DEBUG
Serial.println("Light switched off");
#else
#endif
} else if ((!light_on) && (msg.data[0]>=4)) {
light_on = true;
digitalWrite(SIGNAL_LIGHT, HIGH);
#ifdef SERIAL_DEBUG
Serial.println("Light switched on");
#else
#endif
}
break;
case 0x130: // Zündung
// Teil 1: Schlüssel
if (key_in && (msg.data[0]<65)) {
key_in = false;
digitalWrite(SIGNAL_KEY, LOW);
#ifdef SERIAL_DEBUG
Serial.println("Key removed");
#else
#endif
} else if ((!key_in) && (msg.data[0]>=65)) {
key_in = true;
digitalWrite(SIGNAL_KEY, HIGH);
#ifdef SERIAL_DEBUG
Serial.println("Key inserted");
#else
#endif
}
// Teil 2: Motor
if (ignition && (msg.data[0]<69)) {
ignition = false;
digitalWrite(SIGNAL_IGNITION, LOW);
#ifdef SERIAL_DEBUG
Serial.println("Ignition off");
#else
#endif
} else if ((!ignition) && (msg.data[0]>=69)) {
ignition = true;
digitalWrite(SIGNAL_IGNITION, HIGH);
#ifdef SERIAL_DEBUG
Serial.println("Ignition on");
#else
#endif
}
break;
default: // Alle übrigen Nachrichten
// Direkt nach der Initialisierung warten wir auf die erste gültige CAN-Nachricht
if (status == WaitForCan) {
digitalWrite(STATUS_LED, LOW);
status = CanIdle;
}
} // switch
} // if (can_received)
// 1ms-Timer
if (millis()>last_timer) {
process_timer();
last_timer = millis();
} // if (millis()>...
} // Loop
[/code]
Tip für Debug Ausgaben mit Serial. Sehr schön geht es wenn du dir diese Makros machst:
#define DEBUG 1
#if DEBUG > 0
#define debugPrint(text) Serial.print(text)
#define debugPrintln(text) Serial.println(text)
#else
#define debugPrint(text)
#define debugPrintln(text)
#endif
Dann verwendest du für alle Debug Ausgaben debugPrint(). Wenn DEBUG ungleich 0 ist wird das durch Serial ersetzt. Wenn DEBUG 0 ist wird es praktisch durch eine leere Zeile ersetzt.
So braucht man nicht im Code ständig #ifdef #endif zu schreiben
Und um RAM zu sparen bietet es sich an jedesmal wo print() verwendet wird ein F() um den String zu machen:
Serial.println(F("String im Flash"));
Sonst landen die Strings im RAM
Hat aber nichts mit deinem eigentlichen Problem zu tun ![]()
Trotzdem danke für die Tipps, werde es ergänzen ![]()