Conti Fahrrad Akku Technik Faden.

Der chaotische Hauptfaden

Moderatoren: Heaterman, Finger, Sven, TDI, Marsupilami72, duese

Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Och naja, im Prinzip kann man das auch auf der Zusatzplatine machen, da ist ja ein STM32 drauf, der das erledigen kann.
Der hängt ja auch direkt am CAN-Bus
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Ah oh aeh moment, wir haben ja die Firmare, oder? Gibts die Binaries zum herunterladen? Kriegt man das USB leicht in den DFU Modus?

Dann könnte man die vielleicht auch einfach patchen... Ich finds spannend, das BLE Zeug drin zu lassen. Und soweit ich das verstanden hab, ist die CAN Message um die es hier geht vom STM32 interpretiert, nicht vom BMS. Der macht dann alles nötige, um das BMS anzulassen.
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Der STM 32 ist quasi nur das Display. Er sollte für den Betrieb des Akkus im Prinzip nicht notwendig sein.
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

? Die von ATC im Youtube Video gezeigte Variante mit dem 0x201'er CAN message geht doch NUR mit vorhandenem STM/"Display"
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Müsste ohne das „Display“ laufen.
Teste ich mal.

Ok, Test ergab: Ohne die Zusatzplatine kommt keine Spannung. Das liegt aber im Bereich der CAN- Communikation und der Leitung vom Taster. Der Data+ Anschluss geht über das Zusatzboard zum BMS Board, das müsste man noch brücken. Was das Zusatzboard zum BMS sendet kann man ja simulieren.
Lokaro
Beiträge: 16
Registriert: So 20. Nov 2022, 23:18

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Lokaro »

Man kann die "Display" firmware so byte patchen das sie immer denkt es ist ein motor vorhanden, dann sollte es an bleiben
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Genau, die Lösung mit dem Patchen gefällt mir am besten. Hab ich verpasst, wo es den dump gibt? Gern direkt mit den reverse engineerten Symbolen/Adressen... Dann könnte man mal nen Patch entwickeln. Hab leider gerade weder ne Platine noch ne entsprechend ausgerüstete Werkstatt hier :-( Aber Wenn man die Firmware gepatched bekommt & dann der USB drauf laden kann, was täte mir gut gefallen :-)
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Hier müsste alles drin sein
https://atcnetz.de/downloads/conti.zip
Lokaro
Beiträge: 16
Registriert: So 20. Nov 2022, 23:18

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Lokaro »

In dem ZIP ist nun eine .hex mit gepatchtem "Always on" jedoch wird der akku sich derzeit noch relativ schnell wieder ausschalten, ca. 5minuten, da er nicht fährt und in den StandBy schaltet. das muss noch gepatcht werden.
https://atcnetz.de/downloads/conti.zip

Das DFU proto zu reversen ist nicht einfach ohne die original Software und dauert stunden, also lieber direkt flashen.
SWD Pinout ist auch im ZIP
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Wow, danke! Ghidra analysiert fleissig, ich muss ja den periodischen Reset des Flags entfernen sowie es initial setzen, das klingt aber irgendwie realistisch.

Beim Programmieren komm ich nicht weiter und hab ja wie gesagt auch gerade keine Platine da:
Der BOOT0 Pin, der den DFU Bootloader aktivieren würde, ist per Widerstand auf Masse gelegt (=User Anwendung) und liegt auf ne Test-Pin auf der Rückseite.
Mir scheint auch so, als gäbe es aus der Anwendung keinen Jump in den Systemmemory-Bereich, also wird der Bootloader auch nicht Fern-Aktiviert.

Hat mal jemand den USB angesteckt und geschaut was passiert?

Edit: Oh wow, du hasts schon gemacht :-) Danke! Statt SWD könnte man eben originales DFU nehmen, dazu Akku aus, Akku auf, nen Draht anlöten an den Widerstand links vom stm32 (links vom Pin1) auf 3,3V, Akku einschalten (oder reicht USB dranstecken?) -> STM bekommt Strom, DFU läuft.
Benutzeravatar
Kuddel
Beiträge: 5074
Registriert: Fr 28. Jun 2013, 10:56
Wohnort: Denk immer an St. Alamo!

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Kuddel »

Ich konnte mit dem Akku auch endlich loslegen und habe dazu erstmal einen Adapter gebaut. Wichtig war mir auch ein Leistungstest, und siehe da, der Akku schaltet sich bei zu hohen Strömen einfach ab. Vorwiderstand dazuwischen und es geht.
Hier die Video-Dokumentation dazu:
https://youtu.be/VlQznpBGTC0
Gruß
Kuddel
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Ich hab mir ein Akku8 Board gemacht, an welchem 8 Akkus angeschlossen werden können.
Es werden dort 8 MCP2515 CAN Boards aufgesteckt, die die Can-Kommunikation pro Akku übernehmen, da ja alle Akkus die gleiche CAN_ID haben. Zudem sind 2 PCF8574 I2C Port-Expander drauf, die je Board die Chip_Select und den Akku-Start-Knopf bedienen.
Die Interrupt der MCP2515 gehen direkt zum Arduino.
Zudem ein ULN2803 Transitorarray für den Akku-Knopf, da kommen gerne mal 42V.
Warum der Aufwand?
Ich will die Betriebsdaten der Akkus, man könnte auch einfach stupmf auf dem CAN-Bus das 0x201 senden, aber dann weiß man nicht ob der Akku an ist und wie die Spannung und Strom sind.

Bild

Ich brauche 3 Stück von den Boards, hab aber 5Stück, 2 Wären also zu haben.
Jannyboy
Beiträge: 1406
Registriert: So 11. Aug 2013, 14:49
Wohnort: Kreis Augsburg

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Jannyboy »

Lokaro hat geschrieben: Mi 30. Nov 2022, 13:43 Das DFU proto zu reversen ist nicht einfach ohne die original Software und dauert stunden, also lieber direkt flashen.
Da brauchst du nichts reversen die gibt es fertig zum Download bei ST. Guck mal bei dein Bootloader Application Notes.

Grüße Jan
Lokaro
Beiträge: 16
Registriert: So 20. Nov 2022, 23:18

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Lokaro »

Jannyboy hat geschrieben: Do 1. Dez 2022, 23:22 Da brauchst du nichts reversen die gibt es fertig zum Download bei ST. Guck mal bei dein Bootloader Application Notes.
Meinte das DFU Protokoll vom Continental selber, um die Akkus zu flashen ohne zu öffnen.
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Nach dem Mittag muss ich die Leitungen mal durchbimmeln, ob alles wie geplant verdrahtet ist. Zu 100% ist da ein-zehn Fehler drauf. Wie immer

Die Conti-Hydra

Bild
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Lokaro hat geschrieben: Mi 30. Nov 2022, 13:43 In dem ZIP ist nun eine .hex mit gepatchtem "Always on" jedoch wird der akku sich derzeit noch relativ schnell wieder ausschalten, ca. 5minuten, da er nicht fährt und in den StandBy schaltet. das muss noch gepatcht werden.
Das ist ja schon richtig cool von dir - siehst du ne Chance, eventuell demnächst auch noch die 5 Minuten rauszupatchen? Ich komm mit meiner Firmwareanalyse doch nicht so rein, wie ich dachte und du hast ja die ganze Arbeit schon gemacht :D

Alternativ überleg ich gerade, nen kleines Board mit stm32f072 + SN65HVD230 Can Transceiver und ner Mini-Firmware, die alle 200ms die Motorwerte sendet mit in die Akkus zu packen - das geht dann ja ganz ohne Firmware Patch.
Das Teil wird von der STM Platine mit Strom versorgt, geht also an, sobald der Power-Taster gedrückt wird und hält den Akku dann an.
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Das Smart- Board könnte doch den Motor mit zB 5kmh simulieren oder?
Oder ist das Always-On ein q&d patch auf die original Firmware?
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Nein, der Patch ist die originale Smart-Board Firmware mit 3 geänderten Bytes. Ich weiß garnicht, ob es bisher schon einen proof-of-concept für "original BMS ohne SmartBoard" gibt - da müsste halt ne Ecke mehr auf dem CAN passieren.
Und natürlich ist das Smartboard auch ziemlich cool :-)
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

So, da ich das Konzept verworfen habe, den CS über einen I2C Expander zu expandieren, klappt es schon mal mit 4 Akkus.

kku 3 0 Light:0 076% 38,42V -0,02A
Akku 0 0 Light:0 100% 41,15V -0,04A
Akku 2 0 Light:0 099% 41,20V 00,00A
Akku 1 0 Light:0 100% 40,94V -0,02A
Akku 3 0 Light:0 076% 38,42V -0,02A
Akku 0 0 Light:0 100% 41,15V -0,04A
Akku 2 0 Light:0 099% 41,20V 00,00A

Der Anschluss DATA+ heißt jetzt WakeUp, hat ca die Akkuspannung und wird 3 Sekunden gegen Masse gezogen, dann startet der Akku.
Der Anschluss Data- heißt jetzt 12Vout, dort kommen 12V und ein paar mA raus, wenn der Akku läuft.



Hier schon mal ein bisschen Nachtlektüre:


main.cpp

Code: Alles auswählen

#include <Arduino.h>
#include "conti.h"

void setup()
{
    mcp2515.reset();
    mcp2515.setBitrate(CAN_250KBPS);
    mcp2515.setNormalMode();

    Serial.begin(115200);
    Serial.println("Start");

    for (int a = 0; a < 24; a += 4)
    {
        // akku[a].pcf.begin();
        Serial.print("Start PCF 0x");
        Serial.println(akku[a].i2c_addr, 16);
        pcf.begin(akku[a].i2c_addr);
    }
}

void loop()
{

    if (millis() - lastsend >= 100)
    { // Keeps the Battery alive needs to be send periodically
        lastsend = millis();
        // send keepalive to each
        for (int a = 0; a < 4; a++)
        {
            sendCAN(akku[a].cs_pin, 0x201, 4, 0, 1, 0, 0, 0, 0, 0, 0);
        }
    }

    for (int a = 0; a < 4; a++)
    {
        if (MCP2515(akku[a].cs_pin).getErrorFlags() == 0x15)
        { // On Can Error 0x15 restart CAN & Toggle Akku-WakeUp
            //Serial.print("CAN-Error 0x15, CAN-RESET & Toggle Akku-WakeUp :");
            //Serial.println(a);
            WakeUp(a);                   
          
        };        
     
    }



for (int a = 0; a < 4; a++)
{
    if (AkkuValue[a].idle_time + WakeUpTimeOut < millis()) 
    {

        WakeUp(a);
    }


    if (MCP2515(akku[a].cs_pin).readMessage(&canMsg) == MCP2515::ERROR_OK)
    {    AkkuValue[a].idle_time=millis();
        if (1 == 0)
        {                                     // Turn 0 into a 1 to enable debug prints
            Serial.print(canMsg.can_id, HEX); // print ID
            Serial.print("   ");
            Serial.print(canMsg.can_dlc, HEX); // print DLC
            Serial.print("   ");
            for (int i = 0; i < canMsg.can_dlc; i++)
            { // print the data
                Serial.print(canMsg.data[i], HEX);
                Serial.print(" ");
            }
            Serial.println();
        }
        switch (canMsg.can_id)
        {
        case 0x300:
        {
            power_setting = canMsg.data[0];
            if (canMsg.data[2] == 0x64)
                light_status = 1;
            else
                light_status = 0;
            break;
        }
        case 0x404:
        {
            uint16_t voltage = ((uint16_t)canMsg.data[2] | canMsg.data[3] << 8);
            int16_t ampere = ((uint16_t)canMsg.data[0] | canMsg.data[1] << 8);
            uint8_t percent = canMsg.data[4];
            AkkuValue[a].amp=ampere;
            AkkuValue[a].volt=voltage;
            AkkuValue[a].soc=percent;

            Serial.print("Akku ");
            Serial.print(a);
            Serial.print(" ");
            //printf(out_string, "%u%%  %i Light:%i            ", percent, power_setting, light_status);
            sprintf(out_string, "%i Light:%i   %03u%% ", power_setting, light_status, percent);
            Serial.print(out_string);
            if (ampere < 0)
                sprintf(out_string, "%u,%02uV -%i,%02iA            ", voltage / 1000, (voltage % 999) / 10, abs(ampere) / 1000, (abs(ampere) % 999) / 10);
            else
                sprintf(out_string, "%u,%02uV %02i,%02iA            ", voltage / 1000, (voltage % 999) / 10, abs(ampere) / 1000, (abs(ampere) % 999) / 10);
            Serial.println(out_string);
            break;
        }
        }
    }
}
}

