Go Down

Topic: Arduino-Nano-CAN-Haus-Bus (Read 990 times) previous topic - next topic

postmaster-ino

Jan 01, 2018, 10:54 pm Last Edit: Jan 01, 2018, 10:59 pm by postmaster-ino
Hi

Zutaten:
- Arduino Nano (Klon :/ )
- CAN-Platine (eBay, erste Treffer)
- DS18B20 (Temperatur-Sensor und 'eindeutige ID'
... laut eingebundener Lib wird die CAN-ID als 32bit genutzt, bei meinen Versuchen werden davon aber nur 11bit wirklich übertragen.
Dadurch bekomme ich auch jetzt schon, bei 4 Knoten, zwei gleiche IDs.
Muß schauen, ob ich den Flaschenhals noch finde, schon ärgerlich genug, von den 48bit der DS18B20-ID nur 32 nutzen zu können, wovon akut aber nur 11 über bleiben.

Heute entstanden vier 'Knoten', Alle laufen mit identischem Code, messen sekündlich die eigene Temperatur (Ausgabe in seriellen Terminal), alle 10 Messungen wird die Temperatur 'nach Außen' gesendet.
Alle im 'Hausbus' empfangenen Nachrichten werden ebenfalls auf das Terminal ausgegeben.
Das sieht, im Terminal, dann so aus:
Die ID 164 ist doppelt - sowohl der am PC angeschlossene Knoten, wie Der im Zählerschrank.
164 Zählerschrank
164 PC (Der vom Bild. weiter Unten, Zeichenbegrenzung)
605 PC (ein etwas 'wilderer' Aufbau, hat's noch nicht 'raus' geschafft)
664 Keller
Die Temperatur (warum ist da ein E dran??) ist die sekündlich Gemessene.
(aus dem OneWire-Beispiel).
Empfangene Nachrichten werden bisher nur auf 0xFF 0x03 geprüft und bestehen aus der ID, den 8 Byte der Nachricht und dem @millis() Wert.
Code: [Select]

Empfange ID: 00000605 Data: FF 03 81 01 7F 22 23 24  (8 Byte) @96812
externe Temperature = 24.06 Celsius, 75.31 Fahrenheit
  Temperature = 24.87 Celsius, 76.77 Fahrenheit
  Temperature = 24.87 Celsius, 76.77 Fahrenheit
  Temperature = 24.87 Celsius, 76.77 Fahrenheit
  Temperature = 24.87 Celsius, 76.77 Fahrenheit
Sende Nachricht 00000164 FF 03 8E 01 7F 0E 0F 10 (8 Bytes)
Empfange ID: 00000164 Data: FF 03 75 01 7F CA CB CC  (8 Byte) @100638
externe Temperature = 23.31 Celsius, 73.96 Fahrenheit
  Temperature = 24.87 Celsius, 76.77 Fahrenheit
  Temperature = 24.87 Celsius, 76.77 Fahrenheit
Empfange ID: 00000664 Data: FF 03 3A 01 7F 95 96 97  (8 Byte) @102722
externe Temperature = 19.62 Celsius, 67.32 Fahrenheit
  Temperature = 24.87 Celsius, 76.77 Fahrenheit
  Temperature = 24.87 Celsius, 76.77 Fahrenheit
  Temperature = 24.87 Celsius, 76.77 Fahrenheit
  Temperature = 24.87 Celsius, 76.77 Fahrenheit
Empfange ID: 00000605 Data: FF 03 81 01 7F 23 24 25  (8 Byte) @106810
externe Temperature = 24.06 Celsius, 75.31 Fahrenheit

Der 'Schöne' beim Foto-Termin:

postmaster-ino

#1
Jan 01, 2018, 10:55 pm Last Edit: Jan 02, 2018, 12:54 am by postmaster-ino
Part 2 - der Sketch:
Code: [Select]

// demo: CAN-BUS Shield, send & receive data
#include <mcp_can.h>
#include <SPI.h>
#include <OneWire.h>


//entsprechend anpassen
#define ID11BIT       //verkürzt die ID auf 11bit, wenn ich die Stelle finde, wo die 11bit-Grenze auf 32 aufgebohrt werden kann, kann Das wieder raus
byte DS_Pin = 3;      //wo ist der DS18B20 angeschlossen?
byte CAN_INT = 2;    //Interrupt-Pin, hier meldet sich das CAN-Modul, wenn was abzuholen ist
MCP_CAN CAN0(10);                                      // CS zum CAN-Modul
// MISO -> SO, MOSI -> SI, SCK -> SCK


OneWire  ds(DS_Pin);  // (a 4.7K resistor is necessary) ... 2K2 gehen auch

byte ds_status = 0;           //alle 10 Durchläufe (=10 Sekunden) Temperatur versenden
INT32U rxId;                  //scheinbar werden nur 11bit unterstützt, FF989E64 -> 664
INT32U OwnID = 0;             //wird in Setup auf die Bytes 1-4 des DS18B20 gesetzt
long unsigned lastsend = 0;   //speichert den millis() Wert der letzten Sendung
unsigned char len = 0;        //bekommt die Länge der empfangenen Nachricht
unsigned char rxBuf[8];      //bekomme nur mit 8 Byte eine korrekte Übertragung hin
unsigned char txBuf[8] = {0, 1, 2, 3, 4, 5, 6, 7};

//Arduino resetten nach http://www.instructables.com/id/two-ways-to-reset-arduino-in-software/
void(* resetFunc) (void) = 0;//declare reset function at address 0
//Reset wird ausgeführt, wenn
// - der DS18B20 nicht gefunden wird (für Temperatur und Seriennummer
// - der CAN-Bus nicht initialisiert werden kann


byte ds_data[12];
byte ds_addr[8];
float celsius, fahrenheit;


void setup()
{
  Serial.begin(9600);

  Serial.print("Datum:");
  Serial.println(__DATE__);
  Serial.print("Zeit:");
  Serial.println(__TIME__);
  Serial.print("File:");
  Serial.println(__FILE__);


  // init can bus, baudrate: 500k
  //if (CAN0.begin(CAN_500KBPS) == CAN_OK){
  if (CAN0.begin(CAN_100KBPS) == CAN_OK) {
    Serial.print("can init ok!!\r\n");
  }  else {
    Serial.print("Can init fail!!\r\n");
    digitalWrite(LED_BUILTIN, HIGH);                  //Fehleranzeige
    delay(1000);
    resetFunc();    //erneuter Versuch in einer Sekunde
  }
  pinMode(CAN_INT, INPUT);                            // Setting pin 2 for /INT input
  Serial.println("MCP2515 Library Send & Receive Example...");


  //DS18B20 vorbereiten, ROM-Addy auslesen, ID generieren (ROM 1,2,3,4)
  ds.reset_search();
  if ( !ds.search(ds_addr)) {
    Serial.println("kein DS18B20 gefunden");
    delay(1000);
    resetFunc();
  }
  if (OneWire::crc8(ds_addr, 7) != ds_addr[7]) {
    Serial.println("CRC is not valid!");
    delay(1000);
    resetFunc();
  }


  //ds_addr wurde auf die ROM-Adresse des gefundenen DS18B20 gesetzt
  //Daraus generieren wir uns unsere 32bit ID
  for (int i = 1; i < 5; i++) {
    OwnID = (OwnID << 8) + ds_addr[i];
  }
#ifdef ID11BIT
Serial.print("ID ");
Serial.print(OwnID,HEX);
Serial.println(" auf 11bit begrenzt");
  OwnID=OwnID & 0x07FF;
#endif
  Serial.print("Chip-ID:");
  Serial.println(OwnID, HEX);
}



void loop()
{
  //Sende-Bereich
  if (millis() - lastsend >= 1000) {
    lastsend = millis();
    //alle 1000ms eine Nachricht senden

    //hier jede Sekunde die Temperatur abfragen und seriell ausgeben
    //alle 10 Sekunden in die Nachricht einkopieren und versenden
    ds.reset();
    ds.select(ds_addr);
    ds.write(0xBE);         // Read Scratchpad

//    Serial.print("  Data = ");
//    Serial.print(" ");
    for (byte i = 0; i < 9; i++) {           // we need 9 bytes
      ds_data[i] = ds.read();
/*
      if (ds_data[i] < 0x10) {
        Serial.print("0");
      }
      Serial.print(ds_data[i], HEX);
      Serial.print(" ");
*/     
    }
/*   
    Serial.print(" CRC=");
    Serial.print(OneWire::crc8(ds_data, 8), HEX);
    Serial.println();
*/
    berechnetemperatur();                   //berechne die selbst gemessene Temperatur
    Serial.print("  Temperature = ");   //und gebe Diese auf dem Terminal aus
    Serial.print(celsius);
    Serial.print(" Celsius, ");
    Serial.print(fahrenheit);
    Serial.println(" Fahrenheit");

    //Stoße neue Temperatur-Wandlung an
    ds.reset();
    ds.select(ds_addr);
    ds.write(0x44, 1);        // start conversion, with parasite power on at the end

    ds_status++;
    if (ds_status == 10) {      //bei jedem 10.ten Durchgang diese Temperatur senden
      ds_status = 0;
      txBuf[0] = 0xFF;  //externer Sensor
      txBuf[1] = 0x03;  //DS18B20, 3 Byte
      txBuf[2] = ds_data[0]; //LSB, Temperatur Roh-Wert
      txBuf[3] = ds_data[1]; //MSB
      txBuf[4] = ds_data[4]; //Konfiguration (9...12bit)

      // send data:  id = 0x00, standard flame, data len = 8, txBuf: data buf
      // id ist INT32U, also 32bit breit!!, es werden aber irgendwie nur 11bit übermittelt
      CAN0.sendMsgBuf(OwnID, 0, 8, txBuf);
      Serial.print ("Sende Nachricht ");
//Quelle https://stackoverflow.com/questions/6499183/converting-a-uint32-value-into-a-uint8-array4
//wohl unnötig, konnte die 32bit ID auch normal als HEX ausgeben
    INT8U out[4];
    *(INT32U*)&out = OwnID;
    for (int i = 3; i >= 0; i--) {
      if (out[i] < 0x10) {
        Serial.print("0");
      }
      Serial.print(out[i], HEX);
    }
     
      for (int i = 0; i < 8; i++) {
        if (txBuf[i] < 0x10) {
          Serial.print(" 0");
        } else {
          Serial.print(" ");
        }
        Serial.print(txBuf[i], HEX);
        txBuf[i]++;
      }
      Serial.println(" (8 Bytes)");
    }
  }


  //Empfangs-Bereich
  if (!digitalRead(2))                        // If pin 2 is low, read receive buffer
  {
    //Hole Nachricht vom CAN-Baustein ab, Dessen INT-Leitung ist HIGH
    //Es wird immer nur die letzte Nachricht beibehalten, also zügig auslesen!
    CAN0.readMsgBuf(&len, rxBuf);              // Read data: len = data length, buf = data byte(s)
    rxId = CAN0.getCanId();                    // Get message ID
    Serial.print("Empfange ID: ");

    //Umwandeln der 32bit Zahl in 4 8bit Blöcke
    INT8U out[4];
    *(INT32U*)&out = rxId;
    for (int i = 3; i >= 0; i--) {
      if (out[i] < 0x10) {
        Serial.print("0");
      }
      Serial.print(out[i], HEX);
    }

    //    Serial.print(rxId, HEX);
    Serial.print(" Data: ");
    for (int i = 0; i < len; i++)             // Print each byte of the data
    {
      if (rxBuf[i] < 0x10)                    // If data byte is less than 0x10, add a leading zero
      {
        Serial.print("0");
      }
      Serial.print(rxBuf[i], HEX);
      Serial.print(" ");
    }
    Serial.print (" (");
    Serial.print (len);
    Serial.print (" Byte) @");
    Serial.println(millis());


    //wenn Nachricht 0xFF (externer Sensor) 0x03 (DS18B20, 3 Byte) LSB MSB CONFIG, dann die übermittelte Temperatur anzeigen
    if (rxBuf[0] == 0xFF) {
      //externer Sensorwert
      if (rxBuf[1] == 0x03) {
        //DS18B20, 3 Nutzbytes
        ds_data[0] = rxBuf[2];
        ds_data[1] = rxBuf[3];
        ds_data[4] = rxBuf[4];
        berechnetemperatur();
        Serial.print("externe Temperature = ");
        Serial.print(celsius);
        Serial.print(" Celsius, ");
        Serial.print(fahrenheit);
        Serial.println(" Fahrenheit");
      }
    }
  }
}


void berechnetemperatur() {
  int16_t raw = (ds_data[1] << 8) | ds_data[0];
  byte cfg = (ds_data[4] & 0x60);
  // at lower res, the low bits are undefined, so let's zero them
  if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
  else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
  else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
  //// default is 12 bit resolution, 750 ms conversion time
  celsius = (float)raw / 16.0;
  fahrenheit = celsius * 1.8 + 32.0;
}
/*********************************************************************************************************
  END FILE
*********************************************************************************************************/

Im Anhang die benutzte CAN-Library - hatte Mehrere versucht und finde jetzt den Link nicht mehr - also als ZIP.
Müsste diese Seite gewesen sein (zumindest sind die Beispiel-Sketche identisch):
www.14core.com/wiring-the-mcp2515-controller-area-network-can-bus-diagnostics/
Eine 'Erweiterung':

... ok, bin halt ein altes Spielkind ;)
Die Heizung hier im Raum steht auf 16° - der Rest kommt wohl vom PC :/
Der Flur ist offen, so zieht die Wärme von alleine hoch, im Winter aber durchaus angenehm.
Im Keller nehme ich an, daß der DS18B20 die Strahlwärme vom Ofenrohr 'sieht' - Da ist's nämlich wirklich sau-kalt.

MfG

postmaster-ino

#2
Jan 02, 2018, 02:37 pm Last Edit: Jan 02, 2018, 02:43 pm by postmaster-ino
Hi

Es lässt mir keine Ruhe :)
Auf Wikipedia fand ich aber schon heraus, daß die 11Bit die Länge des Standard-Identifer sind - also keine ID im herkömmlichem Sinne, sondern eine Art Nachrichten-Marker.
Code: [Select]
Die Spezifikation definiert zwei verschiedene Identifier-Formate:

    11-Bit-Identifier, auch „Base frame format" genannt (CAN 2.0A)
    29-Bit-Identifier, auch „Extended frame format" genannt (CAN 2.0B)

