Wireless CAN mit Arduino Nano und HC-05

Hallo Zusammen!
Ich bin zur Zeit noch Student der Elektrotechnik und befinde mich mittlerweile in den Schwerpunkt-Semestern. Für ein Projekt im Modul “Digitaltechnik Praktikum” wurde uns die Aufgabe übertragen eine Möglichkeit zu entwickeln ein CAN-Bus Signal kabellos zu übertragen. Wie wir dieses Projekt umsetzen wurde allein uns überlassen.

Nach einigen Recherchen und aufgrund bisheriger gesammelter Erfahrung im Bereich Arduino und Bluetooth, haben wir uns entschlossen die Umsetzung des Projektes mittels folgender Bauteile zu versuchen:

2x Arduino Nano 328P, 2x HC-05, 2x MCP2515_CAN Board

Bilder der verwendeten HC-05 Module und der MCP2515_CAN Boards findet ihr im Anhang.
Außerdem habe ich einen Prinzipschaltplan für das MCP2515 Board angehängt.
Rx und Tx des HC-05 Moduls wurden über SoftwareSerial auf die Pins 4+5 gelegt.

Um das CAN Board anzusprechen und auszulesen haben wir folgende Git Bibliothek verwendet:

Auf beiden Arduinos läuft folgender Code:

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

SoftwareSerial BTSerial(4,5);

long unsigned int rxIdCAN;
long unsigned int rxIdBT;
unsigned char lenCAN = 0;
unsigned char lenBT = 0;
unsigned char rxBuf[8];
char msgString[128];                        // Array to store serial string
byte data[8];
int counter;

#define CAN0_INT 2                              // Set INT to pin 2
MCP_CAN CAN0(10);                               // Set CS to pin 10


void setup()
{
  Serial.begin(9600);
  BTSerial.begin(38400);
  
  // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled.
  CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) == CAN_OK;
  
  CAN0.setMode(MCP_NORMAL);                     // Set operation mode to normal so the MCP2515 sends acks to received data.
  pinMode(CAN0_INT, INPUT);                     // Configuring pin for /INT input
  counter = 0;
 }

void loop()
{
  if(!digitalRead(CAN0_INT))                         // If CAN0_INT pin is low, read receive buffer, and send via BT
  {
    CAN0.readMsgBuf(&rxIdCAN, &lenCAN, rxBuf);      // Read data: len = data length, buf = data byte(s)
  
     BTSerial.write(lenCAN);
     BTSerial.write(rxIdCAN);
     for(byte i = 0; i<lenCAN; i++){
       BTSerial.write(rxBuf[i]);
     }

  }

  else if(BTSerial.available()) {               // Receive BT Signal and send to MCP2515
    if(counter == 0) {
      lenBT = BTSerial.read();
      counter++;
    }

    else if(counter == 1) {
      rxIdBT = BTSerial.read();
      counter++;
    }

    else if(counter > 1 && counter < 9) {
      data[counter-2] = BTSerial.read();
      counter++;
    }

    else if(counter == 9) {
      data[7] = BTSerial.read();
      byte sndStat = CAN0.sendMsgBuf(rxIdBT, lenBT, data);
      counter = 0;
    }

    else {
      counter = 0;
    }
  }
}

Wir haben nun von unserem Betreuer 2 Geräte Namens TinyCAN bekommen, die wir über USB an 2 PC´s anschließen und mittels der mitgelieferten Software ein CAN Signal ausgeben und empfangen können.
CAN High+Low werden jeweils mit dem MCP2515 verbunden.

Die beiden HC-05 sind bereits miteinander verbunden.

Senden wir nun von der einen Seite eine Nachricht, wird sie auf der anderen Seite von der Software empfangen, alles scheint zu funktionieren. Im nächsten Schritt wollte unser Betreuer, dass wir im 10ms Intervall Nachrichten übertragen. Auch dies funktioniert. Wenn wir aber beginnen von beiden Seiten aus im 10ms Intervall Nachrichten zu verschicken, entstehen irgendwann Fehler. Außerdem melden die TinyCAN-Geräte eine Störung.

Mir ist bewusst, dass der Code für das empfangen der Nachricht via BT nicht besonders schlau gelöst ist und die gesamte Nachricht als ein Paket übertragen werden sollte, jedoch hatten wir hierzu bisher keine Idee. Weder wie die Nachricht auf der Sender-Seite verschickt werden sollte, noch wie wir sie auf der Empfängerseite wieder auseinander nehmen können.

Wir sind mit der gesamten Thematik zur Zeit etwas überfragt. Hilfestellung von unserem Betreuer bekommen wir wenig bis gar nicht. Wir sind also über alle Ideen und Anregungen dankbar.

Viele Grüße
Julian

Es ist nicht gesichert, dass SoftSerial die 38400 Baud sauber übertragen kann. In den meisten Fällen wird es auf 9600 Baud beschränkt.
Der MEGA mit mehreren HardwareSerial wäre hier besser gewesen.