conti.h

Code: Alles auswählen

#include <SPI.h>
#include <mcp2515.h> // https://github.com/atc1441/arduino-mcp2515
#include <PCF8574.h>

PCF8574 pcf(0x26);
MCP2515 mcp2515(0);
struct can_frame canMsg;
void sendCAN(uint8_t mcp_addr, uint32_t id, uint8_t length, uint8_t data0 = 0x00, uint8_t data1 = 0x00, uint8_t data2 = 0x00, uint8_t data3 = 0x00, uint8_t data4 = 0x00, uint8_t data5 = 0x00, uint8_t data6 = 0x00, uint8_t data7 = 0x00);
void WakeUp (int a);
void i2c_datasend(int pcf_addr, int pin, bool lvl);


boolean light_status = 0;
uint8_t power_setting = 0;
char out_string[100];
char test;
uint32_t lastsend = 0;
uint32_t lastmsg = 0;
const int WakeUpTimeOut=10000;
const int WakeUpToggle=3000;
const int WakeUpWait=6000; 


typedef struct
{
    uint16_t volt;
    int16_t amp;
    uint8_t soc;
     unsigned long a_time;
     unsigned long idle_time;
    int status;
} akku_values_t;

akku_values_t AkkuValue[24] = {
    /*
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0}
    */
};

typedef struct
{
    int i2c_addr;
    int intr_pin;
    int toggle_pin;
    int cs_pin;
} akku_t;

akku_t akku[24] = {

    {0x26, 3, 0, 49},
    {0x26, 2, 1, 48},
    {0x26, 5, 6, 47},
    {0x26, 4, 7, 46},
    {0x25, 1, 4, 45},
    {0x25, 3, 5, 44},
    {0x25, 2, 6, 43},
    {0x25, 0, 7, 42},
    {0x24, 3, 0, 30},
    {0x24, 2, 1, 31},
    {0x24, 5, 6, 32},
    {0x24, 4, 7, 33},
    {0x23, 1, 4, 34},
    {0x23, 3, 5, 35},
    {0x23, 2, 6, 36},
    {0x23, 0, 7, 37},
    {0x22, 3, 0, 22},
    {0x22, 2, 1, 23},
    {0x22, 5, 6, 24},
    {0x22, 4, 7, 25},
    {0x21, 1, 4, 26},
    {0x21, 3, 5, 27},
    {0x21, 2, 6, 28},
    {0x21, 0, 7, 29}};




void WakeUp (int a){
if ((!AkkuValue[a].status) && (AkkuValue[a].a_time + WakeUpWait < millis()))
            {
                Serial.print(" Start Toggle Akku-WakeUp :");
                Serial.println(a);
                AkkuValue[a].status =1;
                AkkuValue[a].a_time = millis();             
                i2c_datasend(akku[a].i2c_addr, akku[a].toggle_pin, 1);
                MCP2515(akku[a].cs_pin).reset();
                MCP2515(akku[a].cs_pin).setBitrate(CAN_250KBPS);
                MCP2515(akku[a].cs_pin).setNormalMode();
            }

            else if ((AkkuValue[a].a_time + WakeUpToggle < millis() ) && AkkuValue[a].status) 
            {    
                Serial.print(" END Akku-WakeUp :");
                Serial.println(a);
                i2c_datasend(akku[a].i2c_addr, akku[a].toggle_pin, 0);                
                AkkuValue[a].status = 0;         
            }
}


void sendCAN(uint8_t mcp_addr, uint32_t id, uint8_t length, uint8_t data0, uint8_t data1, uint8_t data2, uint8_t data3, uint8_t data4, uint8_t data5, uint8_t data6, uint8_t data7)
{
    MCP2515 mcp_i(mcp_addr);
    struct can_frame canMsg1;
    canMsg1.can_id = id;
    canMsg1.can_dlc = length;
    canMsg1.data[0] = data0;
    canMsg1.data[1] = data1;
    canMsg1.data[2] = data2;
    canMsg1.data[3] = data3;
    canMsg1.data[4] = data4;
    canMsg1.data[5] = data5;
    canMsg1.data[6] = data6;
    canMsg1.data[7] = data7;
    mcp_i.sendMessage(MCP2515::TXB1, &canMsg1);
}

void i2c_datasend(int pcf_addr, int pin, bool lvl) // i2c Adresse , Pin, Low/High
{
    int status;

    if (!(status = pcf.isConnected()))
    {
        Serial.print("Not connected ");
    }

    if (pcf.lastError())
    {
        Serial.print("I2C Fehler Adresse 0x");
        Serial.println(pcf.getAddress());
    }

    pcf.setAddress(pcf_addr);
    pcf.write(pin, lvl);
}


Tiefdruck
Beiträge: 43
Registriert: So 28. Aug 2022, 15:50

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Tiefdruck »

Hallo zusammen, sieht sehr gut aus.

Was mir momentan noch nicht ganz klar ist: Was brauche ich mindestens um einen Akku in betrieb nehmen zu können?

So wie ich das verstanden habe währe das Mikrocontroller -> MCP2515 -> Akku. Stimmt das so weit?
Für meine Anwendung bräuchte ich die Daten per MQTT über WLAN an mein Smarthome. Das heißt als Mikrocontroller währen ein ESP8266 oder ein ESP32 geeignet. Falls sich jemand hier mit GitHub auskennt könnten wir das Projekt an der Stelle gemeinsam vorantreiben. Da der Arduino und die ESP kompatibel sind währen beide Plattformen abgedeckt.

Viele Grüße Thomas
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Also ein ESP.egal oder ein Arduino.egal mit dem MCP2515 sollte ausreichen.

Ich hab heute zum ersten mal das Haus am 6-Conti-Pack, alle 6 Akkus stumpf parallel, die gleichen sich dann gut an.
das läuft mit einem Arduino micro.

Das ich mir mal wieder einen SB3000 dabei gekillt habe.... naja.
Ich glaub ich hab noch Ersatztransen da.

Bild
D2A209DA-E3BF-4AEC-ABBD-96A7066F2947.jpeg
Tiefdruck
Beiträge: 43
Registriert: So 28. Aug 2022, 15:50

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Tiefdruck »

Hallo zusammen,

gibt es schon Neuigkeiten oder Veränderungen an der Software?

Ich bin momentan dabei mein Balkonkraftwerk aufzubauen und habe mich dazu schon in Grafana eingearbeitet. Der nächste Schritt währe die Akkus per Arduino und dem MCP auszulesen. Ich würde die bestehende Software dann auch um MQTT erweitern und hier zur Verfügung stellen.

Viele Grüße Thomas
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Das hier ist mein Code für einen einzelnen Akku-Client, der redet mit dem Akku über Can und mit dem dem Master über i2c.
Main.cpp

Code: Alles auswählen

#include <Arduino.h>
#include <Wire.h>
#include <conti.h>

void setup()
{
  Serial.begin(115200);
  i2c_addr = (EEPROM.read(1));
  Serial.print("I2C: ");
  Serial.println(i2c_addr);
  delay(1000);
  Wire.begin(i2c_addr);
  Wire.onReceive(receiveEvent); // Function call when Slave receives value from master
  Wire.onRequest(requestEvent); // Function call when Master request value from Slave
  DDRD |= (1 << 3);
  PORTD |= (1 << 3);
  Serial.print(" INIT MCP ");
  WakeUp(1);
  PORTD &= ~(1 << 3);
}

void loop()
{
  akku_err = akku_read();

  if (millis() - lastmsg >= 1000)
  {
    lastmsg = millis();
    print_data();
  }
  if (millis() - lastsend >= 90)
  { // Keeps the Battery alive needs to be send periodically
    lastsend = millis();

    // send keepalive to each
    if (command == 1 && fail == 128)

    {
      sendCAN(0x201, 4, 0, 1, 0, 0);
      Serial.print("^");
    }
    else
    {
      Serial.print(".");
    }

    if ((a_time + WakeUpToggle < millis()) && status)
    {
      PORTD &= ~(1 << 3);
      a_time = millis();
      idle_time = millis();
      status = 0;
    }
  }
}
conti.h

Code: Alles auswählen

#include <Arduino.h>
#include <SPI.h>
#include "c_mcp2515.h" // https://github.com/atc1441/arduino-mcp2515
#include <Wire.h>
#include <EEPROM.h>

#define cs_pin 7 // PD7
#define Rx1BF 8  // PB0
#define Rx0BF 9  // PB1
#define BT1 16   // PC2
#define BT2 15   // PC1
#define BT3 14   // PC0
#define BT4 17   // PC3
MCP2515 mcp2515(cs_pin);
int akku_err;
int i2c_addr;

// #define toggle_pin 3
// #define Pow1 4
// #define mcp_int 7

int can400[4];
int can404[8];
//int can405[8];
int can408[8];
int can3B4[8];
int command = 0;
int fail = 222;

void receiveEvent();
void reqeustEvent();

void sendCAN(uint32_t id, uint8_t length, uint8_t data0 = 0x00, uint8_t data1 = 0x00, uint8_t data2 = 0x00, uint8_t data3 = 0x00, uint8_t data4 = 0x00, uint8_t data5 = 0x00, uint8_t data6 = 0x00, uint8_t data7 = 0x00);
void WakeUp(int reason);
int akku_read();

uint32_t lastsend = 0;
uint32_t lastmsg = 0;
const int32_t WakeUpTimeOut = 15000;
const int WakeUpToggle = 3000;
const int32_t WakeUpWait = 15000;
const int32_t MCPWait = 30000;
bool status = 0;
bool reset;
int count = 0;
unsigned long a_time;
unsigned long idle_time;
unsigned long mcp_time;

void receiveEvent()
{
  command = Wire.read();
}

void requestEvent()
{
  Wire.write(can404[4]); // Akku percent 0
  Wire.write(can404[0]); // ampere low 1
  Wire.write(can404[1]); // ampere high 2
  Wire.write(can404[2]); // voltage low 3
  Wire.write(can404[3]); // voltage high 4
  Wire.write(can400[0]); // Power_fail 5
  Wire.write(fail); // PowerStatus 6
}