Controller_Area_Network, Wikipedia
So darf es pro Identifier nur einen Sender geben, da sonst zwei Sender gleichzeitig senden könnten und über diesen Identifier entschieden wird, Wer von Beiden gewinnt - eine Art Priorität.
Wenn es hier zwei Gewinner gibt, gibt's Datensalat - befürchte, daß die 'interne Prüfroutiene' Das erkennt und eine Neusendung veranlasst - wenn Das auch wieder Beide gleichzeitig schaffen, hängt der Bus - Das ist aber reine Theorie.

Dann werde ich wohl mein 0xFF 0x03 in diesen Identifier verlegen müssen, oder prüfen, ob die Nachricht angekommen ist ... Mal schauen, wenn meine Knoten eindeutige IDs haben, wäre die Aribitierung so auch geregelt..
Der jetzige Versuch läuft übrigens sehr zufriedenstellend!!
Alle Knoten senden brav alle 10 Sekunden die Temperatur (in der Küche ich Einer dazu gekommen - nun sind die Nano's erst Mal alle beschäftigt) und die Anzeige per LCD (2004) lässt Erweiterungen ungeahnten Spielraum.

Gleiche QWiki-Seite:
Code: [Select]
Data Length Code (DLC) = 4 Bit (Anzahl der Bytes im Datenfeld, 0 bis 8 Bytes, Werte 9 bis 15 werden nicht unterstützt)
könnte erklären, warum ich beim Versuch, mehr als 8 Bytes zu versenden, scheiterte.


MfG

AlexVR6

Hallo,

ich würde das gerne mit einem ESP8266 nachbauen, weißt du ob der Sketch 1:1 auf dem ESP funktioniert?

Gruß Alex

skorpi080

Meister, sage mal, wird bei CAN auch ganze zeit gepollt wie bei zB Modbus?
playground.arduino.cc/PmWiki/433Mhz-Funkmodule

HotSystems

ich würde das gerne mit einem ESP8266 nachbauen, weißt du ob der Sketch 1:1 auf dem ESP funktioniert?
Ich würde es einfach mal testen.
Gruß Dieter

I2C = weniger ist mehr: weniger Kabel, mehr Probleme. 8)

