TJA1050 CAN-Bus-Modul mit Arduino & ESP32: Mehrere Boards zuverlässig vernetzen

Sobald ein Projekt aus mehr als einem Mikrocontroller besteht – etwa ein Arduino im Gehäuse und ein ESP32 zehn Meter weiter im Gartenhaus – stößt eine einfache UART-Verbindung schnell an ihre Grenzen: störanfällig über längere Kabelwege, nur Punkt-zu-Punkt und ohne eingebaute Fehlererkennung. Genau dafür wurde der CAN-Bus entwickelt. Mit einem TJA1050-Transceiver-Modul verbindest du beliebig viele Arduino- und ESP32-Boards über ein einziges, robustes Zweidraht-Kabel – die gleiche Technik, die in jedem modernen Auto Motorsteuerung, ABS und Bordcomputer miteinander koppelt.

Warum CAN-Bus statt einfachem UART oder I2C?

UART verbindet immer nur zwei Geräte direkt miteinander, I2C ist auf kurze Kabellängen im Zentimeter- bis Dezimeterbereich beschränkt. Der CAN-Bus (Controller Area Network) wurde dagegen für genau die Anforderungen entwickelt, an denen beide scheitern: mehrere Teilnehmer an einem gemeinsamen Bus, Kabellängen von mehreren zehn Metern und eine differenzielle Signalübertragung, die Störungen durch Motoren, Relais oder lange Leitungswege zuverlässig ausgleicht.

Jede Nachricht auf dem Bus hat eine Priorität (die CAN-ID) und eine eingebaute Prüfsumme (CRC). Kollidieren zwei Nachrichten gleichzeitig auf dem Bus, gewinnt automatisch die Nachricht mit der höheren Priorität – ganz ohne dass du das selbst programmieren musst. Das macht CAN-Bus ideal für Multi-Board-Projekte wie Hausautomation mit mehreren Räumen, Gewächshaus-Steuerungen mit verteilten Sensorknoten oder Modellbau-Projekte mit mehreren Steuereinheiten.

Wie das TJA1050-Modul funktioniert

Die gängigen TJA1050-Module kombinieren zwei Bausteine auf einer Platine: den MCP2515, einen CAN-Controller, der über SPI mit deinem Arduino oder ESP32 spricht und die CAN-Nachrichten aufbaut bzw. auswertet, sowie den TJA1050-Transceiver, der die logischen Signale des MCP2515 in die differenziellen Spannungspegel auf den beiden Busleitungen CAN_H und CAN_L umwandelt. Dein Mikrocontroller muss sich also nie selbst um die exakte Bus-Signalisierung kümmern – er reicht fertige CAN-Frames per SPI an den MCP2515 weiter, der Rest passiert in Hardware.

Wichtig für den Aufbau: An beiden physikalischen Enden des CAN-Busses muss ein 120-Ω-Abschlusswiderstand zwischen CAN_H und CAN_L sitzen. Viele Module bringen dafür einen steckbaren Jumper mit – prüfe bei deinem Modul, ob dieser gesetzt ist, wenn das Board am Busende hängt, und entfernt bei Boards, die in der Mitte der Leitung sitzen.

Was du brauchst

2x TJA1050 CAN-Bus Modul – Zum Produkt
1x Arduino UNO R3 (Sender) – Zum Produkt
1x ESP32 WiFi Bluetooth Entwicklungsboard (Empfänger) – Zum Produkt
1x Solderless Breadboard 400 Kontakte – Zum Produkt
22AWG Kabel 2-adrig (für die CAN_H/CAN_L-Busleitung) – Zum Produkt

Verkabelung

Beide TJA1050-Module werden identisch per SPI an ihr jeweiliges Board angeschlossen. Die Busleitungen CAN_H und CAN_L beider Module werden anschließend direkt miteinander verbunden – das ist die gemeinsame Buchleitung.

TJA1050-Pin Arduino UNO ESP32 (Beispiel-Pins)
VCC 5V 3.3V / 5V (je nach Modul)
GND GND GND
CS Pin 10 GPIO 5
SO (MISO) Pin 12 GPIO 19
SI (MOSI) Pin 11 GPIO 23
SCK Pin 13 GPIO 18
INT Pin 2 GPIO 4

Zusätzlich: CAN_H von Modul 1 an CAN_H von Modul 2, CAN_L von Modul 1 an CAN_L von Modul 2. Installiere in der Arduino IDE die Bibliothek "MCP_CAN Library" von coryjfowler über den Bibliotheksverwalter.

Code 1: Sender (Arduino UNO) – Sensorwert als CAN-Nachricht senden

#include <mcp_can.h>
#include <SPI.h>

#define CS_PIN 10
MCP_CAN CAN(CS_PIN);

void setup() {
  Serial.begin(115200);
  while (CAN_OK != CAN.begin(MCP_ANY, CAN_125KBPS, MCP_8MHZ)) {
    Serial.println("CAN Init fehlgeschlagen, wiederhole...");
    delay(500);
  }
  CAN.setMode(MCP_NORMAL);
  Serial.println("CAN Bus bereit (Sender)");
}

void loop() {
  int sensorRoh = analogRead(A0);
  float spannung = sensorRoh * (5.0 / 1023.0);

  byte daten[2];
  int wert = (int)(spannung * 100); // z.B. 3.42V -> 342
  daten[0] = highByte(wert);
  daten[1] = lowByte(wert);

  // CAN-ID 0x100, 2 Datenbytes
  CAN.sendMsgBuf(0x100, 0, 2, daten);

  Serial.print("Gesendet - Spannung: ");
  Serial.print(spannung);
  Serial.println(" V");

  delay(1000);
}