void WakeUp(int reason)
{
  if ((!(status)) && (count < 100) && (a_time + WakeUpWait < millis()))
  {
    switch (reason)
    {
    case 1:
      Serial.println("PF");
      break;
    case 2:
      Serial.println("TO");
      break;
    case 3:
      Serial.println("MCP");
      break;
    default:
      break;
    }
    mcp2515.reset();
    mcp2515.setBitrate(CAN_250KBPS, MCP_8MHZ);
    // mcp2515.setConfigMode();
    // mcp2515.setInterruptRx1();
    // mcp2515.setFilterMask(MCP2515::MASK1 , false, 0x7FF); // Initialisiere Maskierung für alle IDs
    // mcp2515.setFilter(MCP2515::RXF1, false, 0x400);      // Initialisiere Filter für ID 400
    // mcp2515.setFilter(MCP2515::RXF2, false, 0x404);      // Initialisiere Filter für ID 404
    // mcp2515.setFilter(MCP2515::RXF3, false, 0x405);      // Initialisiere Filter für ID 405
    // mcp2515.setFilter(MCP2515::RXF4, false, 0x405);      // Initialisiere Filter für ID 408   
    mcp2515.setNormalMode();  
    status = 1;
    count++;
    a_time = millis();
    PORTD |= (1 << 3);
  }
}

int akku_read()
{
  struct can_frame canMsg;

  

  if (idle_time + WakeUpTimeOut < millis())
  {
    fail = 111;
    mcp2515.reset();
    mcp2515.setBitrate(CAN_250KBPS, MCP_8MHZ);
    mcp2515.setNormalMode();
    WakeUp(2); // 2 = Reason TimeOut
  }
  
  if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK && !status)
  {     

  if (mcp_time + MCPWait < millis()&& mcp2515.getErrorFlags())
  {
    mcp_time=millis();
    fail = 66;
    mcp2515.reset();
    mcp2515.setBitrate(CAN_250KBPS, MCP_8MHZ);
    mcp2515.setNormalMode();
    WakeUp(3); // 2 = Reason TimeOut
  }
  else if (!mcp2515.getErrorFlags()){
   mcp_time=millis();
  fail = 128;
  }
  




    idle_time = millis();  
    switch (canMsg.can_id)
    {
    case 0x400:
    {
      
      count = 0;
      for (int i = 0; i < canMsg.can_dlc; i++)
      {
        can400[i] = (canMsg.data[i]);

        if (canMsg.data[0] == 0x00 && !status && command)
        {

          WakeUp(1); // Reason 1 = PowerFail
        }
      }
      break;
    }

    case 0x408:
    {
     
      count = 0;
      for (int i = 0; i < canMsg.can_dlc; i++)
      {
        can408[i] = (canMsg.data[i]);
      }
      break;
    }

/*    case 0x405:
    {
      idle_time = millis();
      count = 0;
      fail = 0;
      for (int i = 0; i < canMsg.can_dlc; i++)
      {
        can405[i] = (canMsg.data[i]);
      }

      break;
    }
*/
    case 0x404:
    {
      count = 0;

      for (int i = 0; i < canMsg.can_dlc; i++)
      {
        can404[i] = (canMsg.data[i]);
      }
    }
    }
  }

  return 0;
}

void sendCAN(uint32_t id, uint8_t length, uint8_t data0, uint8_t data1, uint8_t data2, uint8_t data3, uint8_t data4, uint8_t data5, uint8_t data6, uint8_t data7)
{
  struct can_frame canMsg1;
  canMsg1.can_id = id;
  canMsg1.can_dlc = length;
  canMsg1.data[0] = data0;
  canMsg1.data[1] = data1;
  canMsg1.data[2] = data2;
  canMsg1.data[3] = data3;
  canMsg1.data[4] = data4;
  canMsg1.data[5] = data5;
  canMsg1.data[6] = data6;
  canMsg1.data[7] = data7;
  mcp2515.sendMessage(MCP2515::TXB1, &canMsg1);
}

void print_data()
{
  
  Serial.print(" 404 ");
  for (int i = 0; i < 8; i++)
  {
    Serial.print(can404[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" 400 ");
  for (int i = 0; i < 4; i++)
  {
    Serial.print(can400[i], HEX);
    Serial.print(" ");
  }
  //Serial.print(" 405 ");
  //for (int i = 0; i < 8; i++)
  //{
  //  Serial.print(can405[i], HEX);
  //  Serial.print(" ");
 // }

  Serial.print(" 408 ");
  for (int i = 0; i < 8; i++)
  {
    Serial.print(can408[i], HEX);
    Serial.print(" ");
  }

   Serial.print(" fail: ");
  Serial.print(fail);

  Serial.print(" a-time ");
  Serial.print(a_time);
  
   Serial.print(" idle: ");
  Serial.print(idle_time);

  Serial.print(" MCP: ");
  Serial.print(mcp_time);

  Serial.print(" ErrIf: ");
   Serial.println(mcp2515.getErrorFlags(),2);
}
c_mcp2515.cpp

Code: Alles auswählen

#include "c_mcp2515.h"

const struct MCP2515::TXBn_REGS MCP2515::TXB[MCP2515::N_TXBUFFERS] = {
  {MCP_TXB0CTRL, MCP_TXB0SIDH, MCP_TXB0DATA},
  {MCP_TXB1CTRL, MCP_TXB1SIDH, MCP_TXB1DATA},
  {MCP_TXB2CTRL, MCP_TXB2SIDH, MCP_TXB2DATA}
};

const struct MCP2515::RXBn_REGS MCP2515::RXB[N_RXBUFFERS] = {
  {MCP_RXB0CTRL, MCP_RXB0SIDH, MCP_RXB0DATA, CANINTF_RX0IF},
  {MCP_RXB1CTRL, MCP_RXB1SIDH, MCP_RXB1DATA, CANINTF_RX1IF}
};

MCP2515::MCP2515(const uint8_t _CS)
{
  SPI.begin();

  SPICS = _CS;
  pinMode(SPICS, OUTPUT);
  endSPI();
}

void MCP2515::startSPI() {
  SPI.beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE0));
  digitalWrite(SPICS, LOW);
}

void MCP2515::endSPI() {
  digitalWrite(SPICS, HIGH);
  SPI.endTransaction();
}

void MCP2515::setPin(uint8_t pinstate) {
  setRegister(MCP_PinControl, pinstate);
}

MCP2515::ERROR MCP2515::reset(void)
{
  startSPI();
  SPI.transfer(INSTRUCTION_RESET);
  endSPI();

  delay(10);

  uint8_t zeros[14];
  memset(zeros, 0, sizeof(zeros));
  setRegisters(MCP_TXB0CTRL, zeros, 14);
  setRegisters(MCP_TXB1CTRL, zeros, 14);
  setRegisters(MCP_TXB2CTRL, zeros, 14);

  setRegister(MCP_RXB0CTRL, 0);
  setRegister(MCP_RXB1CTRL, 0);



  setRegister(MCP_CANINTE, CANINTF_WAKIF | CANINTF_RX0IF | CANINTF_RX1IF | CANINTF_ERRIF | CANINTF_MERRF);

  modifyRegister(MCP_RXB0CTRL, RXBnCTRL_RXM_MASK | RXB0CTRL_BUKT, RXBnCTRL_RXM_STDEXT | RXB0CTRL_BUKT);
  modifyRegister(MCP_RXB1CTRL, RXBnCTRL_RXM_MASK, RXBnCTRL_RXM_STDEXT);
  return ERROR_OK;
}

uint8_t MCP2515::readRegister(const REGISTER reg)
{
  startSPI();
  SPI.transfer(INSTRUCTION_READ);
  SPI.transfer(reg);
  uint8_t ret = SPI.transfer(0x00);
  endSPI();

  return ret;
}

void MCP2515::readRegisters(const REGISTER reg, uint8_t values[], const uint8_t n)
{
  startSPI();
  SPI.transfer(INSTRUCTION_READ);
  SPI.transfer(reg);
  // mcp2515 has auto-increment of address-pointer
  for (uint8_t i = 0; i < n; i++) {
    values[i] = SPI.transfer(0x00);
  }
  endSPI();
}

void MCP2515::setRegister(const REGISTER reg, const uint8_t value)
{
  startSPI();
  SPI.transfer(INSTRUCTION_WRITE);
  SPI.transfer(reg);
  SPI.transfer(value);
  endSPI();
}

void MCP2515::setRegisters(const REGISTER reg, const uint8_t values[], const uint8_t n)
{
  startSPI();
  SPI.transfer(INSTRUCTION_WRITE);
  SPI.transfer(reg);
  for (uint8_t i = 0; i < n; i++) {
    SPI.transfer(values[i]);
  }
  endSPI();
}

void MCP2515::modifyRegister(const REGISTER reg, const uint8_t mask, const uint8_t data)
{
  startSPI();
  SPI.transfer(INSTRUCTION_BITMOD);
  SPI.transfer(reg);
  SPI.transfer(mask);
  SPI.transfer(data);
  endSPI();
}

uint8_t MCP2515::getStatus(void)
{
  startSPI();
  SPI.transfer(INSTRUCTION_READ_STATUS);
  uint8_t i = SPI.transfer(0x00);
  endSPI();

  return i;
}

MCP2515::ERROR MCP2515::setConfigMode()
{
  return setMode(CANCTRL_REQOP_CONFIG);
}

MCP2515::ERROR MCP2515::setListenOnlyMode()
{
  return setMode(CANCTRL_REQOP_LISTENONLY);
}

MCP2515::ERROR MCP2515::setSleepMode()
{
  return setMode(CANCTRL_REQOP_SLEEP);
}

MCP2515::ERROR MCP2515::setLoopbackMode()
{
  return setMode(CANCTRL_REQOP_LOOPBACK);
}

MCP2515::ERROR MCP2515::setNormalMode()
{
  return setMode(CANCTRL_REQOP_NORMAL);
}

MCP2515::ERROR MCP2515::setMode(const CANCTRL_REQOP_MODE mode)
{
  unsigned long endTime = millis() + 200;
  bool modeMatch = false;
  while (millis() < endTime) {
    modifyRegister(MCP_CANCTRL, CANCTRL_REQOP, mode);
    uint8_t newmode = readRegister(MCP_CANSTAT);
    newmode &= CANSTAT_OPMOD;
    modeMatch = newmode == mode;

    if (modeMatch) {
      break;
    }
  }

  return modeMatch ? ERROR_OK : ERROR_FAIL;

}

MCP2515::ERROR MCP2515::setBitrate(const CAN_SPEED canSpeed)
{
  return setBitrate(canSpeed, MCP_8MHZ);
}