Tommy56

#6
Jan 12, 2018, 11:19 am Last Edit: Jan 12, 2018, 11:21 am by Tommy56
Google mal nach ESP8266-Versionen der Libs.

Gruß Tommy

Edit: Schau mal hier.
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

AlexVR6

Danke für die Hinweise. Die Seite kenne ich schon, da bekomme ich lauter Fehlermeldungen: z.B.

In function 'void setup()':

sketch_can3:15: error: no matching function for call to 'MCP_CAN::begin(int)'

   CAN0.begin(CAN_250KBPS);                       // init can bus : baudrate = 250k

Hab leider zu wenig Erfahrung um die Fehler zu beseitigen.

mfg Alex

michael_x

Ganz grob, ohne Erfahrung mit CAN-Haus-Bus:

Der Fehler sagt nur, dass dein Sketch nicht zur verwendeten Library passt.
Ob die Library überhaupt zur Ziel-Hardware passt, wäre eine mögliche Ursache...

Tommy56

Dessen Sketch passt nicht zur verlinkten Original-Lib. Er hat diese angeblich modifiziert aber schreibt nicht wie.
Die Methode begin will 3 Parameter. Orientiere Dich lieber an den Beispielen der Lib.

Ich mache nix mit CAN - das war nur ein Googletreffer.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

