CAN-Bus Daten aus dem E-Bike auslesen. #4
Endlich wieder Zeit: Überwindung langer Wartezeiten und Erfolge beim SMD-Löten
Nach einer langen Wartezeit auf wichtige Bauteile habe ich endlich wieder die Zeit gefunden, mich meinen Projekten zu widmen. Die Vorfreude hat sich aufgebaut, und nun, da die Teile angekommen sind, kann es endlich losgehen. Mein jüngster Erfolg beim SMD-Löten war ein bedeutender Meilenstein und ebnet den Weg für komplexere und spannendere Arbeiten.
Eine Voraussetzung für dieses Projekt ist die Installation der ESP32 CAN-Bibliothek. Die ACAN-ESP32 scheint mir geeignet und für die Bedürfnisse ausreichend zu sein.
Projektdateistruktur: Ein Überblick
Beim Arbeiten an meinem neuesten Projekt habe ich eine klare und organisierte Dateistruktur erstellt, um den Überblick zu behalten und die Entwicklung zu erleichtern. Hier ist ein kurzer Überblick über die wichtigsten Dateien und deren Funktionalitäten:
- analyzer.ino: Dies ist die Hauptdatei des Projekts. Hier werden die grundlegenden Initialisierungen vorgenommen und die Hauptlogik des Programms ausgeführt.
- src/serial/serial.h und src/serial/serial.cpp: Diese Dateien enthalten die Implementierung der seriellen Kommunikation. Sie definieren die Klasse
Comm, die für die Initialisierung und Handhabung der seriellen Schnittstelle verantwortlich ist. - src/can/can.h und src/can/can.cpp: Diese Dateien sind für die CAN-Kommunikation zuständig. Sie definieren die Klasse
CANopen, die Methoden zur Initialisierung, Empfang und Sendung von CAN-Nachrichten beinhaltet. Hier wird auch die Heartbeat-Nachricht implementiert, um die Verbindung mit anderen Geräten im Netzwerk zu überwachen. - src/can/NMT/nmt.h und src/can/NMT/nmt.cpp: Diese Dateien enthalten die Implementierung der NMT (Network Management) Funktionen. Die Klasse
NMTkümmert sich um den Netzwerk-Management-Status des Geräts, wie z.B. den Wechsel zwischen verschiedenen Zuständen (Initialisieren, Vor-Operation, Operation, etc.). - libraries/ACAN-ESP32: Dies ist die externe Bibliothek, die für die CAN-Kommunikation auf dem ESP32 benötigt wird. Die Installation dieser Bibliothek ist eine Grundvoraussetzung für das Projekt.
Mit dieser strukturierten Herangehensweise hoffe ich, eine solide Basis für mein Projekt zu schaffen und die Entwicklungsprozesse so effizient wie möglich zu gestalten. Bleibt dran für weitere Updates und Fortschritte!
Die Funktion can.init() ist dafür verantwortlich, den CAN-Modus sowie diverse andere Einstellungen zu definieren. Hier ein kurzer Überblick über die wichtigsten Aspekte der Initialisierung:
void setup() {
can.init();
....
}
- CAN-Modus festlegen: Zu Beginn wird der CAN-Modus definiert. Für mein Projekt habe ich den High-Speed-Modus gewählt, um eine schnelle und zuverlässige Datenübertragung zu gewährleisten.
- Pin-Konfiguration: Die RX- und TX-Pins des ESP32 werden entsprechend konfiguriert, um die CAN-Kommunikation zu ermöglichen. In meinem Fall sind dies GPIO 17 für RX und GPIO 18 für TX.
- Baudrate und andere Einstellungen: Die Baudrate wird auf 250 kbps gesetzt, was für die meisten Anwendungen ausreichend und zuverlässig ist. Weitere Einstellungen wie Filter und Puffergrößen werden ebenfalls konfiguriert, um den speziellen Anforderungen des Projekts gerecht zu werden.
- Fehlerbehandlung: Sollte bei der Initialisierung ein Fehler auftreten, wird dieser erkannt und über die serielle Schnittstelle ausgegeben. Dies erleichtert die Fehlersuche und sorgt dafür, dass Probleme schnell behoben werden können.
void CANopen::init() {
ACAN_ESP32_Settings settings(250E3);
settings.mRequestedCANMode = ACAN_ESP32_Settings::NormalMode;
settings.mRxPin = GPIO_NUM_17;
settings.mTxPin = GPIO_NUM_18;
const uint32_t errorCode = ACAN_ESP32::can.begin(settings);
if (errorCode != 0) {
Serial.print("Configuration error 0x");
Serial.println(errorCode, HEX);
}
initTPDO();
}
Erklärung der Methode onReceive()
Die Methode onReceive() in meinem CANopen-Projekt ist dafür zuständig, empfangene CAN-Nachrichten zu verarbeiten. Hier ist eine detaillierte Erklärung der Funktion:
void CANopen::onReceive() {
CANMessage message;
if (ACAN_ESP32::can.receive(message)) {
// CAN Object Identifier
uint16_t cobId = message.id;
if (!message.rtr && message.ext) {
handleMessage(message);
} else if (!message.rtr && !message.ext) {
handleDataStandardFrame(message);
}
}
}
Deklaration eines CAN-Nachrichtenobjekts:
CANMessage message;
Zuerst wird ein Objekt message vom Typ CANMessage deklariert. Dieses Objekt wird verwendet, um die empfangene Nachricht zu speichern.
Empfangen einer Nachricht:
if (ACAN_ESP32::can.receive(message)) {
Mit dieser Bedingung wird überprüft, ob eine neue CAN-Nachricht empfangen wurde. Wenn eine Nachricht empfangen wurde, wird sie im message-Objekt gespeichert.
Auslesen der CAN-ID:
uint16_t cobId = message.id;
Die CAN-ID der empfangenen Nachricht wird aus dem message-Objekt extrahiert und in der Variablen cobId gespeichert.
Unterscheidung der Nachrichtentypen:
if (!message.rtr && message.ext) {
handleMessage(message);
} else if (!message.rtr && !message.ext) {
handleDataStandardFrame(message);
}
- Extended Frames: Wenn die Nachricht keine Remote Transmission Request (RTR) ist und ein Extended Frame ist (
message.extist wahr), wird die MethodehandleMessage(message)aufgerufen. - Standard Frames: Wenn die Nachricht keine RTR ist und kein Extended Frame ist (
message.extist falsch), wird die MethodehandleDataStandardFrame(message)aufgerufen.
Funktionen zur Verarbeitung der Nachrichten
- handleMessage(message):Diese Funktion wird aufgerufen, um Extended Frames zu verarbeiten. In der aktuellen Implementierung ist diese Funktion noch nicht definiert.
- handleDataStandardFrame(message):
void CANopen::handleDataStandardFrame(const CANMessage &inMessage) {
Serial.print(inMessage.id, HEX);
Serial.print("\t");
Serial.print(inMessage.len, HEX);
Serial.print("\t");
for (int i = 0; i < inMessage.len; i++) {
Serial.print(inMessage.data[i], HEX);
Serial.print("\t");
}
Serial.println();
}
Diese Funktion druckt die Details der empfangenen Standard-Nachricht auf den seriellen Monitor. Es werden die CAN-ID, die Länge der Nachricht und die Datenbytes in hexadezimaler Darstellung ausgegeben.
Zusammenfassung
Die onReceive() Methode ist ein zentraler Bestandteil des Nachrichtenaustauschs in unserem CANopen-Projekt. Sie empfängt CAN-Nachrichten und delegiert ihre Verarbeitung an die entsprechenden Funktionen basierend auf dem Nachrichtentyp. Standard-Nachrichten werden detailliert auf dem seriellen Monitor ausgegeben, während Extended Frames für eine zukünftige Implementierung vorgesehen sind.
Grundlegendes zum CANopen
CANopen ist ein Kommunikationsprotokoll und ein Geräteprofil-Spezifikationssatz für eingebettete Systeme, die auf dem CAN-Bus (Controller Area Network) basieren. Es wird hauptsächlich in der Automatisierungstechnik verwendet, um die Kommunikation zwischen verschiedenen Geräten wie Steuerungen, Sensoren und Aktoren zu standardisieren. Hier sind die grundlegenden Konzepte und Elemente von CANopen:
1. CAN-Bus
CANopen baut auf dem CAN-Bus auf, einer robusten, seriellen Kommunikationsschnittstelle, die für Anwendungen entwickelt wurde, bei denen eine hohe Zuverlässigkeit und eine kurze Latenzzeit erforderlich sind. CAN ermöglicht die Kommunikation zwischen mehreren Mikrocontrollern über ein gemeinsames Kabelpaar.
2. Objektverzeichnis (Object Dictionary, OD)
Das Objektverzeichnis ist das zentrale Datenbanksystem in einem CANopen-Gerät. Es enthält alle Kommunikations- und Anwendungsobjekte, die ein Gerät bereitstellt. Jedes Objekt im Verzeichnis hat eine eindeutige 16-Bit-Indexnummer und eine optionale Unterindexnummer.
3. Kommunikationsobjekte (COB)
Kommunikationsobjekte sind die Grundelemente der CANopen-Kommunikation. Jedes Objekt hat eine eindeutige CAN-ID (COB-ID). Es gibt verschiedene Arten von Kommunikationsobjekten:
- PDO (Process Data Object): Für die zyklische Übertragung von Echtzeitdaten.
- SDO (Service Data Object): Für die asynchrone Übertragung von Konfigurations- und Diagnosedaten.
- NMT (Network Management): Zur Steuerung und Überwachung des Netzwerks.
- SYNC: Zur Synchronisation von Geräten im Netzwerk.
- EMCY (Emergency): Zur Übermittlung von Fehler- und Statusmeldungen.
4. Netzwerkmanagement (NMT)
Das NMT-Protokoll steuert die Betriebszustände der CANopen-Knoten im Netzwerk. Es gibt verschiedene NMT-Zustände wie Initialisierung, Pre-operational, Operational und Stopped. Ein NMT-Master kann diese Zustände durch NMT-Kommandos steuern.
5. Heartbeat und Node Guarding
Heartbeat und Node Guarding sind Mechanismen zur Überwachung des Zustands der Geräte im Netzwerk. Jeder Knoten sendet in regelmäßigen Abständen Heartbeat-Nachrichten, um seine Betriebsbereitschaft zu signalisieren. Node Guarding überwacht die Kommunikation zwischen einem NMT-Master und den Knoten, um sicherzustellen, dass die Knoten weiterhin funktionsfähig sind.
6. PDO-Mapping
PDOs können mehrere Anwendungsdaten beinhalten, die aus dem Objektverzeichnis gemappt werden. Die PDO-Mapping-Parameter bestimmen, welche Daten in den PDOs enthalten sind und wie sie übertragen werden.
7. SDO-Kommunikation
SDOs werden verwendet, um auf die Daten im Objektverzeichnis zuzugreifen. Dies ermöglicht das Lesen und Schreiben von Parametern während des Betriebs. SDO-Kommunikation erfolgt typischerweise auf einer Master-Slave-Basis.
8. EMCY-Nachrichten
Emergency-Nachrichten werden verwendet, um sofortige Fehlerzustände im Netzwerk zu signalisieren. Diese Nachrichten haben eine hohe Priorität und ermöglichen eine schnelle Reaktion auf kritische Fehler.
Liste der Objektadressen
Hier ist eine Liste der Objektadressen für verschiedene Objekttypen in einem typischen CANopen-System:
Process Data Objects (PDOs):
// Receive Process Data Objects (RPDOs):
typedef enum {
RPDO1_MIN = 0x180,
RPDO1_MAX = 0x1FF,
RPDO2_MIN = 0x280,
RPDO2_MAX = 0x2FF,
RPDO3_MIN = 0x380,
RPDO3_MAX = 0x3FF,
RPDO4_MIN = 0x480,
RPDO4_MAX = 0x4FF
} rpdoRange_t;
// Transmit Process Data Objects (TPDOs):
typedef enum {
TPDO1_MIN = 0x200,
TPDO1_MAX = 0x27F,
TPDO2_MIN = 0x300,
TPDO2_MAX = 0x37F,
TPDO3_MIN = 0x400,
TPDO3_MAX = 0x47F,
TPDO4_MIN = 0x500,
TPDO4_MAX = 0x57F
} tpdoRange_t;
Service Data Objects (SDOs):
- Server-SDOs: 0x5800 - 0x5FFF
- Client-SDOs: 0x6000 - 0x67FF
// Transmit Service Data Object
typedef enum {
TSDO_MIN = 0x580,
TSDO_MAX = 0x5FF
} tsdoRange_t;
// Receive Service Data Object
typedef enum {
RSDO_MIN = 0x600,
RSDO_MAX = 0x67F
} rsdoRange_t;
Network Management (NMT):
- Control: 0x000 - Network Management (NMT)
- Status: 0x700 - Network Management (NMT)
SYNC:
- Producer: 0x080
- Consumer: 0x100
Emergency (EMCY):
- Emergency messages: 0x80F
Heartbeat:
- Heartbeat messages: 0x700 + Node-ID
Bitte beachten Sie, dass diese Werte je nach CANopen-Implementierung und Gerätekonfiguration variieren können. Es wird empfohlen, die spezifischen Objekte im Objektverzeichnis (Object Dictionary) des jeweiligen CANopen-Geräts zu überprüfen, um die genauen Adressen zu erhalten.
Kommentare
Kommentar veröffentlichen