MCP2515::ERROR MCP2515::setBitrate(const CAN_SPEED canSpeed, CAN_CLOCK canClock)
{
  ERROR error = setConfigMode();
  if (error != ERROR_OK) {
    return error;
  }

  uint8_t set, cfg1, cfg2, cfg3;
  set = 1;
  switch (canClock)
  {
    case (MCP_8MHZ):
      switch (canSpeed)
      {
        case (CAN_5KBPS):                                               //   5KBPS
          cfg1 = MCP_8MHz_5kBPS_CFG1;
          cfg2 = MCP_8MHz_5kBPS_CFG2;
          cfg3 = MCP_8MHz_5kBPS_CFG3;
          break;

        case (CAN_10KBPS):                                              //  10KBPS
          cfg1 = MCP_8MHz_10kBPS_CFG1;
          cfg2 = MCP_8MHz_10kBPS_CFG2;
          cfg3 = MCP_8MHz_10kBPS_CFG3;
          break;

        case (CAN_20KBPS):                                              //  20KBPS
          cfg1 = MCP_8MHz_20kBPS_CFG1;
          cfg2 = MCP_8MHz_20kBPS_CFG2;
          cfg3 = MCP_8MHz_20kBPS_CFG3;
          break;

        case (CAN_31K25BPS):                                            //  31.25KBPS
          cfg1 = MCP_8MHz_31k25BPS_CFG1;
          cfg2 = MCP_8MHz_31k25BPS_CFG2;
          cfg3 = MCP_8MHz_31k25BPS_CFG3;
          break;

        case (CAN_33KBPS):                                              //  33.333KBPS
          cfg1 = MCP_8MHz_33k3BPS_CFG1;
          cfg2 = MCP_8MHz_33k3BPS_CFG2;
          cfg3 = MCP_8MHz_33k3BPS_CFG3;
          break;

        case (CAN_40KBPS):                                              //  40Kbps
          cfg1 = MCP_8MHz_40kBPS_CFG1;
          cfg2 = MCP_8MHz_40kBPS_CFG2;
          cfg3 = MCP_8MHz_40kBPS_CFG3;
          break;

        case (CAN_50KBPS):                                              //  50Kbps
          cfg1 = MCP_8MHz_50kBPS_CFG1;
          cfg2 = MCP_8MHz_50kBPS_CFG2;
          cfg3 = MCP_8MHz_50kBPS_CFG3;
          break;

        case (CAN_80KBPS):                                              //  80Kbps
          cfg1 = MCP_8MHz_80kBPS_CFG1;
          cfg2 = MCP_8MHz_80kBPS_CFG2;
          cfg3 = MCP_8MHz_80kBPS_CFG3;
          break;

        case (CAN_100KBPS):                                             // 100Kbps
          cfg1 = MCP_8MHz_100kBPS_CFG1;
          cfg2 = MCP_8MHz_100kBPS_CFG2;
          cfg3 = MCP_8MHz_100kBPS_CFG3;
          break;

        case (CAN_125KBPS):                                             // 125Kbps
          cfg1 = MCP_8MHz_125kBPS_CFG1;
          cfg2 = MCP_8MHz_125kBPS_CFG2;
          cfg3 = MCP_8MHz_125kBPS_CFG3;
          break;

        case (CAN_200KBPS):                                             // 200Kbps
          cfg1 = MCP_8MHz_200kBPS_CFG1;
          cfg2 = MCP_8MHz_200kBPS_CFG2;
          cfg3 = MCP_8MHz_200kBPS_CFG3;
          break;

        case (CAN_250KBPS):                                             // 250Kbps
          cfg1 = MCP_8MHz_250kBPS_CFG1;
          cfg2 = MCP_8MHz_250kBPS_CFG2;
          cfg3 = MCP_8MHz_250kBPS_CFG3;
          break;

        case (CAN_500KBPS):                                             // 500Kbps
          cfg1 = MCP_8MHz_500kBPS_CFG1;
          cfg2 = MCP_8MHz_500kBPS_CFG2;
          cfg3 = MCP_8MHz_500kBPS_CFG3;
          break;

        case (CAN_1000KBPS):                                            //   1Mbps
          cfg1 = MCP_8MHz_1000kBPS_CFG1;
          cfg2 = MCP_8MHz_1000kBPS_CFG2;
          cfg3 = MCP_8MHz_1000kBPS_CFG3;
          break;

        default:
          set = 0;
          break;
      }
      break;

    case (MCP_16MHZ):
      switch (canSpeed)
      {
        case (CAN_5KBPS):                                               //   5Kbps
          cfg1 = MCP_16MHz_5kBPS_CFG1;
          cfg2 = MCP_16MHz_5kBPS_CFG2;
          cfg3 = MCP_16MHz_5kBPS_CFG3;
          break;

        case (CAN_10KBPS):                                              //  10Kbps
          cfg1 = MCP_16MHz_10kBPS_CFG1;
          cfg2 = MCP_16MHz_10kBPS_CFG2;
          cfg3 = MCP_16MHz_10kBPS_CFG3;
          break;

        case (CAN_20KBPS):                                              //  20Kbps
          cfg1 = MCP_16MHz_20kBPS_CFG1;
          cfg2 = MCP_16MHz_20kBPS_CFG2;
          cfg3 = MCP_16MHz_20kBPS_CFG3;
          break;

        case (CAN_33KBPS):                                              //  33.333Kbps
          cfg1 = MCP_16MHz_33k3BPS_CFG1;
          cfg2 = MCP_16MHz_33k3BPS_CFG2;
          cfg3 = MCP_16MHz_33k3BPS_CFG3;
          break;

        case (CAN_40KBPS):                                              //  40Kbps
          cfg1 = MCP_16MHz_40kBPS_CFG1;
          cfg2 = MCP_16MHz_40kBPS_CFG2;
          cfg3 = MCP_16MHz_40kBPS_CFG3;
          break;

        case (CAN_50KBPS):                                              //  50Kbps
          cfg1 = MCP_16MHz_50kBPS_CFG1;
          cfg2 = MCP_16MHz_50kBPS_CFG2;
          cfg3 = MCP_16MHz_50kBPS_CFG3;
          break;

        case (CAN_80KBPS):                                              //  80Kbps
          cfg1 = MCP_16MHz_80kBPS_CFG1;
          cfg2 = MCP_16MHz_80kBPS_CFG2;
          cfg3 = MCP_16MHz_80kBPS_CFG3;
          break;

        case (CAN_83K3BPS):                                             //  83.333Kbps
          cfg1 = MCP_16MHz_83k3BPS_CFG1;
          cfg2 = MCP_16MHz_83k3BPS_CFG2;
          cfg3 = MCP_16MHz_83k3BPS_CFG3;
          break;

        case (CAN_100KBPS):                                             // 100Kbps
          cfg1 = MCP_16MHz_100kBPS_CFG1;
          cfg2 = MCP_16MHz_100kBPS_CFG2;
          cfg3 = MCP_16MHz_100kBPS_CFG3;
          break;

        case (CAN_125KBPS):                                             // 125Kbps
          cfg1 = MCP_16MHz_125kBPS_CFG1;
          cfg2 = MCP_16MHz_125kBPS_CFG2;
          cfg3 = MCP_16MHz_125kBPS_CFG3;
          break;

        case (CAN_200KBPS):                                             // 200Kbps
          cfg1 = MCP_16MHz_200kBPS_CFG1;
          cfg2 = MCP_16MHz_200kBPS_CFG2;
          cfg3 = MCP_16MHz_200kBPS_CFG3;
          break;

        case (CAN_250KBPS):                                             // 250Kbps
          cfg1 = MCP_16MHz_250kBPS_CFG1;
          cfg2 = MCP_16MHz_250kBPS_CFG2;
          cfg3 = MCP_16MHz_250kBPS_CFG3;
          break;

        case (CAN_500KBPS):                                             // 500Kbps
          cfg1 = MCP_16MHz_500kBPS_CFG1;
          cfg2 = MCP_16MHz_500kBPS_CFG2;
          cfg3 = MCP_16MHz_500kBPS_CFG3;
          break;

        case (CAN_1000KBPS):                                            //   1Mbps
          cfg1 = MCP_16MHz_1000kBPS_CFG1;
          cfg2 = MCP_16MHz_1000kBPS_CFG2;
          cfg3 = MCP_16MHz_1000kBPS_CFG3;
          break;

        default:
          set = 0;
          break;
      }
      break;

    case (MCP_20MHZ):
      switch (canSpeed)
      {
        case (CAN_33KBPS):                                              //  33.333Kbps
          cfg1 = MCP_20MHz_33k3BPS_CFG1;
          cfg2 = MCP_20MHz_33k3BPS_CFG2;
          cfg3 = MCP_20MHz_33k3BPS_CFG3;
          break;

        case (CAN_40KBPS):                                              //  40Kbps
          cfg1 = MCP_20MHz_40kBPS_CFG1;
          cfg2 = MCP_20MHz_40kBPS_CFG2;
          cfg3 = MCP_20MHz_40kBPS_CFG3;
          break;

        case (CAN_50KBPS):                                              //  50Kbps
          cfg1 = MCP_20MHz_50kBPS_CFG1;
          cfg2 = MCP_20MHz_50kBPS_CFG2;
          cfg3 = MCP_20MHz_50kBPS_CFG3;
          break;

        case (CAN_80KBPS):                                              //  80Kbps
          cfg1 = MCP_20MHz_80kBPS_CFG1;
          cfg2 = MCP_20MHz_80kBPS_CFG2;
          cfg3 = MCP_20MHz_80kBPS_CFG3;
          break;

        case (CAN_83K3BPS):                                             //  83.333Kbps
          cfg1 = MCP_20MHz_83k3BPS_CFG1;
          cfg2 = MCP_20MHz_83k3BPS_CFG2;
          cfg3 = MCP_20MHz_83k3BPS_CFG3;
          break;

        case (CAN_100KBPS):                                             // 100Kbps
          cfg1 = MCP_20MHz_100kBPS_CFG1;
          cfg2 = MCP_20MHz_100kBPS_CFG2;
          cfg3 = MCP_20MHz_100kBPS_CFG3;
          break;

        case (CAN_125KBPS):                                             // 125Kbps
          cfg1 = MCP_20MHz_125kBPS_CFG1;
          cfg2 = MCP_20MHz_125kBPS_CFG2;
          cfg3 = MCP_20MHz_125kBPS_CFG3;
          break;

        case (CAN_200KBPS):                                             // 200Kbps
          cfg1 = MCP_20MHz_200kBPS_CFG1;
          cfg2 = MCP_20MHz_200kBPS_CFG2;
          cfg3 = MCP_20MHz_200kBPS_CFG3;
          break;

        case (CAN_250KBPS):                                             // 250Kbps
          cfg1 = MCP_20MHz_250kBPS_CFG1;
          cfg2 = MCP_20MHz_250kBPS_CFG2;
          cfg3 = MCP_20MHz_250kBPS_CFG3;
          break;

        case (CAN_500KBPS):                                             // 500Kbps
          cfg1 = MCP_20MHz_500kBPS_CFG1;
          cfg2 = MCP_20MHz_500kBPS_CFG2;
          cfg3 = MCP_20MHz_500kBPS_CFG3;
          break;

        case (CAN_1000KBPS):                                            //   1Mbps
          cfg1 = MCP_20MHz_1000kBPS_CFG1;
          cfg2 = MCP_20MHz_1000kBPS_CFG2;
          cfg3 = MCP_20MHz_1000kBPS_CFG3;
          break;

        default:
          set = 0;
          break;
      }
      break;

    default:
      set = 0;
      break;
  }

  if (set) {
    setRegister(MCP_CNF1, cfg1);
    setRegister(MCP_CNF2, cfg2);
    setRegister(MCP_CNF3, cfg3);
    return ERROR_OK;
  }
  else {
    return ERROR_FAIL;
  }
}

MCP2515::ERROR MCP2515::setClkOut(const CAN_CLKOUT divisor)
{
  if (divisor == CLKOUT_DISABLE) {
    /* Turn off CLKEN */
    modifyRegister(MCP_CANCTRL, CANCTRL_CLKEN, 0x00);

    /* Turn on CLKOUT for SOF */
    modifyRegister(MCP_CNF3, CNF3_SOF, CNF3_SOF);
    return ERROR_OK;
  }

  /* Set the prescaler (CLKPRE) */
  modifyRegister(MCP_CANCTRL, CANCTRL_CLKPRE, divisor);

  /* Turn on CLKEN */
  modifyRegister(MCP_CANCTRL, CANCTRL_CLKEN, CANCTRL_CLKEN);

  /* Turn off CLKOUT for SOF */
  modifyRegister(MCP_CNF3, CNF3_SOF, 0x00);
  return ERROR_OK;
}

void MCP2515::prepareId(uint8_t *buffer, const bool ext, const uint32_t id)
{
  uint16_t canid = (uint16_t)(id & 0x0FFFF);

  if (ext) {
    buffer[MCP_EID0] = (uint8_t) (canid & 0xFF);
    buffer[MCP_EID8] = (uint8_t) (canid >> 8);
    canid = (uint16_t)(id >> 16);
    buffer[MCP_SIDL] = (uint8_t) (canid & 0x03);
    buffer[MCP_SIDL] += (uint8_t) ((canid & 0x1C) << 3);
    buffer[MCP_SIDL] |= TXB_EXIDE_MASK;
    buffer[MCP_SIDH] = (uint8_t) (canid >> 5);
  } else {
    buffer[MCP_SIDH] = (uint8_t) (canid >> 3);
    buffer[MCP_SIDL] = (uint8_t) ((canid & 0x07 ) << 5);
    buffer[MCP_EID0] = 0;
    buffer[MCP_EID8] = 0;
  }
}