postmaster-ino

Hi
ich würde das gerne mit einem ESP8266 nachbauen, weißt du ob der Sketch 1:1 auf dem ESP funktioniert?
Da ich mit dem ESP8266 noch Nichts gemacht habe, kann ich Dir Da keine große Hilfe sein.
Meister, sage mal, wird bei CAN auch ganze zeit gepollt wie bei zB Modbus?
Jain, die Platinen haben einen INT-Ausgang, Der bei vorhandenen Nachrichten auf LOW gezogen wird.
In meinem Code schaue ich 'jede Runde', ob der Eingang auf LOW ist.
Du könntest auch dieses Signal auf einen Interrupt-Pin geben, aber vielmehr, als ein Flag setzen, passiert dann in der ISR ja auch nicht - und ob ich das Flag polle, oder den Eingang, macht im Ablauf keinen Unterschied.



Was ich bisher weiter rausbekommen habe:
Jede ID muß Einzig im Bus sein, wobei die ID 1 Standard (11 Bit Länge) nicht identisch mit der ID 1 Extended (29 Bit Länge) ist - hier unterscheidet sich die Bitreihenfolge spätestens am 12.ten Bit, wo, meinem Verständnis nach, für Extended eine 1 steht.
Vom Entwickler wurde sich gedacht, daß jeder CAN-Knoten beliebige, aber eindeutige Nachrichten sendet.
Alle Knoten empfangen Diese (was man wohl einstellen kann, daß nur auf bestimmte IDs gehört wird - noch nicht gefunden und noch nicht benötigt) und entscheiden selber, ob Sie damit was anfangen können.
So muß man 'nur' festlegen, daß die ID 1 die Außentemperatur ist und alle Knoten, Die mit der Außentemperatur was anfangen wollen, lesen Diese halt mit (die Anderen vergessen diese Nachricht einfach wieder und gut ist).

