@randyandy sagte in CAN-BUS Interface ESP32 > MQTT und Stiebel Eltron WP steuern:
@dossidr
bei mir läuft der Sniffer nun seit ein paar Wochen. ICh denke ich habe ein ähnliches PRoblem wie Du und von Zeit zu Zeit hängt sich das System auf. Ich habe nun mal die Buffergröße verändert und (Daumen drücken) seit 2 Tagen alles ok. Hast Du das Problem gelöst ?
Die Buffergröße scheint es zu sein. Ich beobachte das nun noch ein wenig und dann poste ich mal meine Firmware.
Hier noch die letzte Firmware. Läuft nun seit Tagen ohne Probleme.
#include <Arduino.h>
#include <mcp2515.h>
#include <WiFi.h>
#include <ArduinoOTA.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
const char* ssid = "..."; // Ersetze mit deinem WiFi-Namen
const char* password = "..."; // Ersetze mit deinem WiFi-Passwort
const char* mqtt_server = "..."; // Dein MQTT Broker Server
const int mqtt_port = ...;
const char* mqtt_user = "...";
const char* mqtt_password = "...";
const char* mqtt_topic = "...";
void(* resetFunc) (void) = 0;
// Timer declare
hw_timer_t * timer = NULL;
volatile SemaphoreHandle_t timerSemaphore;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
volatile uint32_t isrCounter = 0;
volatile uint32_t lastIsrAt = 0;
void ARDUINO_ISR_ATTR onTimer(){
// Increment the counter and set the time of ISR
portENTER_CRITICAL_ISR(&timerMux);
isrCounter = isrCounter + 1;
lastIsrAt = millis();
portEXIT_CRITICAL_ISR(&timerMux);
// Give a semaphore that we can check in the loop
xSemaphoreGiveFromISR(timerSemaphore, NULL);
// It is safe to use digitalRead/Write here if you want to toggle an output
}
WiFiClient espClient;
PubSubClient client(espClient);
MCP2515 mcp2515(5); // Der CS-Pin muss entsprechend deiner Schaltung angepasst werden.
// Diese Funktion wird aufgerufen, wenn eine Nachricht auf einem abonnierten Thema ankommt
void callback(char* topic, byte* payload, unsigned int length) {
Serial.println("Callback aufgerufen: " + String(topic));
char canSendTopic[128]; //War vorher 64
snprintf(canSendTopic, sizeof(canSendTopic), "%s/canRawGesendet", mqtt_topic);
String topicString = String(topic);
// Überprüfe, ob das Topic stimmt
if (topicString.equals(String(mqtt_topic) + "/canSend")) {
// Konvertiere payload in einen String zur Verarbeitung
String payloadString;
for (int i = 0; i < length; i++) {
payloadString += (char)payload[i];
}
if (length == 0 || payloadString == "0" || payloadString == "OK" || payloadString == "INIT" ) {
Serial.println("Leere Nachricht ");
return;
}
// Parse die JSON-Nachricht
StaticJsonDocument<200> doc;
DeserializationError error = deserializeJson(doc, payloadString);
if (error || !doc.is<JsonObject>()) {
Serial.println("Fehler beim Parsen von JSON");
client.publish(canSendTopic, ("JSON PARS ERROR: " + payloadString).c_str());
return;
}
String tempDataString = doc["Data"].as<String>(); // Konvertiert den Wert zu einem String
tempDataString.replace(" ", ""); // Entfernt alle Leerzeichen
doc["Data"] = tempDataString.c_str(); // Speichert den geänderten Wert zurück im JSON-Objekt
// Extrahiere ID und Daten aus dem JSON
const char* idString = doc["ID"];
const char* dataString = doc["Data"];
// Umwandeln der ID in einen Integer
uint16_t id = strtol(idString, NULL, 16);
// Umwandeln des Datenstrings in ein Byte-Array
uint8_t data[10];
size_t dataLength = strlen(dataString) / 2;
for (size_t i = 0; i < dataLength; i++) {
char byteString[] = {dataString[i * 2], dataString[i * 2 + 1], 0};
data[i] = strtol(byteString, NULL, 16);
}
// Erstelle CAN-Nachricht und sende sie
can_frame canMsg;
canMsg.can_id = id;
canMsg.can_dlc = dataLength;
memcpy(canMsg.data, data, dataLength);
// Erstelle einen String, um die Hex-Darstellung zu speichern
char msgString[35]; // Länge = Anzahl der Bytes * 2 (für Hex) + 1 (für '\0')
// Konvertiere jeden Byte der Daten in einen Hex-String
for (int i = 0; i < canMsg.can_dlc; i++) {
sprintf(&msgString[i * 2], "%02X", canMsg.data[i]);
}
// Sende CAN-Nachricht
if (mcp2515.sendMessage(&canMsg) != MCP2515::ERROR_OK) {
Serial.println("Fehler beim Senden der CAN-Nachricht");
client.loop();
delay(100);
client.publish(canSendTopic, "SEND ERROR");
// Initialisiere den MCP2515 CAN Controller neu
mcp2515.reset();
mcp2515.setBitrate(CAN_20KBPS, MCP_8MHZ);
// Setze die Masken und Filter
mcp2515.setFilterMask(MCP2515::MASK0, true, 0x000); // Maske 0 auf 0 setzen (alle Nachrichten akzeptieren)
mcp2515.setFilterMask(MCP2515::MASK1, true, 0x000); // Maske 1 auf 0 setzen (alle Nachrichten akzeptieren)
// Setze alle Filter auf 0, um alle Nachrichten zu akzeptieren
//for (int filter = 0; filter < 6; filter++) {
//mcp2515.setFilter((MCP2515::RXF)filter, true, 0x000);
//}
mcp2515.setNormalMode();
} else {
// Gib den Hex-String aus
Serial.print("Raw CAN Data: ");
Serial.println(msgString);
client.publish(canSendTopic, msgString); // Sende die Daten zum MQTT-Broker
client.loop();
Serial.println("Erfolgreich gesendet");
client.publish(topicString.c_str(), "OK", false);
}
} else {
Serial.println("Falsches Topic");
return;
}
}
void setup() {
// Timer Setup
timerSemaphore = xSemaphoreCreateBinary(); // Create semaphore to inform us when the timer has fired
timer = timerBegin(1000000); // Set timer frequency to 1Mhz = 1µs
timerAttachInterrupt(timer, &onTimer); // Attach onTimer function to our timer.
timerAlarm(timer, 1000000, true, 0); // Set alarm to call onTimer function every second (value in microseconds). Repeat the alarm (third parameter) with unlimited count = 0 (fourth parameter).
// Initialisiere die serielle Kommunikation
Serial.begin(115200);
pinMode(2, OUTPUT); // Setze GPIO 26 als Ausgang für die LED
// Warte auf die serielle Verbindung, falls nötig
while (!Serial) {
; // warte auf die Verbindung der seriellen Schnittstelle
}
// Verbinde mit WiFi-Netzwerk
WiFi.begin(ssid, password);
Serial.print("Verbinde mit WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi verbunden!");
Serial.print("IP-Adresse: ");
Serial.println(WiFi.localIP());
connectMQTT();
// Initialisiere den MCP2515 CAN Controller
mcp2515.reset();
mcp2515.setBitrate(CAN_20KBPS, MCP_8MHZ);
// Setze die Masken und Filter
mcp2515.setFilterMask(MCP2515::MASK0, true, 0x000); // Maske 0 auf 0 setzen (alle Nachrichten akzeptieren)
mcp2515.setFilterMask(MCP2515::MASK1, true, 0x000); // Maske 1 auf 0 setzen (alle Nachrichten akzeptieren)
// Setze alle Filter auf 0, um alle Nachrichten zu akzeptieren
//for (int filter = 0; filter < 6; filter++) {
//mcp2515.setFilter((MCP2515::RXF)filter, true, 0x000);
//}
mcp2515.setNormalMode();
Serial.println("CAN-Bus Monitor gestartet!");
//Starte OTA
ArduinoOTA.setHostname("esp32-ota");
ArduinoOTA.setPassword("..."); //Hier das Passowrd für OTA setzen
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_SPIFFS
type = "filesystem";
}
// Hinweis: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Receive Failed");
} else if (error == OTA_END_ERROR) {
Serial.println("End Failed");
}
});
ArduinoOTA.begin();
}
void loop() {
static unsigned long lastMillis = 0;
char aliveTopic[64];
char timestampString[32]; // Genug Platz für die Darstellung des Zeitstempels
connectMQTT();
if (xSemaphoreTake(timerSemaphore, 0) == pdTRUE){
uint32_t isrCount = 0, isrTime = 0;
// Read the interrupt count and time
portENTER_CRITICAL(&timerMux);
isrCount = isrCounter;
isrTime = lastIsrAt;
portEXIT_CRITICAL(&timerMux);
// Print it
Serial.print("onTimer no. ");
Serial.print(isrCount);
Serial.print(" at ");
Serial.print(isrTime);
Serial.println(" ms");
if (isrTime > 1000 * 60 * 60 * 12) {
resetFunc();
}
}
// Sende alle 5 Sekunden eine Nachricht an das Thema "alive"
if (millis() - lastMillis > 5000) {
lastMillis = millis();
snprintf(aliveTopic, sizeof(aliveTopic), "%s/alive", mqtt_topic); // Kombiniere das Hauptthema mit "/alive"
snprintf(timestampString, sizeof(timestampString), "%lu", millis()); // Konvertiere den Zeitstempel in einen String
client.publish(aliveTopic, timestampString); // Sende das Zeichen "!" an das Unterthema "alive"
Serial.println("Alive-Nachricht gesendet!");
digitalWrite(2, HIGH); // LED einschalten
//delay(10); // Kurz warten
digitalWrite(2, LOW); // LED ausschalten
Serial.println(timestampString);
}
can_frame canMsg;
if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK) {
digitalWrite(2, HIGH); // LED einschalten
delay(100); // Kurz warten
digitalWrite(2, LOW); // LED ausschalten
char msgString[128]; // Ein Array, um die Daten zu speichern
sprintf(msgString, "{\"ID\": \"0x%03X\", \"Len\": \"%1d\", \"Data\": \"", canMsg.can_id, canMsg.can_dlc);
for (int i = 0; i < canMsg.can_dlc; i++) {
sprintf(msgString + strlen(msgString), "%02X ", canMsg.data[i]);
}
sprintf(msgString + strlen(msgString) - 1, "\"}");
snprintf(aliveTopic, sizeof(aliveTopic), "%s/pload", mqtt_topic); // Kombiniere das Hauptthema mit "/alive"
client.publish(aliveTopic, msgString); // Sende die Daten zum MQTT-Broker
Serial.println(msgString);
}
// Kurze Verzögerung, um den CAN-Bus nicht zu überlasten
//Serial.print(".");
//delay(10);
ArduinoOTA.handle();
client.loop();
}
void connectMQTT() {
// Verbinde mit MQTT Broker
while (!client.connected()) {
client.setServer(mqtt_server, mqtt_port);
Serial.print("Verbinde mit MQTT...");
if (client.connect("CAN_MQTT_Client", mqtt_user, mqtt_password)) {
Serial.println("verbunden");
// Setze MQTT-Callback-Funktion
client.setCallback(callback);
// Erstelle das Topic für das Senden von CAN-Nachrichten
char canSendTopic[64];
snprintf(canSendTopic, sizeof(canSendTopic), "%s/canSend", mqtt_topic);
client.publish(canSendTopic, "INIT"); // Sende die Daten zum MQTT-Broker
delay(200);
Serial.print("Abonniere Topic: ");
Serial.println(canSendTopic);
if (client.subscribe(canSendTopic)) {
Serial.println("Abonnement erfolgreich!");
} else {
Serial.println("Abonnement fehlgeschlagen!");
}
delay(200);
} else {
Serial.print("Fehler, rc=");
Serial.print(client.state());
Serial.println(" Versuche es erneut in 5 Sekunden");
delay(5000);
}
}
};
Ich habe im Übrigen noch einen timer eingebaut, der nach 12 Stunden den ESP32 resettet.
War die Übergangslösung die aber keine war. Ich gehe davon aus, dass man dies abschalten kann.
Auf der anderen Seite ein Softreset alle 12 Stunden kann nicht schaden.
Was aber bleibt ist die Fehlermeldungen im Javascript (füllen halt das log, aber das Skript läuft prinzipiell). Ich bin mir sicher, dass es sich dabei um ein nicht definiertes Objekt handelt. Ich find aber leider nicht in welchem Bereich der Fehler ausgelöst wird. Da ich nun nicht wirklich der Javaskript Guru bin vielleicht kann sich das ein Experte mal ansehen.