MCP2515::ERROR MCP2515::setFilterMask(const MASK mask, const bool ext, const uint32_t ulData)
{
  ERROR res = setConfigMode();
  if (res != ERROR_OK) {
    return res;
  }

  uint8_t tbufdata[4];
  prepareId(tbufdata, ext, ulData);

  REGISTER reg;
  switch (mask) {
    case MASK0: reg = MCP_RXM0SIDH; break;
    case MASK1: reg = MCP_RXM1SIDH; break;
    default:
      return ERROR_FAIL;
  }

  setRegisters(reg, tbufdata, 4);

  return ERROR_OK;
}

MCP2515::ERROR MCP2515::setFilter(const RXF num, const bool ext, const uint32_t ulData)
{
  ERROR res = setConfigMode();
  if (res != ERROR_OK) {
    return res;
  }

  REGISTER reg;

  switch (num) {
    case RXF0: reg = MCP_RXF0SIDH; break;
    case RXF1: reg = MCP_RXF1SIDH; break;
    case RXF2: reg = MCP_RXF2SIDH; break;
    case RXF3: reg = MCP_RXF3SIDH; break;
    case RXF4: reg = MCP_RXF4SIDH; break;
    case RXF5: reg = MCP_RXF5SIDH; break;
    default:
      return ERROR_FAIL;
  }

  uint8_t tbufdata[4];
  prepareId(tbufdata, ext, ulData);
  setRegisters(reg, tbufdata, 4);

  return ERROR_OK;
}

MCP2515::ERROR MCP2515::sendMessage(const TXBn txbn, const struct can_frame *frame)
{
  if (frame->can_dlc > CAN_MAX_DLEN) {
    return ERROR_FAILTX;
  }

  const struct TXBn_REGS *txbuf = &TXB[txbn];

  uint8_t data[13];

  bool ext = (frame->can_id & CAN_EFF_FLAG);
  bool rtr = (frame->can_id & CAN_RTR_FLAG);
  uint32_t id = (frame->can_id & (ext ? CAN_EFF_MASK : CAN_SFF_MASK));

  prepareId(data, ext, id);

  data[MCP_DLC] = rtr ? (frame->can_dlc | RTR_MASK) : frame->can_dlc;

  memcpy(&data[MCP_DATA], frame->data, frame->can_dlc);

  setRegisters(txbuf->SIDH, data, 5 + frame->can_dlc);

  modifyRegister(txbuf->CTRL, TXB_TXREQ, TXB_TXREQ);

  return ERROR_OK;
}

MCP2515::ERROR MCP2515::sendMessage(const struct can_frame *frame)
{
  if (frame->can_dlc > CAN_MAX_DLEN) {
    return ERROR_FAILTX;
  }

  TXBn txBuffers[N_TXBUFFERS] = {TXB0, TXB1, TXB2};

  for (int i = 0; i < N_TXBUFFERS; i++) {
    const struct TXBn_REGS *txbuf = &TXB[txBuffers[i]];
    uint8_t ctrlval = readRegister(txbuf->CTRL);
    if ( (ctrlval & TXB_TXREQ) == 0 ) {
      return sendMessage(txBuffers[i], frame);
    }
  }

  return ERROR_FAILTX;
}

MCP2515::ERROR MCP2515::readMessage(const RXBn rxbn, struct can_frame *frame)
{
  const struct RXBn_REGS *rxb = &RXB[rxbn];

  uint8_t tbufdata[5];

  readRegisters(rxb->SIDH, tbufdata, 5);

  uint32_t id = (tbufdata[MCP_SIDH] << 3) + (tbufdata[MCP_SIDL] >> 5);

  if ( (tbufdata[MCP_SIDL] & TXB_EXIDE_MASK) ==  TXB_EXIDE_MASK ) {
    id = (id << 2) + (tbufdata[MCP_SIDL] & 0x03);
    id = (id << 8) + tbufdata[MCP_EID8];
    id = (id << 8) + tbufdata[MCP_EID0];
    id |= CAN_EFF_FLAG;
  }

  uint8_t dlc = (tbufdata[MCP_DLC] & DLC_MASK);
  if (dlc > CAN_MAX_DLEN) {
    return ERROR_FAIL;
  }

  uint8_t ctrl = readRegister(rxb->CTRL);
  if (ctrl & RXBnCTRL_RTR) {
    id |= CAN_RTR_FLAG;
  }

  frame->can_id = id;
  frame->can_dlc = dlc;

  readRegisters(rxb->DATA, frame->data, dlc);

  modifyRegister(MCP_CANINTF, rxb->CANINTF_RXnIF, 0);

  return ERROR_OK;
}

MCP2515::ERROR MCP2515::readMessage(struct can_frame *frame)
{
  ERROR rc;
  uint8_t stat = getStatus();

  if ( stat & STAT_RX0IF ) {
    rc = readMessage(RXB0, frame);
  } else if ( stat & STAT_RX1IF ) {
    rc = readMessage(RXB1, frame);
  } else {
    rc = ERROR_NOMSG;
  }

  return rc;
}

bool MCP2515::checkReceive(void)
{
  uint8_t res = getStatus();
  if ( res & STAT_RXIF_MASK ) {
    return true;
  } else {
    return false;
  }
}

bool MCP2515::checkError(void)
{
  uint8_t eflg = getErrorFlags();

  if ( eflg & EFLG_ERRORMASK ) {
    return true;
  } else {
    return false;
  }
}

uint8_t MCP2515::getErrorFlags(void)
{
  return readRegister(MCP_EFLG);
}

void MCP2515::clearRXnOVRFlags(void)
{
  modifyRegister(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0);
}

uint8_t MCP2515::getInterrupts(void)
{
  return readRegister(MCP_CANINTF);
}

void MCP2515::clearInterrupts(void)
{
  setRegister(MCP_CANINTF, 0);
}

uint8_t MCP2515::getInterruptMask(void)
{
  return readRegister(MCP_CANINTE);
}

void MCP2515::clearTXInterrupts(void)
{
  modifyRegister(MCP_CANINTF, (CANINTF_TX0IF | CANINTF_TX1IF | CANINTF_TX2IF), 0);
}

void MCP2515::clearRXnOVR(void)
{
  uint8_t eflg = getErrorFlags();
  if (eflg != 0) {
    clearRXnOVRFlags();
    clearInterrupts();
    //modifyRegister(MCP_CANINTF, CANINTF_ERRIF, 0);
  }

}

void MCP2515::clearMERR()
{
  //modifyRegister(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0);
  //clearInterrupts();
  modifyRegister(MCP_CANINTF, CANINTF_MERRF, 0);
}

void MCP2515::clearERRIF()
{
  //modifyRegister(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0);
  //clearInterrupts();
  modifyRegister(MCP_CANINTF, CANINTF_ERRIF, 0);
}

 void MCP2515::setInterruptRx1()
 {

setRegister(MCP_PinControl,4);
setRegister(MCP_RXB1CTRL,5);
 }
 

c_mcp2515.h

Code: Alles auswählen

#ifndef _C_MCP2515_H_
#define _C_MCP2515_H_

#include <SPI.h>
#include "can.h"

/*
    Speed 8M
*/
#define MCP_8MHz_1000kBPS_CFG1 (0x00)
#define MCP_8MHz_1000kBPS_CFG2 (0x80)
#define MCP_8MHz_1000kBPS_CFG3 (0x80)

#define MCP_8MHz_500kBPS_CFG1 (0x00)
#define MCP_8MHz_500kBPS_CFG2 (0x90)
#define MCP_8MHz_500kBPS_CFG3 (0x82)

#define MCP_8MHz_250kBPS_CFG1 (0x00)
#define MCP_8MHz_250kBPS_CFG2 (0xB1)
#define MCP_8MHz_250kBPS_CFG3 (0x85)

#define MCP_8MHz_200kBPS_CFG1 (0x00)
#define MCP_8MHz_200kBPS_CFG2 (0xB4)
#define MCP_8MHz_200kBPS_CFG3 (0x86)

#define MCP_8MHz_125kBPS_CFG1 (0x01)
#define MCP_8MHz_125kBPS_CFG2 (0xB1)
#define MCP_8MHz_125kBPS_CFG3 (0x85)

#define MCP_8MHz_100kBPS_CFG1 (0x01)
#define MCP_8MHz_100kBPS_CFG2 (0xB4)
#define MCP_8MHz_100kBPS_CFG3 (0x86)

#define MCP_8MHz_80kBPS_CFG1 (0x01)
#define MCP_8MHz_80kBPS_CFG2 (0xBF)
#define MCP_8MHz_80kBPS_CFG3 (0x87)

#define MCP_8MHz_50kBPS_CFG1 (0x03)
#define MCP_8MHz_50kBPS_CFG2 (0xB4)
#define MCP_8MHz_50kBPS_CFG3 (0x86)

#define MCP_8MHz_40kBPS_CFG1 (0x03)
#define MCP_8MHz_40kBPS_CFG2 (0xBF)
#define MCP_8MHz_40kBPS_CFG3 (0x87)

#define MCP_8MHz_33k3BPS_CFG1 (0x47)
#define MCP_8MHz_33k3BPS_CFG2 (0xE2)
#define MCP_8MHz_33k3BPS_CFG3 (0x85)

#define MCP_8MHz_31k25BPS_CFG1 (0x07)
#define MCP_8MHz_31k25BPS_CFG2 (0xA4)
#define MCP_8MHz_31k25BPS_CFG3 (0x84)

#define MCP_8MHz_20kBPS_CFG1 (0x07)
#define MCP_8MHz_20kBPS_CFG2 (0xBF)
#define MCP_8MHz_20kBPS_CFG3 (0x87)

#define MCP_8MHz_10kBPS_CFG1 (0x0F)
#define MCP_8MHz_10kBPS_CFG2 (0xBF)
#define MCP_8MHz_10kBPS_CFG3 (0x87)

#define MCP_8MHz_5kBPS_CFG1 (0x1F)
#define MCP_8MHz_5kBPS_CFG2 (0xBF)
#define MCP_8MHz_5kBPS_CFG3 (0x87)

/*
    speed 16M
*/
#define MCP_16MHz_1000kBPS_CFG1 (0x00)
#define MCP_16MHz_1000kBPS_CFG2 (0xD0)
#define MCP_16MHz_1000kBPS_CFG3 (0x82)

#define MCP_16MHz_500kBPS_CFG1 (0x00)
#define MCP_16MHz_500kBPS_CFG2 (0xF0)
#define MCP_16MHz_500kBPS_CFG3 (0x86)

#define MCP_16MHz_250kBPS_CFG1 (0x41)
#define MCP_16MHz_250kBPS_CFG2 (0xF1)
#define MCP_16MHz_250kBPS_CFG3 (0x85)

#define MCP_16MHz_200kBPS_CFG1 (0x01)
#define MCP_16MHz_200kBPS_CFG2 (0xFA)
#define MCP_16MHz_200kBPS_CFG3 (0x87)

#define MCP_16MHz_125kBPS_CFG1 (0x03)
#define MCP_16MHz_125kBPS_CFG2 (0xF0)
#define MCP_16MHz_125kBPS_CFG3 (0x86)