Selber werde ich aber wohl IDs vergeben und eine Art Kombination aus Empfänger und Sender-ID als CAN-ID verschicken.
Die Knoten entschlüsseln Diese und erkennen, ob Sie damit gemeint sind.
Je nach Absender oder Nachrichteninhalt wird dann was ausgeführt.

Werde BME280-5V (Luftfeuchte, -temperatur, -druck, I2C) und die FRAM (32kb Speicher, ebenfalls I2C) noch irgendwie verbauen. Eine Art Display oder eine Idee, wie ich Werteverläufe auf einem LCD (2004, kleiner auch vorhanden) anzeigen kann, würde mich durchaus interessieren.
Beim LCD kann ich ja nur wenige Zeichen selbst definieren - eine Pixel-Grafik lässt sich So kaum machen - vll. doch auf das OLED bzw. Nextion warten ...
Eine RTC wird sich in Zukunft auch um eine halbwegs genaue, aber überall identische Uhrzeit kümmern dürfen.
Auch soll, irgend wann Mal, der ganze Kram an eine Datanbank verfüttert werden, wo mindestens lustige Graphiken mit generiert werden sollen (Das hatte ich vor Jahren bereits mit PHP hin bekommen).
Ob ich auch von der PC-Seite wieder zurück in den Bus kommen will, da bin ich mir noch nicht schlüssig.

MfG

Tommy56

Wenn Du irgendwo in Deiner Anlage Internetzugriff hast, dann empfehle ich zur Synchronisation auf die 4 deutschen Zeitserver zurückzugreifen.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

postmaster-ino

Hi

Bisher noch keine Verbindung zur Außenwelt.
Habe hier 'W-Lan-Platinen' ... vor langer Zeit Mal erstanden ... las damals wohl Was mit Wlan.
Einen Monat später waren die Interessen wohl wo anders.
Möchte derzeit aber eh nur ins Netzwerk, um auf einen (noch aufzusetzenden) Apachen mit PHP und mySQL diverse Messwerte abzulegen, eben für Graphen.
Auch reicht mir die Genauigkeit der 3231 - wie im echten Leben, wenn's doch zu weit auseinander läuft, stellt man die Uhr halt nach - ich steuere hier ja kein AKW (und zugegeben: die Wanduhren sind alle DCF77).

MfG

Tommy56

Wenn Du die MySQL im lokalen Netz betreibst, kannst Du direkt in die DB schreiben ohne den Umweg über PHP und Webserver.
Dass man die dann für die grafische Auswertung (z.B. JpGraph) nimmt, ist ja ziemlich komfortabel.
Für Arduino für ESP8266 #6 hier die Libversion beachten.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

michael_x

Wenn Du die MySQL im lokalen Netz betreibst, kannst Du direkt in die DB schreiben ohne Umweg über [...] Webserver.
Nur zu meinem generellen Verständnis: wo läuft/liegt die Datenbank dann,  wenn nicht auf einem Webserver?

Go Up