Gruß Tommy

Moin Julian,
da habt Ihr Euch was vorgenommen.

SoftwareSerial: Grob gerechnet 100 Nachrichten/s mal 10 Byte mal 10 Bit sind 10kbps - das wird eng, wenn ihr wirklich nur mit 9600 baud arbeiten müsstet.

rxIdCAN/rxIdBt: Da habt ihr jeweils eine Beschränkung auf ein Byte beim Schreiben auf BTSerial (Zeile 40) und beim Lesen (Zeile 54). Seid ihr sicher, dass eure CAN-Id’s in ein Byte passen? Die haben schon beim Standard 11 Bit, es können auch 29 sein.

TinyCANs: Ihr müsst höllisch aufpassen, dass die beiden Kandidaten nicht Nachrichten auf derselben ID senden - das gibt immer Bruch bei CAN. Was ist denn die Fehlermeldung? Error Active / Error Passive?

500KPBS CAN: Das ist mutig. Versucht mal 125, das ist signaltechnisch etwas entspannter und das Ding dann immer noch zehnmal so schnell wie die serielle Schnittstelle zu den HC05.

Ich habe mal auf einem Raspi mit dem MCP2515 gearbeitet; da gab es bei hoher Nachrichtenlast auch immer Probleme.
Eure Lib von GitHub kenne ich jetzt nicht, aber wenn die nur einen Nachrichtenpuffer hat, kann es sein, dass auf dem CAN schon die nächste Nachricht eingetrudelt ist während ihr noch mit der seriellen Übertragung zu den BT Chips beschäftigt seid.

Ist der CAN-Interrupt wirklich nutzbar? Dann würde ich den ausprobieren, um die Nachrichten vom MCP abzuholen - ggf. müsst ihr einen kleinen Nachrichtenpuffer aufbauen. Da seht ihr dann schon, ob der systematisch überläuft.

Viel Erfolg!
Walter

Hallo Walter, zunächst einmal herzlichen Dank für deine ausführliche Antwort!

wno158:
SoftwareSerial: Grob gerechnet 100 Nachrichten/s mal 10 Byte mal 10 Bit sind 10kbps - das wird eng, wenn ihr wirklich nur mit 9600 baud arbeiten müsstet.

Am Freitag stehen uns die TinyCANs wieder zur Verfügung, da werde ich mal versuchen die Baudrate auf 9600 für die SoftwareSerial zu stellen. Sollten wir anstelle der SoftwareSerial vielleicht mal die normale serielle Schnittstelle des Arduino verwenden?

wno158:
rxIdCAN/rxIdBt: Da habt ihr jeweils eine Beschränkung auf ein Byte beim Schreiben auf BTSerial (Zeile 40) und beim Lesen (Zeile 54). Seid ihr sicher, dass eure CAN-Id's in ein Byte passen? Die haben schon beim Standard 11 Bit, es können auch 29 sein.

Hier verstehe ich leider nicht genau was du meinst. rxIdCAN und rxIdBt sind doch long Integer? Tut mir leid, wenn ich deine Frage nicht richtig verstehe, wir sind in der ganzen Materie noch sehr neu (aber lernwillig :)).

wno158:
TinyCANs: Ihr müsst höllisch aufpassen, dass die beiden Kandidaten nicht Nachrichten auf derselben ID senden - das gibt immer Bruch bei CAN. Was ist denn die Fehlermeldung? Error Active / Error Passive?

Wir haben darauf geachtet nicht bei beiden Tinys die selben IDs zu versenden. Auf eine Fehlermeldung haben wir leider nicht geachtet es blinkte lediglich ein rotes Lämpchen am TinyCAN so lange, bis wir die USB Verbindung unterbrochen und neu verbunden haben. Auch hier können wir am Freitag nochmal nachsehen.

wno158:
500KPBS CAN: Das ist mutig. Versucht mal 125, das ist signaltechnisch etwas entspannter und das Ding dann immer noch zehnmal so schnell wie die serielle Schnittstelle zu den HC05.

Das werden wir am Freitag auch mal probieren, vielen Dank für den Tipp!

wno158:
Ist der CAN-Interrupt wirklich nutzbar? Dann würde ich den ausprobieren, um die Nachrichten vom MCP abzuholen - ggf. müsst ihr einen kleinen Nachrichtenpuffer aufbauen. Da seht ihr dann schon, ob der systematisch überläuft.

Verstehe ich das richtig, dass der Interrupt reagiert wenn am CAN eine Nachricht eingeht? Ich denke das sollte funktionieren, wir lesen ja in Zeile 35 den CAN0_INT in einer if-Bedingung aus und reagieren, wenn dieser auf Low ist.

Danke nochmal für die Antworten und Tipps. Am Freitag werde ich diese mal probieren.

Hallo Julian,
da sind ein paar neue Fragen dazugekommen - ich probiere es mal.