Code 2: Empfänger (ESP32) – Nachrichten auswerten

#include <mcp_can.h>
#include <SPI.h>

#define CS_PIN 5
MCP_CAN CAN(CS_PIN);

void setup() {
  Serial.begin(115200);
  while (CAN_OK != CAN.begin(MCP_ANY, CAN_125KBPS, MCP_8MHZ)) {
    Serial.println("CAN Init fehlgeschlagen, wiederhole...");
    delay(500);
  }
  CAN.setMode(MCP_NORMAL);
  Serial.println("CAN Bus bereit (Empfaenger)");
}

void loop() {
  if (CAN_MSGAVAIL == CAN.checkReceive()) {
    byte len = 0;
    byte daten[8];
    long unsigned int canId;

    CAN.readMsgBuf(&canId, &len, daten);

    if (canId == 0x100 && len == 2) {
      int wert = (daten[0] << 8) | daten[1];
      float spannung = wert / 100.0;
      Serial.print("Empfangen von ID 0x100 - Spannung: ");
      Serial.print(spannung);
      Serial.println(" V");
    }
  }
}

Code 3: Mehrere CAN-IDs filtern (mehrere Sensorknoten)

Sobald mehrere Sender am Bus hängen, willst du auf dem Empfänger gezielt nur bestimmte IDs auswerten. Der MCP2515 unterstützt Hardware-Filter, wodurch irrelevante Nachrichten gar nicht erst den Empfangspuffer belasten.

#include <mcp_can.h>
#include <SPI.h>

#define CS_PIN 5
MCP_CAN CAN(CS_PIN);

void setup() {
  Serial.begin(115200);
  while (CAN_OK != CAN.begin(MCP_ANY, CAN_125KBPS, MCP_8MHZ)) {
    delay(500);
  }

  // Nur IDs 0x100 und 0x101 durchlassen (Temperatur- und Feuchte-Knoten)
  CAN.init_Mask(0, 0, 0x7FF);
  CAN.init_Filt(0, 0, 0x100);
  CAN.init_Mask(1, 0, 0x7FF);
  CAN.init_Filt(1, 0, 0x101);

  CAN.setMode(MCP_NORMAL);
}

void loop() {
  if (CAN_MSGAVAIL == CAN.checkReceive()) {
    byte len = 0;
    byte daten[8];
    long unsigned int canId;
    CAN.readMsgBuf(&canId, &len, daten);

    switch (canId) {
      case 0x100:
        Serial.println("Nachricht vom Temperatur-Knoten");
        break;
      case 0x101:
        Serial.println("Nachricht vom Feuchtigkeits-Knoten");
        break;
    }
  }
}

Praxistipps

🔌 Baudrate immer identisch: Alle Teilnehmer am Bus müssen exakt dieselbe Baudrate verwenden (z. B. CAN_125KBPS). Ein einziges Board mit falscher Rate stört den gesamten Bus.

📏 Kabellänge und Baudrate hängen zusammen: Bei 125 kBit/s sind bis zu 500 m Buslänge möglich, bei 1 MBit/s nur wenige Meter. Für Projekte im Garten oder über mehrere Räume ist eine niedrigere Baudrate meist die bessere Wahl.

🆔 CAN-IDs bewusst vergeben: Niedrigere IDs haben höhere Priorität auf dem Bus. Reserviere niedrige IDs für zeitkritische Nachrichten (z. B. Alarme) und höhere für reine Messwerte.

Troubleshooting

⚠️ "CAN Init fehlgeschlagen" in Dauerschleife: Meist ein SPI-Verdrahtungsfehler oder ein falscher Quarztakt in CAN.begin() – die meisten China-Module nutzen MCP_8MHZ, manche aber MCP_16MHZ. Prüfe die Beschriftung des Quarzes auf deinem Modul.

⚠️ Keine Nachrichten kommen an: Kontrolliere, ob an mindestens einem der beiden Busenden der 120-Ω-Abschlusswiderstand aktiv ist. Fehlt er komplett, reflektieren Signale auf der Leitung und die Kommunikation bricht ab.

ℹ️ Mehr als zwei Boards: Für einen dritten oder vierten Teilnehmer schließt du dessen TJA1050-Modul einfach an dieselben beiden CAN_H/CAN_L-Leitungen an – der CAN-Bus ist als Multi-Drop-Bus genau dafür ausgelegt, ohne Sterntopologie oder zusätzliche Hubs.

⚡ Baue dein eigenes Multi-Board-Netzwerk

Alle Bauteile für dieses Tutorial findest du bei MAKEROO.

Fazit

Mit einem TJA1050-Modul pro Board verlässt du die Welt der Punkt-zu-Punkt-Verbindungen und baust dir ein robustes, erweiterbares Netzwerk aus beliebig vielen Arduino- und ESP32-Knoten auf. Gerade für räumlich verteilte Projekte – Gewächshaus, Smart Home über mehrere Zimmer, Modellbau mit mehreren Steuereinheiten – ist CAN-Bus die deutlich zuverlässigere Wahl gegenüber langen UART- oder I2C-Leitungen.

Zurück zum Blog