#define MCP_16MHz_100kBPS_CFG1 (0x03)
#define MCP_16MHz_100kBPS_CFG2 (0xFA)
#define MCP_16MHz_100kBPS_CFG3 (0x87)

#define MCP_16MHz_80kBPS_CFG1 (0x03)
#define MCP_16MHz_80kBPS_CFG2 (0xFF)
#define MCP_16MHz_80kBPS_CFG3 (0x87)

#define MCP_16MHz_83k3BPS_CFG1 (0x03)
#define MCP_16MHz_83k3BPS_CFG2 (0xBE)
#define MCP_16MHz_83k3BPS_CFG3 (0x07)

#define MCP_16MHz_50kBPS_CFG1 (0x07)
#define MCP_16MHz_50kBPS_CFG2 (0xFA)
#define MCP_16MHz_50kBPS_CFG3 (0x87)

#define MCP_16MHz_40kBPS_CFG1 (0x07)
#define MCP_16MHz_40kBPS_CFG2 (0xFF)
#define MCP_16MHz_40kBPS_CFG3 (0x87)

#define MCP_16MHz_33k3BPS_CFG1 (0x4E)
#define MCP_16MHz_33k3BPS_CFG2 (0xF1)
#define MCP_16MHz_33k3BPS_CFG3 (0x85)

#define MCP_16MHz_20kBPS_CFG1 (0x0F)
#define MCP_16MHz_20kBPS_CFG2 (0xFF)
#define MCP_16MHz_20kBPS_CFG3 (0x87)

#define MCP_16MHz_10kBPS_CFG1 (0x1F)
#define MCP_16MHz_10kBPS_CFG2 (0xFF)
#define MCP_16MHz_10kBPS_CFG3 (0x87)

#define MCP_16MHz_5kBPS_CFG1 (0x3F)
#define MCP_16MHz_5kBPS_CFG2 (0xFF)
#define MCP_16MHz_5kBPS_CFG3 (0x87)

/*
    speed 20M
*/
#define MCP_20MHz_1000kBPS_CFG1 (0x00)
#define MCP_20MHz_1000kBPS_CFG2 (0xD9)
#define MCP_20MHz_1000kBPS_CFG3 (0x82)

#define MCP_20MHz_500kBPS_CFG1 (0x00)
#define MCP_20MHz_500kBPS_CFG2 (0xFA)
#define MCP_20MHz_500kBPS_CFG3 (0x87)

#define MCP_20MHz_250kBPS_CFG1 (0x41)
#define MCP_20MHz_250kBPS_CFG2 (0xFB)
#define MCP_20MHz_250kBPS_CFG3 (0x86)

#define MCP_20MHz_200kBPS_CFG1 (0x01)
#define MCP_20MHz_200kBPS_CFG2 (0xFF)
#define MCP_20MHz_200kBPS_CFG3 (0x87)

#define MCP_20MHz_125kBPS_CFG1 (0x03)
#define MCP_20MHz_125kBPS_CFG2 (0xFA)
#define MCP_20MHz_125kBPS_CFG3 (0x87)

#define MCP_20MHz_100kBPS_CFG1 (0x04)
#define MCP_20MHz_100kBPS_CFG2 (0xFA)
#define MCP_20MHz_100kBPS_CFG3 (0x87)

#define MCP_20MHz_83k3BPS_CFG1 (0x04)
#define MCP_20MHz_83k3BPS_CFG2 (0xFE)
#define MCP_20MHz_83k3BPS_CFG3 (0x87)

#define MCP_20MHz_80kBPS_CFG1 (0x04)
#define MCP_20MHz_80kBPS_CFG2 (0xFF)
#define MCP_20MHz_80kBPS_CFG3 (0x87)

#define MCP_20MHz_50kBPS_CFG1 (0x09)
#define MCP_20MHz_50kBPS_CFG2 (0xFA)
#define MCP_20MHz_50kBPS_CFG3 (0x87)

#define MCP_20MHz_40kBPS_CFG1 (0x09)
#define MCP_20MHz_40kBPS_CFG2 (0xFF)
#define MCP_20MHz_40kBPS_CFG3 (0x87)

#define MCP_20MHz_33k3BPS_CFG1 (0x0B)
#define MCP_20MHz_33k3BPS_CFG2 (0xFF)
#define MCP_20MHz_33k3BPS_CFG3 (0x87)

enum CAN_CLOCK {
  MCP_20MHZ,
  MCP_16MHZ,
  MCP_8MHZ
};

enum CAN_SPEED {
  CAN_5KBPS,
  CAN_10KBPS,
  CAN_20KBPS,
  CAN_31K25BPS,
  CAN_33KBPS,
  CAN_40KBPS,
  CAN_50KBPS,
  CAN_80KBPS,
  CAN_83K3BPS,
  CAN_95KBPS,
  CAN_100KBPS,
  CAN_125KBPS,
  CAN_200KBPS,
  CAN_250KBPS,
  CAN_500KBPS,
  CAN_1000KBPS
};

enum CAN_CLKOUT {
  CLKOUT_DISABLE = -1,
  CLKOUT_DIV1 = 0x0,
  CLKOUT_DIV2 = 0x1,
  CLKOUT_DIV4 = 0x2,
  CLKOUT_DIV8 = 0x3,
};

class MCP2515
{
  public:
    enum ERROR {
      ERROR_OK        = 0,
      ERROR_FAIL      = 1,
      ERROR_ALLTXBUSY = 2,
      ERROR_FAILINIT  = 3,
      ERROR_FAILTX    = 4,
      ERROR_NOMSG     = 5
    };

    enum MASK {
      MASK0,
      MASK1
    };

    enum RXF {
      RXF0 = 0,
      RXF1 = 1,
      RXF2 = 2,
      RXF3 = 3,
      RXF4 = 4,
      RXF5 = 5
    };

    enum RXBn {
      RXB0 = 0,
      RXB1 = 1
    };

    enum TXBn {
      TXB0 = 0,
      TXB1 = 1,
      TXB2 = 2
    };

    enum /*class*/ CANINTF : uint8_t {
      CANINTF_RX0IF = 0x01,
      CANINTF_RX1IF = 0x02,
      CANINTF_TX0IF = 0x04,
      CANINTF_TX1IF = 0x08,
      CANINTF_TX2IF = 0x10,
      CANINTF_ERRIF = 0x20,
      CANINTF_WAKIF = 0x40,
      CANINTF_MERRF = 0x80
    };

    enum /*class*/ EFLG : uint8_t {
      EFLG_RX1OVR = (1 << 7),
      EFLG_RX0OVR = (1 << 6),
      EFLG_TXBO   = (1 << 5),
      EFLG_TXEP   = (1 << 4),
      EFLG_RXEP   = (1 << 3),
      EFLG_TXWAR  = (1 << 2),
      EFLG_RXWAR  = (1 << 1),
      EFLG_EWARN  = (1 << 0)
    };

  private:
    static const uint8_t CANCTRL_REQOP = 0xE0;
    static const uint8_t CANCTRL_ABAT = 0x10;
    static const uint8_t CANCTRL_OSM = 0x08;
    static const uint8_t CANCTRL_CLKEN = 0x04;
    static const uint8_t CANCTRL_CLKPRE = 0x03;

    enum /*class*/ CANCTRL_REQOP_MODE : uint8_t {
      CANCTRL_REQOP_NORMAL     = 0x00,
      CANCTRL_REQOP_SLEEP      = 0x20,
      CANCTRL_REQOP_LOOPBACK   = 0x40,
      CANCTRL_REQOP_LISTENONLY = 0x60,
      CANCTRL_REQOP_CONFIG     = 0x80,
      CANCTRL_REQOP_POWERUP    = 0xE0
    };

    static const uint8_t CANSTAT_OPMOD = 0xE0;
    static const uint8_t CANSTAT_ICOD = 0x0E;

    static const uint8_t CNF3_SOF = 0x80;
    static const uint8_t CNF3_WAKFIL = 0x40;

    static const uint8_t TXB_EXIDE_MASK = 0x08;
    static const uint8_t DLC_MASK       = 0x0F;
    static const uint8_t RTR_MASK       = 0x40;

    static const uint8_t RXBnCTRL_RXM_STD    = 0x20;
    static const uint8_t RXBnCTRL_RXM_EXT    = 0x40;
    static const uint8_t RXBnCTRL_RXM_STDEXT = 0x00;
    static const uint8_t RXBnCTRL_RXM_MASK   = 0x60;
    static const uint8_t RXBnCTRL_RTR        = 0x08;
    static const uint8_t RXB0CTRL_BUKT       = 0x04;

    static const uint8_t MCP_SIDH = 0;
    static const uint8_t MCP_SIDL = 1;
    static const uint8_t MCP_EID8 = 2;
    static const uint8_t MCP_EID0 = 3;
    static const uint8_t MCP_DLC  = 4;
    static const uint8_t MCP_DATA = 5;

    enum /*class*/ STAT : uint8_t {
      STAT_RX0IF = (1 << 0),
      STAT_RX1IF = (1 << 1)
    };

    static const uint8_t STAT_RXIF_MASK = STAT_RX0IF | STAT_RX1IF;

    enum /*class*/ TXBnCTRL : uint8_t {
      TXB_ABTF   = 0x40,
      TXB_MLOA   = 0x20,
      TXB_TXERR  = 0x10,
      TXB_TXREQ  = 0x08,
      TXB_TXIE   = 0x04,
      TXB_TXP    = 0x03
    };

    static const uint8_t EFLG_ERRORMASK = EFLG_RX1OVR
                                          | EFLG_RX0OVR
                                          | EFLG_TXBO
                                          | EFLG_TXEP
                                          | EFLG_RXEP;

    enum /*class*/ INSTRUCTION : uint8_t {
      INSTRUCTION_WRITE       = 0x02,
      INSTRUCTION_READ        = 0x03,
      INSTRUCTION_BITMOD      = 0x05,
      INSTRUCTION_LOAD_TX0    = 0x40,
      INSTRUCTION_LOAD_TX1    = 0x42,
      INSTRUCTION_LOAD_TX2    = 0x44,
      INSTRUCTION_RTS_TX0     = 0x81,
      INSTRUCTION_RTS_TX1     = 0x82,
      INSTRUCTION_RTS_TX2     = 0x84,
      INSTRUCTION_RTS_ALL     = 0x87,
      INSTRUCTION_READ_RX0    = 0x90,
      INSTRUCTION_READ_RX1    = 0x94,
      INSTRUCTION_READ_STATUS = 0xA0,
      INSTRUCTION_RX_STATUS   = 0xB0,
      INSTRUCTION_RESET       = 0xC0
    };