Serielle Schnittstelle:
Mehr als 9600baud wären schon besser. Wenn Du die Hardware-Schnittstelle verwenden willst, kannst Du nur entweder Arduino-ID (Flashen, seriellen Monitor nutzen) oder eben den BT-Chip ansteuern. Beides gleichzeitig geht nicht. Deshalb die Anregung von Tommy, einen Mega zu verwenden.

CAN-IDs:
Ja, die Variablen sind long deklariert. Aber BTSerial.write(rxIdCAN) und rxIdBT = BTSerial.read() bringen jeweils - soweit mir bekannt und wie der Aufruf aussieht - nur ein Byte rüber (dann das unterste). Schaltet doch mal alle Compilerwarnungen ein; da müsste eigentlich Mecker wegen Informationsverlust durch implizite Typkonvertierung zu sehen sein.

Interrupt:
Auch richtig - ihr fragt die INT-Leitung immer wieder ab und holt Euch dann die Nachricht com CAN ab. INT kann man aber auch dazu verwenden, automatisch eine Funktion auszuführen, eine sogenannte Interrupt Service Routine. Die wird dann so schnell wie möglich ausgeführt - egal was der Prozessor sonst gerade zu tun hat. Wenn die ISR fertig ist, geht es an der Stelle weiter wo das normale Programm unterbrochen wurde.
Vorteil: Der CAN wird sofort ausgelesen und damit frei für den Empfang der nächsten Nachricht; bei dem jetzigen Verfahren kommt der erst wieder an die Reihe, wenn ihr mit dem BT fertig seid.
Nachteil: Man muss ein wenig Aufwand treiben für die Synchronisierung zwischen normalem Programm (was die CAN-Nachricht an den BT gibt) und der ISR (die die Nachricht com CAN speichert). Nicht dass ihr gerade eine Nachricht auf den BT schreibt und mittendrin der Interrupt die Bytes überschreibt - dann können lustige Mischungen zwischen alter und neuer Nachricht dabei herauskommen.
Da solltet ihr Euch noch etwas schlau lesen.

Gruß Walter

Hallo Walter,

zunächst mal etwas allgemeines: Ich habe exakt die selbe Frage in einem weiteren, bekannten Microcontroller-Forum gestellt. Ich denke meine Frage war sauber und sachlich formuliert und kein "Hilfe ich bin faul, macht ihr das"-Thread. In dem anderen Forum habe ich nicht einen einzigen sinnvollen Beitrag erhalten. Lediglich die Hinweise, dass es traurig wäre was heutzutage so in den Unis herumläuft und sich später Ingenieur nennt. Weiterhin haben wir Hinweise bekommen, dass wir bisher alles falsch gemacht haben. Wie wir es besser machen können fehlte diesen Beiträgen leider.

Deshalb an dieser Stelle mal ein ganz großes Lob an dieses Forum und vielen, vielen Dank an dich Walter!

Am Freitag war ich wieder im Labor und konnte deine Vorschläge testen. Wir haben die Übertragungsrate auf 125kbps eingestellt. Außerdem habe ich die Bibliothek für den CAN-Transceiver angepasst und sämtliche Serial Befehle entfernt. So konnten wir die reguläre serielle Schnittstelle des Arduino mit der Baudrate 38400 verwenden. Diese beiden Schritte halfen uns schließlich zum Erfolg. Wir sind jetzt in der Lage mehrere Nachrichten mit unterschiedlicher ID und 10ms Intervall in beide Richtungen zeitgleich zu übertragen. Das ist genau das, was unser Betreuer von uns erwartet hat.

Wir können nun am Montag anfangen eine entsprechende Platine zu layouten und schaffen es so wohl doch noch zum Abgabetermin fertig zu werden. :slight_smile:

Also noch einmal vielen Dank für deine Tipps Walter, du hast uns sehr geholfen!

Edit: Selbstverständlich auch ein Dank an Tommy!

... ich erlaube mir ja kaum zu Fragen ... klingt irgendwie nach mc.net :wink:
(aka Arduino und so)

Schön, daß Du Es bis hierhin schon hinbekommen hast und Danke für das Feedback.

Na prima! Dem "Dank für das Feedback" möchte ich mich gerne anschließen. Da bleibt heutzutage leider so mancher Thread in der Luft hängen und nachfolgende Leser können schwer ermessen, ob es denn nun läuft oder nicht.

Noch ein kleiner Senf zum "Ingenieur":
Du hast ein Problem, kannst das - aus welchen Gründen auch immer (Zeitdruck kann einer sein) nicht alleine lösen und suchst Dir deshalb Hilfe. Korrekt!
Du fragst freundlich, liest und verstehst die Antworten und versuchst, daraus Nutzen für Deine Aufgabe zu ziehen. Korrekt!
Und am Ende gibst Du auch noch anständig Rückmeldung - ich kann jetzt nirgendwo was erkennen, was mich zum Maulen veranlassen könnte.

Gruß Walter
(der gar kein Ingenieur ist, aber mit vielen seit fast drei Jahrzehnten zusammen arbeitet)