Heute stelle ich ein weiteres Projekt vor.
Ich habe mich mal ran gesetzt, einen Sketch zu erstellen, um mit dem Canbus Netzwerk ein wenig zu spielen, da ich noch einige Projekte bzgl. Steuerungen im Auto geplant habe und da kommt mir das gelegen.
Der Spaßfaktor steht natürlich auch im Mittelpunkt.
Später werde ich ggf. noch ein Programm veröffentlichen, welches die Bedienung, Diagnose und Reverse Engineering am PC erleichtert.
Derzeit wird alles via Konsole in der Arduino IDE oder vergleichbares Terminal Programm gemacht.
Der Sketch sieht hier und da noch etwas 'Wüst' aus, aber ich nutze auch mehrere Tabs und muss den Sketch in einer Datei zusammenbasteln.
Ich habe hier und da einige Kommentare drin gelassen, damit man das eine oder das andere leichter nachvollziehen kann, wenn man selbst dran basteln möchte.
Ich gehe mal von aus, dass hier und da noch Fehler auftauchen können und vermutlich auch werden aber so funktioniert Entwicklung nun mal.
Solltet ihr was finden, lasst es mich wissen.
Zukünftige Updates und Changelogs, findet ihr in der neuen Roadmap.
Verdrahtung des Arduinos ist dieselbe wie hier:
Außerdem habe ich die Pinbelegung für NAC, RCC und Smeg hinzugefügt, sowie die entsprechende Baudrate, die benötigt wird.
Sollte das entsprechende Infotainment bereits mit einem Zentralsteuergerät (BSI) verbunden sein, könnt ihr entsprechend auch über die Pins am Infotainment System ebenfalls die Canbus Nachrichten abhören.
Benötigtes Material:
- 1x Arduino Nano
Später erstelle ich hier und da noch kleine Tutorials, wie ihr beispielsweise mit Diagbox oder im Auto, verschiedene Canbus Daten auslesen könnt.
VORWORT : BITTE LESEN
Da gerne vom eigentlichen Thema abgewichen wird, stelle ich ein paar Regeln, speziell für dieses Thema auf, damit das Thema sauber und übersichtlich bleibt.
Wer sich nicht an die Regeln hält, dessen Beitrag wird kommentarlos gelöscht.
- Hier werden keine Fragen aka "Wie aktiviere ich dies, wie aktiviere ich jenes" gestellt.
- Phrasen wie "Geht nicht" oder "Ich hab ein Problem" werden nicht geduldet. Eine detaillierte Fehlerbeschreibung ist (wie überall) zwingend erforderlich.
- Fragen wie "Was macht der Sketch?" werden gelöscht, denn es wird alles in diesem Beitrag beschrieben, diese Frage ist also unnötig. -> Lesen hilft!
- Bei Problemen oder Informationen sind, je nach Situation, immer folgende Daten anzugeben:
- Betriebssystem
- Genutzte Software und Version
- Arduino-Modell
- Verdrahtung (ggf. Foto)
- Schritte zur Reproduktion des Problems
- Bisherige Lösungsversuche
Soll heißen, in diesem Thema geht es ausschließlich um dieses Programm, die Funktionen und Funktionsweise, sowie die erforderliche und genutzte Hardware.
Was macht dieser Sketch?
Ganz einfach!
Mit dem Sketch kann man Canbus Nachrichten zu diagnostischen Zwecken auslesen und auch Canbus Nachrichten senden.
Unterstützt werden Single Frames und Multi Frames, wobei letzteres erstmals nur für's auslesen gilt.
Des weiteren bietet der Sketch hier und da noch nützliche Funktionen, die ihr den Features entnehmen könnt.
Minimale Systemvoraussetzungen
- CPU: Mindestens 8Mhz / Optimalerweise 16Mhz
- Programmspeicherplatz: 24Kb (23,590 Bytes)
- Dynamischen Speicher: 15Kb (1471 Bytes)
- Can Modul: MCP2515 Modul mit 8MHz oder 16MHz Kristalloszillator
Features
- Canbus: Nachrichten auslesen und in verschiedenen Formaten ausgeben
- Sketch Einstellungen: Einstellungen können direkt im Arduino gespeichert werden und bleiben nach einem Neustart erhalten.
- MAN-Filter: Can-ID's können einem Filter hinzugefügt oder entfernt werden, sodass diese nicht ausgegeben werden.
- AUTO-Filter: Filtert automatisch alle ankommenden Can-ID's. Benötigte ID's können anschließend aus dem Filter entfernt und angezeigt werden.
- Verschiedene Sprachen: Der Sketch unterstützt bisher 2 einstellbare Sprachen (Deutsch + Englisch) und kann im Laufe der Entwicklung erweitert werden (Übersetzer sind willkommen)
- Dynamische Can-Modul Einstellungen: Can Geschwindigkeit und Bitrate kann während der Laufzeit geändert werden. (Kein erneutes flashen des Sketches erforderlich)
- Ausgabe Format: Can Nachrichten können, je nach Einstellung, entweder RAW oder den Einstellungen entsprechend ausgegeben werden.
- Eco-Disabler: !ALPHA! Deaktiviert den Eco Modus für 1 Stunde. Kompatibilität muss geprüft werden. (Unterstützer sind willkommen)
PSA Kompatibilität:
Single Frames senden:
Das erste Byte (Full Data Length) wird automatisch berechnet, wenn man eine Can-Nachricht sendet.
Single frames (1-7 Bytes) empfangen:
Das erste Byte (Full Data Length) wird automatisch entfernt, wenn man eine Can-Nachricht empfangen wird.
Multi frames (>7 Bytes) senden:
Noch nicht unterstützt.
Multi frames empfangen:
FullData Length und Startbytes werden entfernt.
Multiframes können als Einzeiler ausgegeben werden.
(Frames können verloren gehen. Entwicklung läuft...)
Sonstige Informationen & Hinweise
Information beim erneuten flashen des Sketches:
Die Einstellungen im EEPROM bleiben auch beim erneuten flashen des Sketches erhalten.
Es ist nicht auszuschließen, dass trotz interne Überprüfungen, durch Sketch-Updates, doch mal Komplikationsfehler auftauchen können.
Sollte es Probleme mit zukünftigen Versionen geben, führt den Befehl clearEEPROM aus und löscht die Einstellungen.
Danach werden die Standardwerte geladen.
Das Programm wird dabei nicht gelöscht.
How To Use
Befehl | Notiz / Beispiel | Funktion |
---|---|---|
>123:123 | >CAN_TX:CAN_RX | Ändert das ID-Pärchen, welches zum Senden und empfangen eines spezifischen Steuergeräts verwendet werden kann. Unabhängig des eingestellten ID-Pärchens, werden alle Can Frames empfangen. Wenn Can-Nachrichten geschickt werden, dann werden diese an die eingestellte CAN_TX ID gesendet. |
? | Zeigt Sketch-Informationen an | |
wiring | Zeigt Verdrahtungsinformationen zwischen Mikrocontrollers und MCP2515 Modul | |
save | Speichert Einstellungen im EEPROM des Mikrocontrollers. | |
clearEEPROM | Löscht das EEPROM des Arduinos. Alle gespeicherten Einstellungen gehen verloren. (Das Programm selbst bleibt davon unberührt!) |
|
showidpair | Zeigt das gegenwärtig eingestellte ID Pärchen | |
SETUP_MCP_BITRATE | SETUP_MCP_SPEED:0 SETUP_MCP_SPEED:1 SETUP_MCP_SPEED:2 |
Ändert zur Laufzeit die MCP2515 Bitrate. 0 = 125Kbps / 1 = 250Kbps / 2 = 500Kbps (Nicht während der Diagnose empfohlen!) |
SETUP_MCP_SPEED | SETUP_MCP_SPEED:0 SETUP_MCP_SPEED:1 |
Ändert zur Laufzeit die MCP2515 Frequenz. 0 = 8Mhz / 1 = 16Mhz (Nicht während der Diagnose empfohlen!) |
RAW_OUTPUT | RAW_OUTPUT:true RAW_OUTPUT:false |
Aktiviert oder Deaktiviert den RAW Output |
HIDE_PSA_BYTES | HIDE_PSA_BYTES:true HIDE_PSA_BYTES:false |
Aktiviert oder Deaktiviert die Ausgabe des ersten Bytes eines Single Frames oder Multi frames oder die ersten beiden Bytes eines Startframes (0x10) |
HIDE_KEEP_ALIVE | HIDE_KEEP_ALIVE:true HIDE_KEEP_ALIVE:false |
Aktiviert oder Deaktiviert die Ausgabe des Keep Alive Frames |
HIDE_ACK_FRAME | HIDE_ACK_FRAME:true HIDE_ACK_FRAME:false |
Aktiviert oder Deaktiviert die Ausgabe des Acknowledge Frames |
AUTO_ACK_FRAME | AUTO_ACK_FRAME:true AUTO_ACK_FRAME:false |
Aktiviert oder Deaktiviert das automatische Acknowledge Frame |
SHOWSPACE | SHOWSPACE:true SHOWSPACE:false |
Aktiviert oder Deaktiviert die Anzeige der Can-Bytes mit einem Leerzeichen als Trennzeichen |
SHOWID | SHOWID:true SHOWID:false |
Zeigt die Can ID der empfangenen Daten an. |
LANG | LANG:0 LANG:1 |
Ändert die Sprache. 0 = EN / 1 = DE |
AUTOFILTER | AUTOFILTER:true AUTOFILTER:false |
Filtert alle eingehenden Can-Nachrichten und fügt die ID's dem Filter hinzu. |
IDFILTER_ADD | IDFILTER_ADD:0A5 | Can-ID dem Filter hinzufügen. Z.b. 0A5 |
IDFILTER_REMOVE | IDFILTER_REMOVE:0A5 | Can-ID aus dem Filter entfernen. Z.b. 0A5 |
SHOWIDFILTER | Zeigt alle Can-ID's aus dem Filter an | |
ECO_OFF | Alpha Status! | Deaktiviert den Eco Modus für eine Stunde. Kompatibel mit BSI2010 |
Releases
- v1.0.0 beta Release
Bugs & To-Do's
- Funktion zum entsperren von Steuergeräten
- Multi frame Support für's senden
- Funktion zum überprüfen der Multi frame Integrität
- MCP Einstellungen beim Start anzeigen und zum Fortfahren vorher bestätigen lassen
- LIN Multi frame Unterstützung
- Vielleicht: 11Bit Unterstützung
- Vielleicht: Kompatibilität für nicht auf PSA basierenden Fahrzeuge
- Vielleicht: Hex To Text via Konsole / Text To Hex via Konsole
- Keep Alive Frame Befehl auf Richtigkeit überprüfen
Playground
Hier sammle ich ein paar Befehle, die ihr zum testen nutzen könnt.
Zur Zeit teste ich nur auf meiner Werkbank und habe ein BSI und ein Nac zur Verfügung.
Später teste ich an meinem Auto weiter.
Wer mitmachen will, kann das gerne tun und seine ausprobierten Befehle hier reinschreiben.
Viele Befehle kann man via Diagbox auslesen.
Info: Die meisten Befehle benötigen die Diagnosesitzung (1003), bevor diese akzeptiert werden.
Befehl | Beschreibung | Kompatibel NAC? | Kompatibel BSI2010? |
---|---|---|---|
1003 | Diagnosesitzung starten | ||
1103 | Steuergerät neu starten | ||
2FD6000300 | Touchscreen ausschalten | ||
3101DF07 | Touchscreen Test | ||
22xxxxx | Zone auslesen. Bsp: 22F190 = Fahrgestellnummmer auslesen |
||
190209 | Fehler auslesen | ||
2FD819030664 | Kofferraumbeleuchtung einschalten | ||
2FD877030664 | Interior: Led Warnblinker einschalten | ||
2FDAC6030101 | Kofferraum entriegeln/öffnen | ||
2FD923030A01 | Scheiben entfrosten | ||
2FD822030601 | Scheibenwischer vorne aktivieren |
Entwicklung
Es dürfte klar sein, dass dieses Projekt mein persönliches Hobby ist und ich es ausschließlich in meiner Freizeit vorantreibe.
Ich schätze jede Form von Unterstützung, aber ich bevorzuge natürlich auch die Zusammenarbeit mit Leuten, die ein ernsthaftes Interesse an der Entwicklung und Verbesserung des Projekts oder generell Spaß an sowas haben.
Da das Programm ziemlich umfangreich ist und ich mehrere Tabs nutze, ist es einfacher, dieses erstmal nur kompiliert zur Verfügung zu stellen, da ich sonst bei jeder kleinen Änderung alle Tabs zusammenführen muss.
Folgende Unterstützung hilft zur Verbesserung und Motivation:
- Diagnose und Tests zwecks Funktion und Kompatibilität mehrere Fahrzeuge
- Übersetzung von Texten in anderen Sprachen (Kein simples Copy & Paste per Übersetzer!)
- Tests und Reverse Engineering bzgl. Can-Nachrichten Aufbau und Entschlüsselung
const char germanTexts[NUM_TEXTS][LANG_BUFFER] PROGMEM = {
"Programm: ",
"MCP2515 erfolgreich initiiert!",
"Warte auf Setup Befehl...",
"Fehler 400. Neustart erforderlich.",
"Überprüfe die Verkabelung und/oder das can modul.",
"Keine Daten eingegeben",
"Thread hinzugefügt.",
"Thread entfernt.",
"Einstellungen wurden geladen.",
"Einstellungen wurden nicht geladen.",
"Einstellungen wurden gespeichert.",
"Einstellungen wurden nicht gespeichert.",
"Möchtest du Änderungen speichern? (Y/N): ",
"Keine Konfiguration gefunden. Lade Standardwerte...",
"Ungültiger Befehl",
"Nachricht nicht gesendet",
"Freier RAM (Bytes): ",
"EEPROM mit allen Einstellungen löschen? (Y/N): ",
"BITTE WARTEN...",
"EEPROM nicht gelöscht.",
"Sprache: ",
"MCP Frequenz: ",
"MCP2515 erfolgreich initiiert.",
"Fehler 400. Neustart benötigt.",
"Überprüfe die Verdrahtung und/oder Can-Modul.",
"Fehler: Ungültige Zeichenlänge: ",
"Nachricht konnte nicht gesendet werden.",
"EEPROM gelöscht.",
"Andere Sketch-Version erkannt. Lade Standardwerte...",
"Einstellungen",
"nicht",
"geladen",
"gespeichert",
"Möchtest Du Änderungen speichern? (Y/N): ",
"Möchtest Du Einstellungen laden? (Y/N): ",
"Fehler",
"WARNUNG: Batteriezustand wird vom Steuergerät nicht überwacht!",
"Fortfahren?"
};
Alles anzeigen
const char englishTexts[NUM_TEXTS][LANG_BUFFER] PROGMEM = {
"Sketch: ",
"MCP2515 successfully initiated!",
"Waiting for Setup command...",
"Error 400. Restart required.",
"Check your wiring and/or can module.",
"No data entered",
"Thread added.",
"Thread removed.",
"Settings loaded.",
"Settings not loaded.",
"Settings saved.",
"Settings not saved.",
"Would you like to save changes? (Y/N): ",
"No configuration found. Load default values...",
"Invalid command",
"Message not sent",
"Free RAM (Bytes): ",
"Delete EEPROM with all settings? (Y/N): ",
"PLEASE WAIT...",
"EEPROM not deleted.",
"Language: ",
"MCP Frequency: ",
"MCP2515 successfully initiated.",
"Error 400. Restart required.",
"Check your wiring and/or can module.",
"Error: Invalid character length: ",
"Message could not be sent.",
"EEPROM deleted.",
"Different Sketch version detected. Load default values...",
"Settings",
"not",
"loaded",
"saved",
"Would you like to save changes? (Y/N): ",
"Would you like to load settings? (Y/N): ",
"Error",
"WARNING: Battery level will be ignored by ECU!",
"Continue? (Y/N)"
};
Alles anzeigen
Natürlich hilft es auch, wenn Ihr als nette Geste anderweitig Unterstützung für das Forum und den Server zukommen lasst, um zumindest ein Teil der Kosten decken zu können.
Haftungsausschluss und Nutzungsbedingungen
Es ist zwar klar, dass die Verantwortung beim Nutzer liegt, jedoch möchte ich aus verantwortungsvoller Sicht explizit nochmal auf folgendes hinweisen:
Dieser Arduino-Sketch dient ausschließlich zu diagnostischen und Entwicklungs-zwecken.
Die Nutzung während der Fahrt im Straßenverkehr ist nicht empfohlen und kann im Falle falscher Handhabung und daraus resultierenden Unfällen oder schlimmeres zu schwerwiegenden rechtlichen Konsequenzen führen, seit euch dem bewusst.
Jegliche Nutzung des Sketches erfolgt auf eigene Gefahr.
Ich übernehme keinerlei Haftung für Schäden oder Verluste, die direkt oder indirekt durch die Verwendung dieses Sketches entstehen könnten.
Es liegt in der Verantwortung des Nutzers sicherzustellen, dass die Verwendung des Sketches den geltenden Gesetzen und Vorschriften entspricht.
Durch die Nutzung dieses Sketches erklärt sich der Nutzer mit den oben genannten Nutzungsbedingungen einverstanden.
Sketch
/*
.: Mittns Can Sketch :.
---------------------------
Für Diagnose, Reverse Engineering & Fun
Copyright ©2024 by Mittns <http://www.mittns.de>
Program Version: 1.0.0 beta
Last modified: 27-03-2024
Alle Rechte vorbehalten.
Dieses Programm ist urheberrechtlich geschützt und darf nicht ohne vorherige schriftliche
Genehmigung des Autors vervielfältigt, modifiziert oder anderweitig genutzt werden.
*/
//------------------------------------
// .: Librarys :. //
//------------------------------------
#include <mcp_can.h>
#include <SPI.h>
#include <Thread.h>
#include <ThreadController.h>
#include <EEPROM.h>
//------------------------------------
// .: Sketch Informationen :. //
//------------------------------------
#define Sketch_Name "Mittns Can Sketch"
#define Sketch_Autor "©2024 Mittns"
#define Sketch_Webseite "https://www.mittns.de/"
#define Sketch_Version "1.0.0"
int EEPROM_Version = 100;
#define Sketch_Build "BUILD28032024 beta"
#define WARNUNG "WARNING: Please do not use this program while driving or in traffic!"
//------------------------------------
// .: MCP2515 :. //
//------------------------------------
#define CAN0_INT 2
#define CAN0_CS 10
MCP_CAN CAN0(CAN0_CS);
//------------------------------------
// .: Settings :. //
//------------------------------------
uint8_t Setup_Sprache = 1; // Language: 0 = EN / 1 = DE
uint8_t Setup_MCP_Bitrate = 2; // 0=125kbps, 1= 250kbps, 2 = 500kbps
uint8_t Setup_MCP_Frequenz = 0; // 0=8Mhz, 1=16Mhz
const uint8_t Setup_MCP_Modus = 4; // Do not change!
bool MCP_Configured = true; // true = Set up MCP automatically // false = Use application or console to set up
unsigned int CANID_TX = 0x123; /*0x752;*/ // Default Can ID: -> Send
unsigned int CANID_RX = 0x456; /*0x652;*/ // Default Can ID: <- Receive
//bool PSA_COMPATIBILITY = true; // Don't change if you're using PSA vehicles! // Only for Singleframes at this time
bool HIDE_PSA_BYTES = true;
bool HIDE_KEEP_ALIVE = true;
bool HIDE_ACK_FRAME = true;
bool AUTO_ACK_FRAME = true; // Automatically send acknowledge frame when 0x10 is received.
bool RAW_OUTPUT = false; // Show complete frames. All of the 4 above settings are ignored.
bool SHOWSPACE = false;
bool FILTER = false;
bool SHOWID = true;
bool AUTO_ID_FILTER = false;
const unsigned char ACK_DELAY = 0x05; // Acknowledge frame delay in milliseconds => 0x05 = 5ms
bool Debug = false; // More output comments for testings. Not recommended for normal use.
//------------------------------------
// .: EEPROM :. //
//------------------------------------
uint8_t EEPROM_Flag = 0; //DO NOT CHANGE
char EEPROM_UID[4] = "MCS"; //DO NOT CHANGE
//------------------------------------
// .: Sonstiges :. //
//------------------------------------
const char LEERZEICHEN = ' ';
const char FNULL = '0';
//------------------------------------
// .: Multithreading :. //
//------------------------------------
ThreadController Multithread_Controller = ThreadController();
Thread Send_Keep_Alive_Thread = Thread();
#define MAX_MULTITHREADS 10
Thread* addedThreads[MAX_MULTITHREADS];
void (*threadFunctions[MAX_MULTITHREADS])();
int numThreads = 0;
void Thread_Remove(void (*threadFunction)()) {
for (int i = 0; i < numThreads; ++i) {
if (threadFunctions[i] == threadFunction) {
Multithread_Controller.remove(addedThreads[i]);
delete addedThreads[i];
for (int j = i; j < numThreads - 1; ++j) {
addedThreads[j] = addedThreads[j + 1];
threadFunctions[j] = threadFunctions[j + 1];
}
numThreads--;
if (Debug){Serial.println(F("Thread removed."));}
return;
}
}
Serial.println(F("Error: Thread not found."));
}
void Thread_Add(void (*threadFunction)(), unsigned long interval = 0) {
for (int i = 0; i < numThreads; ++i) {
if (threadFunctions[i] == threadFunction) {
Serial.println(F("Error: Thread already exists."));
return;
}
}
if (numThreads < MAX_MULTITHREADS) {
Thread* newThread = new Thread();
newThread->onRun(threadFunction);
newThread->setInterval(interval);
Multithread_Controller.add(newThread);
addedThreads[numThreads] = newThread;
threadFunctions[numThreads] = threadFunction;
numThreads++;
if (Debug){Serial.println(F("Thread added."));}
} else {
Serial.println(F("Error: Thread limit reached!"));
}
}
const size_t MAX_INPUT_SIZE = 512; // -> 512/2 = Max 256 Hex-Bytes
uint8_t input_data[MAX_INPUT_SIZE / 2];
uint8_t data_buffer[8];
char msgString[256];
void setup() {
Serial.begin(115200);
ShowSketchInfo();
Serial.println(WARNUNG);
if (EEPROM_CheckIfProgramFromMittns()) {
if (EEPROM_CheckSketchVersionIdentical()) {
loadSettingsFromEEPROM();
} else {
Serial.println(getLang(28)); // Other sketch version. Load default values.
}
} else {
Serial.println(getLang(13)); // No config. Load default values.
}
Serial.print(getLang(16));
Serial.println(CheckFreeMemory());
if (!MCP_Configured) {
Serial.println(getLang(2));
while (!MCP_Configured) {
CheckSerial_Input();
}
} else {
if (!Set_CanControllerSettings()) {
while (1); /* Stop program */
}
}
}
void loop() {
Multithread_Controller.run();
CheckSerial_Input();
if (RAW_OUTPUT) {
Can_Receive_RAW();
} else {
Can_Receive();
}
}
void CheckSerial_Input() {
if (Serial.available() > 0) {
String data = Serial.readStringUntil('\n');
if (data.length() > 0 && data[0] != '\n' && data != "") {
if (data == "?") {
ShowSketchInfo();
} else if (equalsIgnoreCase(data, F("wiring"))) {
ShowWiringInfo();
} else if (equalsIgnoreCase(data, F("save"))) {
Einstellungen_speichern(true);
} else if (equalsIgnoreCase(data, F("help"))) {
ShowHelpText();
} else if (equalsIgnoreCase(data, F("showidpair"))) {
Serial.print(CANID_TX, HEX);
Serial.print(F(":"));
Serial.println(CANID_RX, HEX);
} else if (data == F("clearEEPROM")) {
resetSettings(true);
} else if (data.startsWith(F(">"))) {
data.remove(0, 1);
int ValueIndex = data.indexOf(':');
if (ValueIndex != -1) {
String byte1_str = data.substring(0, ValueIndex);
String byte2_str = data.substring(ValueIndex + 1);
CANID_TX = strtoul(byte1_str.c_str(), NULL, 16);
CANID_RX = strtoul(byte2_str.c_str(), NULL, 16);
Snd_OK();
} else {
Snd_NOT_OK();
}
} else if (data.startsWith("SETUP_MCP_SPEED:")) {
data.remove(0, 16);
Setup_MCP_Frequenz = data.toInt();
if (!Set_CanControllerSettings()) {
while (1); // Stop program
}
} else if (data.startsWith("SETUP_MCP_BITRATE:")) {
data.remove(0, 18);
Setup_MCP_Bitrate = data.toInt();
if (!Set_CanControllerSettings()) {
while (1); // Stop program
}
} else if (data.startsWith(F("RAW_OUTPUT:"))) {
data.remove(0, 11);
setBooleanFromString(RAW_OUTPUT, data);
} else if (data.startsWith(F("HIDE_PSA_BYTES:"))) {
data.remove(0, 15);
setBooleanFromString(HIDE_PSA_BYTES, data);
} else if (data.startsWith(F("HIDE_KEEP_ALIVE:"))) {
data.remove(0, 16);
setBooleanFromString(HIDE_KEEP_ALIVE, data);
} else if (data.startsWith(F("HIDE_ACK_FRAME:"))) {
data.remove(0, 15);
setBooleanFromString(HIDE_ACK_FRAME, data);
} else if (data.startsWith(F("AUTO_ACK_FRAME:"))) {
data.remove(0, 15);
setBooleanFromString(AUTO_ACK_FRAME, data);
} else if (data.startsWith(F("SHOWSPACE:"))) {
data.remove(0, 10);
setBooleanFromString(SHOWSPACE, data);
} else if (data.startsWith(F("SHOWID:"))) {
data.remove(0, 7);
setBooleanFromString(SHOWID, data);
} else if (data.startsWith(F("FILTER:"))) {
data.remove(0, 7);
setBooleanFromString(FILTER, data);
} else if (data.startsWith(F("IDFILTER_ADD:"))) {
data.remove(0, 13);
unsigned long id = strtoul(data.c_str(), NULL, 16);
Serial.print(F(">>"));
Serial.println(id, HEX);
addFilterId(id);
} else if (data.startsWith(F("IDFILTER_REMOVE:"))) {
data.remove(0, 16);
unsigned long id = strtoul(data.c_str(), NULL, 16);
Serial.print(F(">>"));
Serial.println(id, HEX);
removeFilterId(id);
} else if (data == F("SHOWIDFILTER")) {
printFilterIds();
} else if (data == F("START_DIAG")) {
StartDiag();
Thread_Add(Send_Keep_Alive, 3000); //Send keep alive every 3 seconds
} else if (data == F("STOP_DIAG")) {
Thread_Remove(Send_Keep_Alive);
EndCommunication();
} else if (data.startsWith(F("AUTOFILTER:"))) {
data.remove(0, 11);
setBooleanFromString(AUTO_ID_FILTER, data);
} else if (data.startsWith(F("LANG:"))) {
data.remove(0, 5);
if (isInteger(data)){
if (data == "0" || data == "1") {
Setup_Sprache = data.toInt();
Snd_OK();
} else {
Serial.println(getLang(35)); /* Error */
}
} else {
Serial.println(getLang(35)); /* Error */
}
} else if (data == F("ECO_OFF")) {
Serial.print(getLang(36)); /*Warning*/
Serial.print(getLang(37)); /*Continue?*/
while (!Serial.available());
char response = Serial.read();
if (tolower(response) == 'y') {
Disable_ECO();
Serial.println();
} else {
Serial.println();
}
} else {
CheckCanMessage_Input(data); // Declare as can message if options above not valid
}
}
}
}
unsigned long rxId;
unsigned char len2 = 0;
unsigned char rxBuf[8];
int StartOutputByte = 0; //Ausgabe ggf. reduzieren
void SendSingleFrame(uint32_t can_id, uint8_t DLC, uint8_t* data) {
data_buffer[0] = DLC; // Das erste Byte ist die Länge der Daten
for (uint8_t i = 0; i < DLC; ++i) {
data_buffer[i + 1] = data[i]; // Kopieren der Daten in das Datenpaket
}
byte sndStat = CAN0.sendMsgBuf(can_id, 0, DLC + 1, data_buffer);
if(sndStat != CAN_OK){
Serial.println();
Serial.println(getLang(26));
}
}
/* NOT READY YET */
/* NOT READY YET */
const unsigned long WaitForMultiFrameError = 30; // Waiting time until (ms)
bool MultiframeEmpfangen = false;
uint8_t MultiFrameCounter = 0;
uint8_t MultiFrameCounterExpected = 0;
bool MultiframeAcknowledgeFrameEmpfangen = false;
void SendMultiFrame(uint32_t can_id, uint8_t len, uint8_t* data) {
Serial.println(F("Send multi frame..."));
uint8_t full_data_len = len;
uint8_t data_buffer[8];
// Prepare first frame
data_buffer[0] = 0x10; // First data 0x10
data_buffer[1] = full_data_len; // Second data (FDL)
for (uint8_t i = 0; i < 6 && i < len; ++i) { // Last 6 Bytes
data_buffer[i + 2] = data[i];
}
// Send first frame
byte sndStat = CAN0.sendMsgBuf(can_id, 0, 8, data_buffer);
if(sndStat != CAN_OK){
Serial.println();
Serial.println(getLang(15));
return;
}
unsigned long start_time = millis(); // Startzeit merken
unsigned long wait_time = 2000; // Wartezeit auf das Acknowledge Frame (2 Sekunden)
uint8_t interval = 0; // Initialisiere das Intervall
MultiframeAcknowledgeFrameEmpfangen = false;
while (millis() - start_time < wait_time) { // wait a while for response
if (CAN0.checkReceive() == CAN_MSGAVAIL) {
uint8_t recv_buffer[8];
uint8_t recv_len = 0;
uint32_t recv_id = 0;
CAN0.readMsgBuf(&recv_id, &recv_len, recv_buffer); // Nachricht lesen
if (recv_id == can_id && recv_len == 3 && recv_buffer[0] == 0x30 && recv_buffer[1] == 0x00) { // Überprüfen, ob es sich um das erwartete Acknowledge Frame handelt
Serial.println(F("ACK frame received"));
MultiframeAcknowledgeFrameEmpfangen = true;
interval = recv_buffer[2]; // Extrahiere das Intervall aus dem Acknowledge Frame
break;
}
}
}
if (!MultiframeAcknowledgeFrameEmpfangen) {
Serial.println(F("No ACK frame received. Abort..."));
return;
}
Serial.println(F("Send next multi frame..."));
uint8_t counter = 0x21; // Start counter for multi frame
for (uint8_t i = 6; i < len; i += 7) { // Start from 7. Byte and increment 7 Bytes
// Vorbereitung des nächsten Frames
uint8_t frame_len = (len - i) < 7 ? (len - i) : 7; // Length next frame
data_buffer[0] = counter++; // Increment counter and set 1. Byte
for (uint8_t j = 0; j < frame_len; ++j) { // Copy bytes for the next frame
data_buffer[j + 1] = data[i + j];
}
CAN0.sendMsgBuf(can_id, 0, 8, data_buffer);
delay(interval); // Change with millis or use thread in upcoming versions or what so ever
if (counter > 0x2F) { counter = 0x20; } //Reset counter if reached max
}
}
bool LastFrameWasMultiframe = false;
void Can_Receive() {
if (!digitalRead(CAN0_INT)) { // If CAN0_INT pin is low, read receive buffer
if (CAN0.checkReceive()) {
CAN0.readMsgBuf(&rxId, &len2, rxBuf); // Read data: len = data length, buf = data byte(s)
if (AUTO_ID_FILTER) {
addFilterId(rxId);
}
if (FILTER) {
if (rxId != CANID_TX && rxId != CANID_RX) {
return;
}
}
if (Check_ID_Filter(rxId)) {
return;
}
if (HIDE_ACK_FRAME && len2 == 3 && rxBuf[0] == 0x30) {
return;
}
if (HIDE_KEEP_ALIVE) {
if ((len2 == 3 && rxBuf[0] == 0x02 && rxBuf[1] == 0x7E && rxBuf[2] == 0x00) || (len2 == 3 && rxBuf[0] == 0x02 && rxBuf[1] == 0x3E && rxBuf[2] == 0x00)) {
return;
}
}
if (HIDE_PSA_BYTES) {
if (rxBuf[0] <= 0x07) {
StartOutputByte = 1; //Skip 1. Byte
} else if (rxBuf[0] == 0x10 || rxBuf[0] == 0x14) {
StartOutputByte = 2; //1. Byte überspringen
} else if ((rxBuf[0] >= 0x20 && rxBuf[0] <= 0x2F) || rxBuf[0] >= 0x2F) {
StartOutputByte = 1; //Ab byte 2
} else {
StartOutputByte = 1; //Ab byte 2
}
} else {
StartOutputByte = 0; //Everything
}
if (LastFrameWasMultiframe && rxBuf[0] <= 0x07) {
LastFrameWasMultiframe = false;
}
if (SHOWID) {
if (rxBuf[0] <= 0x14) {
Serial.println();
if (rxId < 0x100) {
Serial.print(FNULL);
}
Serial.print(rxId, HEX);
Serial.print(F(":"));
}
}
for (byte i = StartOutputByte; i < len2; i++) {
if (rxBuf[i] < 0x10) {
Serial.print(FNULL);
}
Serial.print(rxBuf[i], HEX);
if (SHOWSPACE) {Serial.print(LEERZEICHEN);}
}
if (rxBuf[0] <= 0x07) {
LastFrameWasMultiframe = false;
} else {
LastFrameWasMultiframe = true;
}
if (AUTO_ACK_FRAME && rxBuf[0] == 0x10) {
unsigned char ACK[3] = { 0x30, 0x00, 0x05 }; //0x05 = 5ms
CAN0.sendMsgBuf(CANID_TX, 0, 3, ACK);
}
}
}
}
//Raw Output of all messages without options!!
void Can_Receive_RAW() {
if (CAN0.checkReceive() == CAN_MSGAVAIL) {
CAN0.readMsgBuf(&rxId, &len2, rxBuf);
if (SHOWID) {
if (rxId < 0x100) {Serial.print(FNULL);}
Serial.print(rxId, HEX);
Serial.print(F(":"));
}
for (byte i = StartOutputByte; i < len2; i++) {
if (rxBuf[i] < 0x10) {Serial.print(FNULL);}
Serial.print(rxBuf[i], HEX);
Serial.print(LEERZEICHEN);
}
Serial.println();
}
}
void CheckCanMessage_Input(String input) {
if (input.length() > 0 && input[0] != '\n') {
for (size_t i = 0; i < input.length(); i += 2) {
if (!isHexadecimalDigit(input.charAt(i)) || !isHexadecimalDigit(input.charAt(i + 1))) {
Serial.println(getLang(14));
return;
}
}
size_t input_len = input.length();
uint8_t data_len = 0;
uint8_t input_data[data_len];
for (size_t i = 0; i < input_len; i += 2) {
char* hex_str = new char[3];
hex_str[0] = input.charAt(i);
hex_str[1] = input.charAt(i + 1);
hex_str[2] = '\0'; // Nullterminierung
input_data[i / 2] = strtol(hex_str, NULL, 16); // Hex to byte
delete[] hex_str;
data_len++;
}
Serial.println();
Serial.print(F(">"));
Serial.print(CANID_TX, HEX);
Serial.print(F(":"));
Serial.print(input);
if (data_len > 0 && data_len <= 7) {
SendSingleFrame(CANID_TX, data_len, input_data);
} else if (data_len > 7) {
//SendMultiFrame(CANID_TX, data_len, input_data); //NOT READY YET
Serial.print(F("Multiframes not supported yet!"));
return;
} else {
Serial.print(getLang(25));
Serial.println(data_len);
}
} else {
Serial.println(getLang(5));
}
}
unsigned char KeepAliveDIAG[2] = {0x7E, 0x00}; //Keep Alive Frame UDS
void Send_Keep_Alive() {
SendSingleFrame(CANID_TX, 3, KeepAliveDIAG);
}
unsigned char Disable_ECO_Frame[5] = {0x31, 0x01, 0xDF, 0x0A, 0x3C}; //Keep Alive Frame UDS
void Disable_ECO() {
SendSingleFrame(CANID_TX, 5, Disable_ECO_Frame);
}
unsigned char StartDiagFrame[2] = {0x10, 0x03};
void StartDiag() {
SendSingleFrame(CANID_TX, 2, StartDiagFrame);
}
unsigned char StopDiagFrame[2] = {0x10, 0x01};
void EndCommunication() {
SendSingleFrame(CANID_TX, 2, Disable_ECO_Frame);
}
//------------------------------------
// .: EEPROM :. //
//------------------------------------
template<typename T>
void writeToEEPROM(int Adresse, const T& Wert) {
for (unsigned int i = 0; i < sizeof(Wert); i++) {
EEPROM.update(Adresse + i, *((byte*)&Wert + i));
}
}
template<typename T>
void readFromEEPROM(int Adresse, T& Wert) {
for (unsigned int i = 0; i < sizeof(Wert); i++) {
*((byte*)&Wert + i) = EEPROM.read(Adresse + i);
}
}
void Einstellungen_laden(bool confirmNeeded) {
if (confirmNeeded) {
Serial.println(getLang(34)); // Save settings?
while (!Serial.available());
char response = Serial.read();
if (tolower(response) == 'y') {
loadSettingsFromEEPROM();
Serial.print(getLang(29));
Serial.print(LEERZEICHEN);
Serial.println(getLang(31)); // Settings loaded
} else {
Serial.print(getLang(29));
Serial.print(LEERZEICHEN);
Serial.print(getLang(30));
Serial.print(LEERZEICHEN);
Serial.println(getLang(31)); // Settings not loaded
}
} else {
loadSettingsFromEEPROM();
Serial.print(getLang(29));
Serial.print(LEERZEICHEN);
Serial.print(getLang(30));
Serial.print(LEERZEICHEN);
Serial.println(getLang(31)); // Settings not loaded
}
}
void Einstellungen_speichern(bool confirmNeeded) {
if (confirmNeeded) {
Serial.print(getLang(33)); // Save settings?
while (!Serial.available());
char response = Serial.read();
Serial.println(response);
if (tolower(response) == 'y') {
saveSettingsToEEPROM();
Serial.print(getLang(29));
Serial.print(LEERZEICHEN);
Serial.println(getLang(32)); // Settings saved
} else {
Serial.print(getLang(29));
Serial.print(LEERZEICHEN);
Serial.print(getLang(30));
Serial.print(LEERZEICHEN);
Serial.println(getLang(32)); // Settings not saved
}
} else {
saveSettingsToEEPROM();
Serial.print(getLang(29));
Serial.print(LEERZEICHEN);
Serial.print(getLang(30));
Serial.print(LEERZEICHEN);
Serial.println(getLang(32)); // Settings not saved
}
}
void saveSettingsToEEPROM() {
writeToEEPROM(0, 1);
writeToEEPROM(1, EEPROM_UID);
writeToEEPROM(10, EEPROM_Version);
writeToEEPROM(40, Setup_Sprache);
writeToEEPROM(41, Setup_MCP_Bitrate);
writeToEEPROM(42, Setup_MCP_Frequenz);
writeToEEPROM(43, CANID_TX);
writeToEEPROM(45, CANID_RX);
writeToEEPROM(47, HIDE_PSA_BYTES);
writeToEEPROM(48, HIDE_KEEP_ALIVE);
writeToEEPROM(49, HIDE_ACK_FRAME);
writeToEEPROM(50, AUTO_ACK_FRAME);
writeToEEPROM(51, RAW_OUTPUT);
writeToEEPROM(52, SHOWSPACE);
writeToEEPROM(53, FILTER);
writeToEEPROM(54, SHOWID);
writeToEEPROM(55, AUTO_ID_FILTER);
}
void loadSettingsFromEEPROM() {
//readFromEEPROM(0, EEPROM_Flag); //Obsolete at the moment
readFromEEPROM(40, Setup_Sprache);
readFromEEPROM(41, Setup_MCP_Bitrate);
readFromEEPROM(42, Setup_MCP_Frequenz);
readFromEEPROM(43, CANID_TX);
readFromEEPROM(45, CANID_RX);
readFromEEPROM(47, HIDE_PSA_BYTES);
readFromEEPROM(48, HIDE_KEEP_ALIVE);
readFromEEPROM(49, HIDE_ACK_FRAME);
readFromEEPROM(50, AUTO_ACK_FRAME);
readFromEEPROM(51, RAW_OUTPUT);
readFromEEPROM(52, SHOWSPACE);
readFromEEPROM(53, FILTER);
readFromEEPROM(54, SHOWID);
readFromEEPROM(55, AUTO_ID_FILTER);
for (int i = 0; i < 40; i++) {
Serial.print(F("-"));
}
Serial.println();
if (Debug) {
Serial.print(F("EEPROM_Flag: "));
Serial.println(String(EEPROM_Flag));
}
Serial.print(getLang(20));
Serial.println(checkConfiguredLanguage(Setup_Sprache));
Serial.print(F("MCP Bitrate: "));
switch (Setup_MCP_Bitrate) {
case 0:
Serial.println(F("125Kbps"));
break;
case 1:
Serial.println(F("250Kbps"));
break;
case 2:
Serial.println(F("500Kbps"));
break;
default:
Serial.print(Setup_MCP_Bitrate);
Serial.println(F("|??"));
}
Serial.print(getLang(21));
switch (Setup_MCP_Frequenz) {
case 0:
Serial.println(F("8Mhz"));
break;
case 1:
Serial.println(F("16Mhz"));
break;
default:
Serial.println(F("??"));
}
Serial.print(F("CANID_TX: "));
Serial.println(CANID_TX, HEX);
Serial.print(F("CANID_RX: "));
Serial.println(CANID_RX, HEX);
Serial.print(F("HIDE_PSA_BYTES: "));
Serial.println(String(HIDE_PSA_BYTES));
Serial.print(F("HIDE_KEEP_ALIVE: "));
Serial.println(String(HIDE_KEEP_ALIVE));
Serial.print(F("HIDE_ACK_FRAME: "));
Serial.println(String(HIDE_ACK_FRAME));
Serial.print(F("AUTO_ACK_FRAME: "));
Serial.println(String(AUTO_ACK_FRAME));
Serial.print(F("RAW_OUTPUT: "));
Serial.println(String(RAW_OUTPUT));
Serial.print(F("SHOWSPACE: "));
Serial.println(String(SHOWSPACE));
Serial.print(F("FILTER: "));
Serial.println(String(FILTER));
Serial.print(F("SHOWID: "));
Serial.println(String(SHOWID));
Serial.print(F("AUTO_ID_FILTER: "));
Serial.println(String(AUTO_ID_FILTER));
Serial.println(getLang(8));
for (int i = 0; i < 40; i++) {
Serial.print(F("-"));
}
Serial.println();
}
void resetSettings(bool confirmNeeded) {
if (confirmNeeded) {
Serial.print(getLang(17));
while (!Serial.available());
char response = Serial.read();
Serial.println(response);
if (tolower(response) == 'y') {
Serial.println(getLang(18));
deleteEEPROM();
} else {
Serial.println(getLang(19));
}
} else {
deleteEEPROM();
}
}
void deleteEEPROM() {
unsigned long EEPROM_Groesse = EEPROM.length();
for (unsigned long i = 0; i < EEPROM_Groesse; i++) {
EEPROM.write(i, 0xFF);
}
Serial.println(getLang(27));
}
int CheckFreeMemory() {
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
bool EEPROM_CheckIfProgramFromMittns() {
char Tmp_EEPROM_UID[4];
readFromEEPROM(1, Tmp_EEPROM_UID);
return strcmp(EEPROM_UID, Tmp_EEPROM_UID) == 0; // Operator '==' nicht nutzen, muss mit strcmp verglichen werden => 0
}
bool EEPROM_CheckSketchVersionIdentical() {
int Tmp_EEPROM_Version;
readFromEEPROM(10, Tmp_EEPROM_Version);
return EEPROM_Version == Tmp_EEPROM_Version;
}
bool equalsIgnoreCase(const String& Variable, const String& Wert) {
if (Variable.length() != Wert.length()) {
return false;
}
for (size_t i = 0; i < Variable.length(); i++) {
if (tolower(Variable[i]) != tolower(Wert[i])) {
return false;
}
}
return true;
}
bool isInteger(String input) {
int Laenge = input.length();
for (int i = 0; i < Laenge; i++) {
if (!isdigit(input.charAt(i))) {
return false;
}
}
return true;
}
bool setBooleanFromString(bool& variable, const String& sValue) {
if (equalsIgnoreCase(sValue, F("true")) || sValue == F("1")) {
variable = true;
Snd_OK();
return true;
} else if (equalsIgnoreCase(sValue, F("false")) || sValue == F("1")) {
variable = false;
Snd_OK();
return true;
} else {
Serial.println(F("No bool value!"));
return false;
}
}
/*später mal sprintf nutzen*/
void ShowSketchInfo() {
Serial.println(Sketch_Name);
Serial.println(Sketch_Autor);
Serial.print(Sketch_Version);
Serial.print(LEERZEICHEN);
Serial.println(Sketch_Build);
Serial.println(Sketch_Webseite);
}
void Snd_OK() {
Serial.println(F("OK"));
}
void Snd_NOT_OK() {
Serial.println(F("NOT OK"));
}
const char HelpText[] PROGMEM = "NOT READY YET!!!";
void ShowHelpText() {
Serial.println(reinterpret_cast<const __FlashStringHelper *>(HelpText));
}
void ShowWiringInfo() {
Serial.println(F("-----------------------------------------------"));
Serial.println(F("Wiring Arduino to MCP2515:"));
Serial.println(F("Arduino\t\t => \tMCP2515"));
Serial.println(F("5V\t\t => \tVCC"));
Serial.println(F("GND\t\t => \tGND"));
Serial.println(F("D10(SS)\t\t => \tCS"));
Serial.println(F("D11(MOSI)\t => \tSI"));
Serial.println(F("D12(MISO)\t => \tSO"));
Serial.println(F("D13(SCK)\t => \tSCK"));
Serial.println(F("D2\t\t => \tINT"));
}
const int MAX_FILTER_IDS = 200;
unsigned long filterIds[MAX_FILTER_IDS];
int numFilterIdsPos = 0;
bool Check_ID_Filter(unsigned long rxId) {
for (int i = 0; i < numFilterIdsPos; ++i) {
if (rxId == filterIds[i]) {
return true;
}
}
return false;
}
void addFilterId(unsigned long CanId) {
if (numFilterIdsPos >= MAX_FILTER_IDS) {
Serial.println(F("Filter full"));
return;
}
for (int i = 0; i < numFilterIdsPos; ++i) {
if (CanId == filterIds[i]) {
if (!AUTO_ID_FILTER) {
Serial.println(F("Filter already exist"));
}
return;
}
}
filterIds[numFilterIdsPos++] = CanId;
Snd_OK();
}
void removeFilterId(unsigned long CanId) {
for (int i = 0; i < numFilterIdsPos; ++i) {
if (filterIds[i] == CanId) {
for (int j = i; j < numFilterIdsPos - 1; ++j) {
filterIds[j] = filterIds[j + 1];
}
numFilterIdsPos--;
Snd_OK();
return;
}
}
}
void printFilterIds() {
Serial.println(F("Filter-IDs:"));
for (int i = 0; i < numFilterIdsPos; ++i) {
if (filterIds[i] < 0x100) {
Serial.print(FNULL);
}
Serial.println(filterIds[i], HEX);
}
}
bool Set_CanControllerSettings() {
if (CAN0.begin(MCP_ANY, MCP_SETUP_SET_BITRATE(), MCP_SETUP_SET_SPEED()) == CAN_OK) {
CAN0.setMode(MCP_NORMAL);
pinMode(CAN0_INT, INPUT_PULLUP);
MCP_Configured = true;
Serial.println(getLang(22));
Serial.print(F("MCP Bitrate: "));
switch (Setup_MCP_Bitrate) {
case 0:
Serial.print(Setup_MCP_Bitrate);
Serial.println(F("|125Kbps"));
break;
case 1:
Serial.println(F("250Kbps"));
break;
case 2:
Serial.println(F("500Kbps"));
break;
default:
Serial.println(F("??"));
return false;
}
Serial.print(getLang(21));
switch (Setup_MCP_Frequenz) {
case 0:
Serial.println(F("8Mhz"));
break;
case 1:
Serial.println(F("16Mhz"));
break;
default:
Serial.println(F("??"));
return false;
}
return true;
} else {
Serial.println(getLang(23));
Serial.println(getLang(24));
return false;
}
}
uint8_t MCP_SETUP_SET_BITRATE() {
switch (Setup_MCP_Bitrate) {
case 0:
return CAN_125KBPS;
case 1:
return CAN_250KBPS;
case 2:
return CAN_500KBPS;
default:
Serial.print(Setup_MCP_Bitrate);
Serial.println(F("Fehler: MCP BITRATE"));
return 0;
}
}
uint8_t MCP_SETUP_SET_SPEED() {
switch (Setup_MCP_Frequenz) {
case 0:
return MCP_8MHZ;
case 1:
return MCP_16MHZ;
default:
Serial.print(Setup_MCP_Frequenz);
Serial.println(F("|Error: MCP SPEED"));
return 0;
}
}
/* Prepared for future versions */
uint8_t MCP_SETUP_SET_MODE() {
switch (Setup_MCP_Modus) {
case 0:
return MCP_STDEXT;
case 1:
return MCP_STD;
case 2:
return MCP_EXT;
case 3:
return MCP_ANY;
case 4:
return MCP_NORMAL;
default:
Serial.println(F("Error: MCP MODUS"));
return 0;
}
}
//------------------------------------
// .: Language :. //
//------------------------------------
#define NUM_TEXTS 38 // Text counts => Always adapt
#define LANG_BUFFER 65 // Minimum buffer size must correspond to the longest text
const char germanTexts[NUM_TEXTS][LANG_BUFFER] PROGMEM = {
"Programm: ", /*getLang ID 0*/
"MCP2515 erfolgreich initiiert!", /*getLang ID 1*/
"Warte auf Setup Befehl...",
"Fehler 400. Neustart erforderlich.",
"Überprüfe die Verkabelung und/oder das can modul.",
"Keine Daten eingegeben",
"Thread hinzugefügt.",
"Thread entfernt.",
"Einstellungen wurden geladen.",
"Einstellungen wurden nicht geladen.",
"Einstellungen wurden gespeichert.",
"Einstellungen wurden nicht gespeichert.",
"Möchtest du Änderungen speichern? (Y/N): ",
"Keine Konfiguration gefunden. Lade Standardwerte...",
"Ungültiger Befehl",
"Nachricht nicht gesendet",
"Freier RAM (Bytes): ",
"EEPROM mit allen Einstellungen löschen? (Y/N): ",
"BITTE WARTEN...",
"EEPROM nicht gelöscht.",
"Sprache: ",
"MCP Frequenz: ",
"MCP2515 erfolgreich initiiert.",
"Fehler 400. Neustart benötigt.",
"Überprüfe die Verdrahtung und/oder Can-Modul.",
"Fehler: Ungültige Zeichenlänge: ",
"Nachricht konnte nicht gesendet werden.",
"EEPROM gelöscht.",
"Andere Sketch-Version erkannt. Lade Standardwerte...",
"Einstellungen",
"nicht",
"geladen",
"gespeichert",
"Möchtest Du Änderungen speichern? (Y/N): ",
"Möchtest Du Einstellungen laden? (Y/N): ",
"Fehler",
"WARNUNG: Batteriezustand wird vom Steuergerät nicht überwacht!",
"Fortfahren?"
};
const char englishTexts[NUM_TEXTS][LANG_BUFFER] PROGMEM = {
"Sketch: ",
"MCP2515 successfully initiated!",
"Waiting for Setup command...",
"Error 400. Restart required.",
"Check your wiring and/or can module.",
"No data entered",
"Thread added.",
"Thread removed.",
"Settings loaded.",
"Settings not loaded.",
"Settings saved.",
"Settings not saved.",
"Would you like to save changes? (Y/N): ",
"No configuration found. Load default values...",
"Invalid command",
"Message not sent",
"Free RAM (Bytes): ",
"Delete EEPROM with all settings? (Y/N): ",
"PLEASE WAIT...",
"EEPROM not deleted.",
"Language: ",
"MCP Frequency: ",
"MCP2515 successfully initiated.",
"Error 400. Restart required.",
"Check your wiring and/or can module.",
"Error: Invalid character length: ",
"Message could not be sent.",
"EEPROM deleted.",
"Different Sketch version detected. Load default values...",
"Settings",
"not",
"loaded",
"saved",
"Would you like to save changes? (Y/N): ",
"Would you like to load settings? (Y/N): ",
"Error",
"WARNING: Battery level will be ignored by ECU!",
"Continue? (Y/N)"
};
const char* getLang(int Lang_ID) {
static char buffer[LANG_BUFFER];
strcpy_P(buffer, (Setup_Sprache == 0) ? germanTexts[Lang_ID] : englishTexts[Lang_ID]);
return buffer;
}
String checkConfiguredLanguage(uint8_t Lang_ID) {
switch (Lang_ID) {
case 0:
return F("DE");
break;
case 1:
return F("EN");
break;
default:
return F("?");
}
}
Alles anzeigen