    enum /*class*/ REGISTER : uint8_t {
      MCP_RXF0SIDH = 0x00,
      MCP_RXF0SIDL = 0x01,
      MCP_RXF0EID8 = 0x02,
      MCP_RXF0EID0 = 0x03,
      MCP_RXF1SIDH = 0x04,
      MCP_RXF1SIDL = 0x05,
      MCP_RXF1EID8 = 0x06,
      MCP_RXF1EID0 = 0x07,
      MCP_RXF2SIDH = 0x08,
      MCP_RXF2SIDL = 0x09,
      MCP_RXF2EID8 = 0x0A,
      MCP_RXF2EID0 = 0x0B,
      MCP_PinControl = 0x0C,
      MCP_CANSTAT  = 0x0E,
      MCP_CANCTRL  = 0x0F,
      MCP_RXF3SIDH = 0x10,
      MCP_RXF3SIDL = 0x11,
      MCP_RXF3EID8 = 0x12,
      MCP_RXF3EID0 = 0x13,
      MCP_RXF4SIDH = 0x14,
      MCP_RXF4SIDL = 0x15,
      MCP_RXF4EID8 = 0x16,
      MCP_RXF4EID0 = 0x17,
      MCP_RXF5SIDH = 0x18,
      MCP_RXF5SIDL = 0x19,
      MCP_RXF5EID8 = 0x1A,
      MCP_RXF5EID0 = 0x1B,
      MCP_TEC      = 0x1C,
      MCP_REC      = 0x1D,
      MCP_RXM0SIDH = 0x20,
      MCP_RXM0SIDL = 0x21,
      MCP_RXM0EID8 = 0x22,
      MCP_RXM0EID0 = 0x23,
      MCP_RXM1SIDH = 0x24,
      MCP_RXM1SIDL = 0x25,
      MCP_RXM1EID8 = 0x26,
      MCP_RXM1EID0 = 0x27,
      MCP_CNF3     = 0x28,
      MCP_CNF2     = 0x29,
      MCP_CNF1     = 0x2A,
      MCP_CANINTE  = 0x2B,
      MCP_CANINTF  = 0x2C,
      MCP_EFLG     = 0x2D,
      MCP_TXB0CTRL = 0x30,
      MCP_TXB0SIDH = 0x31,
      MCP_TXB0SIDL = 0x32,
      MCP_TXB0EID8 = 0x33,
      MCP_TXB0EID0 = 0x34,
      MCP_TXB0DLC  = 0x35,
      MCP_TXB0DATA = 0x36,
      MCP_TXB1CTRL = 0x40,
      MCP_TXB1SIDH = 0x41,
      MCP_TXB1SIDL = 0x42,
      MCP_TXB1EID8 = 0x43,
      MCP_TXB1EID0 = 0x44,
      MCP_TXB1DLC  = 0x45,
      MCP_TXB1DATA = 0x46,
      MCP_TXB2CTRL = 0x50,
      MCP_TXB2SIDH = 0x51,
      MCP_TXB2SIDL = 0x52,
      MCP_TXB2EID8 = 0x53,
      MCP_TXB2EID0 = 0x54,
      MCP_TXB2DLC  = 0x55,
      MCP_TXB2DATA = 0x56,
      MCP_RXB0CTRL = 0x60,
      MCP_RXB0SIDH = 0x61,
      MCP_RXB0SIDL = 0x62,
      MCP_RXB0EID8 = 0x63,
      MCP_RXB0EID0 = 0x64,
      MCP_RXB0DLC  = 0x65,
      MCP_RXB0DATA = 0x66,
      MCP_RXB1CTRL = 0x70,
      MCP_RXB1SIDH = 0x71,
      MCP_RXB1SIDL = 0x72,
      MCP_RXB1EID8 = 0x73,
      MCP_RXB1EID0 = 0x74,
      MCP_RXB1DLC  = 0x75,
      MCP_RXB1DATA = 0x76
    };

    static const uint32_t SPI_CLOCK = 10000000; // 10MHz

    static const int N_TXBUFFERS = 3;
    static const int N_RXBUFFERS = 2;

    static const struct TXBn_REGS {
      REGISTER CTRL;
      REGISTER SIDH;
      REGISTER DATA;
    } TXB[N_TXBUFFERS];

    static const struct RXBn_REGS {
      REGISTER CTRL;
      REGISTER SIDH;
      REGISTER DATA;
      CANINTF  CANINTF_RXnIF;
    } RXB[N_RXBUFFERS];

    uint8_t SPICS;

  private:

    void startSPI();
    void endSPI();

    ERROR setMode(const CANCTRL_REQOP_MODE mode);

    uint8_t readRegister(const REGISTER reg);
    void readRegisters(const REGISTER reg, uint8_t values[], const uint8_t n);
   
    void setRegisters(const REGISTER reg, const uint8_t values[], const uint8_t n);
    void modifyRegister(const REGISTER reg, const uint8_t mask, const uint8_t data);

    void prepareId(uint8_t *buffer, const bool ext, const uint32_t id);

  public:
    MCP2515(const uint8_t _CS);
    ERROR reset(void);
    void setPin(uint8_t pinstate);
    ERROR setConfigMode();
    ERROR setListenOnlyMode();
    ERROR setSleepMode();
    ERROR setLoopbackMode();
    ERROR setNormalMode();
    ERROR setClkOut(const CAN_CLKOUT divisor);
    ERROR setBitrate(const CAN_SPEED canSpeed);
    ERROR setBitrate(const CAN_SPEED canSpeed, const CAN_CLOCK canClock);
    ERROR setFilterMask(const MASK num, const bool ext, const uint32_t ulData);
    ERROR setFilter(const RXF num, const bool ext, const uint32_t ulData);
    ERROR sendMessage(const TXBn txbn, const struct can_frame *frame);
    ERROR sendMessage(const struct can_frame *frame);
    ERROR readMessage(const RXBn rxbn, struct can_frame *frame);
    ERROR readMessage(struct can_frame *frame);
    bool checkReceive(void);
    bool checkError(void);
    void setRegister(const REGISTER reg, const uint8_t value);
    uint8_t getErrorFlags(void);
    void clearRXnOVRFlags(void);
    uint8_t getInterrupts(void);
    uint8_t getInterruptMask(void);
    void clearInterrupts(void);
    void clearTXInterrupts(void);
    uint8_t getStatus(void);
    void clearRXnOVR(void);
    void clearMERR();
    void clearERRIF();
    void setInterruptRx1();
};

#endif

can.h

Code: Alles auswählen

#ifndef CAN_H_
#define CAN_H_

#include <stdint.h>


typedef unsigned char __u8;
typedef unsigned short __u16;
typedef unsigned long __u32;


/* special address description flags for the CAN_ID */
#define CAN_EFF_FLAG 0x80000000UL /* EFF/SFF is set in the MSB */
#define CAN_RTR_FLAG 0x40000000UL /* remote transmission request */
#define CAN_ERR_FLAG 0x20000000UL /* error message frame */

/* valid bits in CAN ID for frame formats */
#define CAN_SFF_MASK 0x000007FFUL /* standard frame format (SFF) */
#define CAN_EFF_MASK 0x1FFFFFFFUL /* extended frame format (EFF) */
#define CAN_ERR_MASK 0x1FFFFFFFUL /* omit EFF, RTR, ERR flags */

/*
 * Controller Area Network Identifier structure
 *
 * bit 0-28 : CAN identifier (11/29 bit)
 * bit 29   : error message frame flag (0 = data frame, 1 = error message)
 * bit 30   : remote transmission request flag (1 = rtr frame)
 * bit 31   : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
 */
typedef __u32 canid_t;

#define CAN_SFF_ID_BITS     11
#define CAN_EFF_ID_BITS     29

/* CAN payload length and DLC definitions according to ISO 11898-1 */
#define CAN_MAX_DLC 8
#define CAN_MAX_DLEN 8

struct can_frame {
    canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
    __u8    can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
    __u8    data[CAN_MAX_DLEN] __attribute__((aligned(8)));
};

#endif /* CAN_H_ */

Benutzeravatar
Toni
Beiträge: 2523
Registriert: Di 13. Aug 2013, 18:24

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Toni »

Hightech hat geschrieben: So 22. Jan 2023, 17:45 ....

Das ich mir mal wieder einen SB3000 dabei gekillt habe.... naja.
Ich glaub ich hab noch Ersatztransen ...
Weißt du was genau den SB3000 gekillt hatte?
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Ja, ein leeren 2200yF Kondensator an den geladenen PV Eingang angeschlossen .
Benutzeravatar
Toni
Beiträge: 2523
Registriert: Di 13. Aug 2013, 18:24

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Toni »

Ah, OK. Gut zu wissen.

Ich habe bei meinem DC-DC auf SB3000 eine "Ladeschaltung": 2x 100W 230V Halogen in Reihe, die mit einem Schalter gebrückt werden, wenn die Spannungen angeglichen sind.
Tiefdruck
Beiträge: 43
Registriert: So 28. Aug 2022, 15:50

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Tiefdruck »

Wenn ich das richtig verstanden habe hast du pro "Akku Client" einen Arduino und zusätzlich einen Master, der die Informationen per I2C empfängt und entsprechend weiter leitet.

Bei mir werden das später zwei oder 4 Akkus sein die ich Parallel schalten werde es gibt also nur einen CAN Eingang.

Edit:

Hier die Antwort auf die Frage die gleich folgt, im Link geht es zu einem Schaubild in dem der Anschluss erläutert ist:
https://github.com/atc1441/arduino-mcp2515


Platform IO kannte ich noch nicht, mit ein bisschen Einarbeitung funktioniert das aber super. Der Code läuft auf meinem Arduino, allerdings konnte ich nicht herausfinden welcher Pin des MCP an welchen Pin des Arduino gehört. Ich hätte gedacht das es in der Conti.h definiert ist, allerdings werde ich aus der Angabe nicht schlau:

Viele Grüße Thomas
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Der MCP2515 kommt am Arduino an die Pins
SCK, RESET, MOSI, MISO
Nur den CS (SS) Pin muss man noch in der MCP2515 angeben mit

z.B.
MCP2515 mcp2515(cs_pin);
Bildschirmfoto zu 2023-03-11 17-33-05.png
Benutzeravatar
ProgBernie
Beiträge: 584
Registriert: Fr 16. Sep 2022, 21:59
Wohnort: Zwischen Hamburg und Haiti ^W Lübeck

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von ProgBernie »

Hinweis zum Wiki: Die dort gezeigte Steckerbelegung der Flachsteckerbuchse ist falsch, es sind BAT+ und BAT- vertauscht! Bei der gezeigten Lage ist BAT- oben links neben "+12V out" und BAT+ unten rechts naben "Wakeup +Vbat". Findet man wenn man den Ladestecker gegen die Flachmannbuchse durchklingelt oder sich wundert wenn bei abgeschaltetem Akku 42V über Vorwiderling auf 0.7V zusammenbrechen.

Erster Akku lädt gerade testweise...
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Danke, ich habe es korrigiert.

Nochmal für die Verzweifelten und Ratlosen:

Der Akku läuft in der Regel, wenn man 12V an BAT- und Wakeup anschließt. Dann lässt er sich auch laden.

Vorsicht! Der Wakeup wird vom Test-Taster kurzgeschlossen, also die Spannungsquelle evtl. mit 1k an den Wakeup anschließen.
Und nochmal Vorsicht, am Wakeup liegen 30-42V an. Zwar nur sehr wenig Strom, aber zum Port-Killen reicht es.
daniel.haustein
Beiträge: 1
Registriert: Di 16. Mai 2023, 12:35

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von daniel.haustein »

Hallo zusammen,

ich habe heute mal euren Beitrag durchgelesen, weil ich langsam etwas verzweifelt bin. Ich habe ein Continental Bike mit 36 V Antrieb und die Batterie bricht so ab ca. 30 % zusammen. Daher habe ich mir einen vermeintlich gleichen Akku bestellt. Leider ist die Artikelnummer minimal anders, nämlich anstelle 6802200304, ist diese beim neuen Akku 6802200313. Einziger Unterschied am Gehäuse ist eine kleine Schnittstelle über der Ladestandsanzeige. Also genau wie bei eurem Akku. Die Folge ist, das das Bike nichts tut, nachdem man den Akku gewechselt hat. Das Bike ist einfach tot. Schreiben an Hersteller und hin und Kunz hat nichts gebracht. Nach einem halben Jahr weiterer Suche habe ich nun aufgegeben und gestern versucht das Zellenpaket umzuschrauben. Dabei ist mir aufgefallen, das es bei meinem Akku die obere Platine nicht gibt und das Bandkabel was dorthin geht und zur Ladestandsanzeige, bei mir nicht geteilt ist. Bei mir geht es ausschließlich zur Ladestandsanzeige. Der seitliche Chip ist fast identisch, nur wo das Band angeklemmt wird, sind es 10 Kontakte bei euch, bei mir nur 8. Leider ist beim Umschrauben trotz aller Vorsicht ein kurzer Funken gekommen. Nun ist die unersetzliche Platine wohl gegrillt und ich kenne mich in dieser Materie so gar nicht aus und hoffe daher auf eure Hilfe, einen Tipp etc. was mir zur Lösung meines Problems beiträgt. Für alle die sich meiner Sache annhemen schon einmal einen herzlichen Dank.[img]
Tiefdruck
Beiträge: 43
Registriert: So 28. Aug 2022, 15:50

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Tiefdruck »

Hallo zusammen,

gibt es etwas neues von eurer Seite? Wer hat sein Projekt schon umgesetzt und bei wem hapert es noch?

Ich hatte bisher nur angefangen mit die Schaltung zum anzapfen des Bus zusammenzubauen, weiter bin ich noch nicht gekommen.

Momentan überlege ich, wie ich die Kontaktierung dauerhaft korrosionsbeständig hinbekomme. An den Seiten der Akkus währen für den Strom und die Ansteuerung noch Hohlbuchsen vorhanden, ich bin mir aber nicht sicher was das korrekte maß an Büschelsteckern ist.

Viele Grüße Thomas
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Mein 20x ContiAkku PV Speicher läuft seit April stabil.
Da sind 20 Parallel angeschlossen und werden mittels CAN Bus am Laufen gehalten.
Kontaktiert sind die über die Flachstecker 4.8mm
Benutzeravatar
ferdimh
Beiträge: 9379
Registriert: Fr 16. Aug 2013, 15:19

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von ferdimh »

Ich habe probeweise 25 Akkus im Auto in Betrieb. Bisher bin ich noch nicht liegengeblieben, die Kapazität schien sich nach 50 Zyklen noch zu steigern. Insgesamt habe ich jetzt um die 150 Zyklen runter, und 6000km zurückgelegt.
RickY
Beiträge: 268
Registriert: Mo 12. Aug 2013, 09:44

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von RickY »

ferdimh hat geschrieben: Di 11. Jul 2023, 07:51 Ich habe probeweise 25 Akkus im Auto in Betrieb. Bisher bin ich noch nicht liegengeblieben, die Kapazität schien sich nach 50 Zyklen noch zu steigern. Insgesamt habe ich jetzt um die 150 Zyklen runter, und 6000km zurückgelegt.
Was ist das für ein Auto? Und wie hast du das mit der Ansteuerung der Akkus gemacht?
Benutzeravatar
video6
Beiträge: 6794
Registriert: Mi 23. Sep 2015, 09:18
Wohnort: Laage bei Rostock

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von video6 »

RickY hat geschrieben: Di 11. Jul 2023, 08:52
ferdimh hat geschrieben: Di 11. Jul 2023, 07:51 Ich habe probeweise 25 Akkus im Auto in Betrieb. Bisher bin ich noch nicht liegengeblieben, die Kapazität schien sich nach 50 Zyklen noch zu steigern. Insgesamt habe ich jetzt um die 150 Zyklen runter, und 6000km zurückgelegt.
Was ist das für ein Auto? Und wie hast du das mit der Ansteuerung der Akkus gemacht?
Das würde mich auch richtig interessieren ich kaue noch gedankliche drauf rum.
Mechanisch wie elektrisch und wie das aufladen dann von sich gehen soll.
Hab nur einen Accu in Betrieb genommen aber auch nur probehalber.
Benutzeravatar
ferdimh
Beiträge: 9379
Registriert: Fr 16. Aug 2013, 15:19

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von ferdimh »

Der Karren ist ein 2000er Citroen Berlingo Electrique.
Der hat original eine Systemspannung von 162V, daher erschienen 50 Zellen in Reihe zweckmäßig.
Am Antrieb und an der Steuerung wurde nichts verändert.
Das Ganze ist erstmal nur ein Provisorium, um die Fuhre überhaupt fahrbar zu kriegen und das Verhalten zu beobachten. Der Akku steht daher auch zur Zeit im Kofferraum.
Die einzelnen Packen wurden vom Gehäuse befreit und 5 Stück parallel und 5 Stück in Reihe verschaltet, mit einer 160A NH-Sicherung nach jedem Paket zu 10 Zellen.
Die Überwachungselektronik ist ein Voltmeter im Handschuhfach, mitdem ich regelmäßig (ca. alle 10 Zyklen) die Zellspannungen nachmesse. Einen Balancer gibt es nicht, bisher bestand auch keine Notwendigkeit des Ausgleichens (außer, als ich ein Paket für andere Zwecke gebraucht habe, und danach nicht genau auf die richtige Spannung geladen habe).

Blöderweise kann ich die Zellanordnung des Originalakkus nicht weiternutzen, weil die Form überhaupt nicht zur Form des Batteriekastens passen will.
Die originalen Batteriekästen sind mittlerweile entkernt, und letzten Donnerstag sind dann auch endlich die Platinen zur entgültigen Verschaltung der Zellen gekommen. Dort sind dann auch Anschlüsse für 50 Microcontrollerboards vorgesehen, die die Einzelzellspannungen über Optokoppler melden. Automatische Balancierung ist nach wie vor nicht vorgesehen, da ich das Schadensrisiko eines amoklaufenden Balancers (hatte ich am CityEl, war super...) höher einschätze als die Notwendigkeit, irgendwann von Hand zu balancieren, weil die Zellen auseinander laufen.
Tiefdruck
Beiträge: 43
Registriert: So 28. Aug 2022, 15:50

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Tiefdruck »

Video6 hast du den Akku inclusive BUS Steuerung in Betrieb genommen?

Hightech, nutzt du den CAN Bus nur um die Akkus wach zu halten oder auch um die Werte daraus auszulesen? Mein Plan war einen ESP32/ESP8266 an meine vier Akkus anzubinden und die Daten der Akkus (Ladezustand, Spannung... ) per MQTT an mein Smarthome weiter zu leiten.

Leider bin ich dabei nicht sehr weit gekommen.

Viele Grüße Thomas
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Ja, ich nutze den CAN-BUS um die Daten der Akkus zu überwachen und den Akku wach zu halten.
Strom/Spannung/Ladezustand.
tom
Beiträge: 1242
Registriert: Sa 19. Okt 2013, 23:17

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von tom »

Hallo,
weiß jemand wieviele Wh der Conti-Foren-Akku hat ?
Benutzeravatar
Toni
Beiträge: 2523
Registriert: Di 13. Aug 2013, 18:24

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Toni »

Ich glaube der 36V Akku hat 650Wh
tom
Beiträge: 1242
Registriert: Sa 19. Okt 2013, 23:17

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von tom »

Hat noch jemand belastbare Angaben der Kapazität ?
Stimmt die Angabe vom Hightech: 636,125Wh ?
Bei meinen Akkus wurde das Typenschild Zerstört. Deshalb habe ich keine Angaben.
Benutzeravatar
sukram
Beiträge: 3063
Registriert: Sa 10. Mär 2018, 18:27
Wohnort: Leibzsch

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von sukram »

Schau mal hier:

https://www.fingers-welt.de/wiki/index.php?title=Akku

Da wäre alles wichtige am Anfang, denke ich. 10S*5P*3Ah*3,635V sind dann die oben genannten 636,125Wh
Bert
Beiträge: 3
Registriert: Fr 21. Jul 2023, 08:13

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Bert »

Hallo Zusammen
Ich bin übers Netz auf diesen Thread gestoßen da Ich seit 3 Tagen ein Problem mit meinen E-Bike Akku habe.
Es handelt sich um einen Continental 48V 610 WH Akku. Laufleistung des Akkus ca.4500Km Sommer wie Winter durch.
Das Problem ist sobald Ich den Akku an das Ladegerät klemme blinkt die LED rot/grün, Problem beim Ladevorgang laut Netz.
Hatte ihn dann gestern mal aufgeschraubt ,nix zu erkennen.
Ich selber komme aus dem Mateallbau und Elektronik ist nicht so unbedingt meine Welt :roll:
Gibt es eine Möglichkeit den Fehler irgendwie Einzugrenzen auf das Ladegerät/Akku.
Lohnt es sich die Packs mal durch zu messen oder Finger weg als Laie.
Da Conti leider die E-Bike Sparte aufgegeben hat ist es was schwer da für Ersatz zu sorgen. Es gibt ein/zwei die Verkaufen zwar als neu doch ob es wirklich so ist?

Danke schon mal für eure Mühen und Gruß aus Köln

Bert
Benutzeravatar
video6
Beiträge: 6794
Registriert: Mi 23. Sep 2015, 09:18
Wohnort: Laage bei Rostock

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von video6 »

Messe doch mal alle Zellenpakete einzeln durch.
Vielleicht hast du dann den Fehler schon gefunden.
Bert
Beiträge: 3
Registriert: Fr 21. Jul 2023, 08:13

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Bert »

Hi.
Das heißt auf Schlosserdeutsch die kompletten Pakete :D und da wo ein großer Unterschied ist da würde dann der Fehler liegen bzw. die Ursache dafür das der Akku nicht mehr geladen wird?
Dachte bis jetzt halt, Ok wenn eine Zelle kaputt ist dann währe nicht mehr die Kapazität(Reichweite) vorhanden.

Gruß
Benutzeravatar
ferdimh
Beiträge: 9379
Registriert: Fr 16. Aug 2013, 15:19

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von ferdimh »

Eine Kette ist nur so stark wie ihr schwächstes Glied. Also müsste der Strom auch durch die defekte Zelle durch. dabei würde man diese potentiell anzünden. Da das in der Allgemeinheit eher unpopulär ist, wird die Kiste bei einer defekten Zellgruppe (und die Ursache davon wird eine einzelne Zelle sein) abschalten.
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Schau mal nach der Artikelnummer.
Einen 48V Akku hab ich noch.
Bert
Beiträge: 3
Registriert: Fr 21. Jul 2023, 08:13

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Bert »

Hi
Anbei mal ein Bild vom Akku
Bild

Wenn du da noch was hättest das währ super.

Gruß Bert
Benutzeravatar
zauberkopf
Beiträge: 9481
Registriert: So 11. Aug 2013, 15:33
Wohnort: gefährliches Halbwissen

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von zauberkopf »

Automatische Balancierung ist nach wie vor nicht vorgesehen, da ich das Schadensrisiko eines amoklaufenden Balancers (hatte ich am CityEl, war super...) höher einschätze als die Notwendigkeit, irgendwann von Hand zu balancieren, weil die Zellen auseinander laufen.
Was issen denn bei Dir schiefgelaufen ?
Meine selbst entwickelten Balancer hatten z.B. das Problem, das die auf Schmutz allergisch ( schwingen ) reagiert hatten.
Dann gibts ein paar BMS Chips von TI, die ggf so krass abstürzen können, das jede 2. Zelle tiefentladen wird.

Ich habe übrigens heute erfolgreich einen Bosch Akku reparieren können.
Das Ding kann auch interessante Fehler liefern :
Bei mir war nur die 40(!!!!!)V Diode vom Stepdown Wandler durchlegiert.
Und hat dadurch den rest geschützt.
Allerdings kann auch mehr passieren, und dann fließt dauerhaft strom, und der Akku ist irgendwann tot.
Beim Bosch Akku sind die Balancerwiderstände richtig winzig !
Allerdings sind die Streuung von 18650er aus feinster Massenproduktion auch verdammt gering.
Schau aber, das die Akkus nicht ungleichmäßig warm werden.
Antworten