Hi liebes Forum.
In letzter Zeit versuche ich Funksteckdosen mit meinem Arduino über Lan, MQTT und Openhab anzusteueren, was auch funktioniert. Lediglich habe ich ein Problem, was immer auftritt. Wenn der Arduino eingeschaltet wird verbindet er sich via Lan mit einem Raspberry Pi auf dem der Mosquitto Broker läuft, um Anwesiungen für die Funksteckdosen und aderes zu erhalten. Das Problem ist, das die ganze Sache nach mehreren Stunden nicht mehr funktioniert, d.h. der Arduino nimmt keine Anweisungen mehr eintegegen und man kann die Funksteckdosen nicht mehr ein und aus schalten. Was dabei auffällt ist, dass die gelbe LED am Arduino, die beim starten leuchtet, nicht mehr leuchtet.
Hier der erste Teil meines Codes:
#include <SPI.h>
#include <UIPEthernet.h>
#include "PubSubClient.h"
#include <RCSwitch.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// unkommemntieren um Debug-Modus zu aktivieren
//#define DEBUG
// --------------------------------
// Ethernet Server
// http://arduino.cc/de/Reference/Ethernet
// --------------------------------
EthernetClient ethClient;
// --------------------------------
// MQTT Client
// http://knolleary.net/arduino-client-for-mqtt/
// --------------------------------
byte MQTTserver[] = { 192, 168, 0, 1 };
PubSubClient MQTTClient(MQTTserver, 1883, callback, ethClient);
unsigned long time;
long previoustime = 0;
long MQTTTimer = 7200000; //2 Stunden
// --------------------------------
// LED Leiste
// --------------------------------
// Pins des RGB LED Lichts
int LEDredPIN=12;
int LEDgreenPIN=10;
int LEDbluePIN=11;
// Übermittelte Farbwerte
int LEDredValue = 0;
int LEDgreenValue = 0;
int LEDblueValue = 0;
// Gerade gesetzte Farbe
int LEDredCurrent = 0;
int LEDgreenCurrent = 0;
int LEDblueCurrent = 0;
// Timervariable für eine Verzögerung. Als alternative zu delay was die verarbeitung anhält.
int LEDtimer = 0;
int LEDendTimer = 50;
// --------------------------------
// RC-Switch
// https://code.google.com/p/rc-switch/
// --------------------------------
RCSwitch RCsender = RCSwitch();
String rcID = ""; // Übermittelte ID
String rcCommand = ""; // Übermittelter Befehl
char rcID1[6]; // ID erster Teil
char rcID2[6]; // ID zweiter Teil
// --------------------------------
// Temperatur
// http://milesburton.com/Main_Page?title=Dallas_Temperature_Control_Library
// http://www.pjrc.com/teensy/td_libs_OneWire.html
// --------------------------------
#define SENSOR_PIN 2 // Any pin 2 to 12 (not 13) and A0 to A5
OneWire temp(SENSOR_PIN);
unsigned long TEMPtimer;
unsigned long TEMPendTimer = 300000; // Schleifendurchläufe bis neue Abfrage startet
float tempC;
#define TEMPERATURE_PRECISION 9
DallasTemperature sensors(&temp);
DeviceAddress insideThermometer; // Variable zum zwischenspeichern der Adresse eines Sensors
//================================================================================================================
void setup()
//================================================================================================================
{
#ifdef DEBUG
Serial.begin(9600);
Serial.println("Basestation v1.0");
#endif
// --------------------------------
// LED Leiste
// --------------------------------
// Setzen der PINS als Ausgang
pinMode(LEDbluePIN, OUTPUT);
pinMode(LEDredPIN, OUTPUT);
pinMode(LEDgreenPIN, OUTPUT);
// Bei start Farbe Blau setzen
analogWrite(LEDredPIN, 0);
analogWrite(LEDgreenPIN, 0);
analogWrite(LEDbluePIN, 10);
// --------------------------------
// RC-Switch
// --------------------------------
RCsender.enableTransmit(40); //Arduino Pin #40
RCsender.setRepeatTransmit(15);
// --------------------------------
// Temperatur
// --------------------------------
sensors.begin();
sensors.getAddress(insideThermometer, 0);
sensors.setResolution(insideThermometer, 12);
byte addr[8];
temp.search(addr);
temp.reset_search();
// --------------------------------
// Ethernet Server
// --------------------------------
// Initialisierung des Ethernets
Ethernet.begin(addr);
if (Ethernet.begin(addr) == 0) {
// Wenn DHCP fehlschlägt dann rot setzen und aufhören
analogWrite(LEDredPIN, 100);
analogWrite(LEDgreenPIN, 0);
analogWrite(LEDbluePIN, 0);
while (true);
}
else {
// Wenn DHCP OK ist dann grün setzen
LEDgreenValue = 10;
#ifdef DEBUG
Serial.print("IP-Adresse:");
Serial.println(Ethernet.localIP());
#endif
}
}
Hier der zweite :
//================================================================================================================
void loop(){
//================================================================================================================
// --------------------------------
// MQTT Client
// --------------------------------
// Aufbau der Verbindung mit MQTT falls diese nicht offen ist.
time = millis();
if (!MQTTClient.connected()) {
MQTTClient.connect("Basestation");
// Abonieren von Nachrichten mit dem angegebenen Topics
// MQTTClient.subscribe("/haus/moritz/ledkugel/RED/#");
// MQTTClient.subscribe("/haus/moritz/ledkugel/GREEN/#");
// MQTTClient.subscribe("/haus/moritz/ledkugel/BLUE/#");
MQTTClient.subscribe("/haus/moritz/ledkugel/#");
// RC Steckdosen Commands
MQTTClient.subscribe("/haus/RC/#");
}
Ethernet.maintain();
/* if(time - previoustime > MQTTTimer){
previoustime = time;
MQTTClient.disconnect();
delay(100);
MQTTClient.connect("Basestation");
MQTTClient.subscribe("/haus/moritz/ledkugel/#");
MQTTClient.subscribe("/haus/RC/#");
} */
// --------------------------------
// LED Leiste
// --------------------------------
if (LEDtimer <= LEDendTimer) LEDtimer++;
else {
LEDtimer = 0;
if (LEDredValue < LEDredCurrent) LEDredCurrent--;
else if (LEDredValue > LEDredCurrent) LEDredCurrent++;
if (LEDgreenValue < LEDgreenCurrent) LEDgreenCurrent--;
else if (LEDgreenValue > LEDgreenCurrent) LEDgreenCurrent++;
if (LEDblueValue < LEDblueCurrent) LEDblueCurrent--;
else if (LEDblueValue > LEDblueCurrent) LEDblueCurrent++;
}
analogWrite(LEDredPIN, LEDredCurrent);
analogWrite(LEDgreenPIN, LEDgreenCurrent);
analogWrite(LEDbluePIN, LEDblueCurrent);
// --------------------------------
// RC Steckdosen
// --------------------------------
if (rcID.length() == 10) {
#ifdef DEBUGDEBUG
Serial.println("ID : " + rcID);
#endif
rcID.toCharArray(rcID1,6);
rcID = rcID.substring(5,10);
rcID.toCharArray(rcID2,6);
if (rcCommand == "ON")RCsender.switchOn(rcID1, rcID2);
if (rcCommand == "OFF")RCsender.switchOff(rcID1, rcID2);
rcID = "";
}
// if (rcID.length() == 10)
if (rcID.length() == 5) {
#ifdef DEBUG
Serial.println("ID : " + rcID);
#endif
rcID.toCharArray(rcID1,6);
rcID = rcID.substring(5,10);
rcID.toCharArray(rcID2,6);
if (rcCommand == "ON") RCsender.switchOn(rcID1, 1);
if (rcCommand == "OFF")RCsender.switchOff(rcID1, 1);
rcID = "";
}
// if (rcID.length() == 6)
// --------------------------------
// Temperatur
// --------------------------------
if (TEMPtimer <= TEMPendTimer) TEMPtimer++;
else {
TEMPtimer = 0;
sensors.requestTemperatures();
// Bei Änderungen zum vorherigen Wert Publizieren
if (tempC != sensors.getTempCByIndex(0) ) {
tempC = sensors.getTempC(insideThermometer);
char tempbuf[4];
dtostrf(tempC, 2, 2, tempbuf);
//client.connect("Arduino-Mega");
MQTTClient.publish("/haus/moritz/temperatur", tempbuf);
#ifdef DEBUG
Serial.println("Temp: " + tempbuf);
#endif
} // TEMPSensor
} // else TEMPtimer
// --------------------------------
// MQTT Client Loop
// --------------------------------
MQTTClient.loop(); // Schleife für MQTT
}
//================================================================================================================
// Callback Funktion von MQTT. Die Funktion wird aufgerufen
// wenn ein Wert empfangen wurde.
//================================================================================================================
void callback(char* topic, byte* payload, unsigned int length) {
TEMPtimer = 0; // Wenn eine Nachricht kommt wird der Timer
// für die Temperatur auf 0 gesetzt um den Ablauf nicht zu Verzögern
// Zähler
int i = 0;
// Hilfsvariablen für die Convertierung der Nachricht in ein String
char message_buff[100];
// Kopieren der Nachricht und erstellen eines Bytes mit abschließender \0
for(i=0; i<length; i++) {
message_buff[i] = payload[i];
}
message_buff[i] = '\0';
// Konvertierung der nachricht in ein String
String msgString = String(message_buff);
String topString = String(topic);
#ifdef Debug
Serial.println("Topic: " + topString);
Serial.println("Msg : " + msgString);
#endif
// --------------------------------
// RC Steckdose
// --------------------------------
// Wenn das Topic mit "/haus/RC/" anfängt dann ID und Message ermitteln und schalten
if (topString.startsWith("/haus/RC/")) {
rcID = topString.substring(topString.lastIndexOf('/') + 1);
rcCommand = msgString;
} // if (topString.startsWith("/openHAB/wzRC/"))
// --------------------------------
// LED Leiste
// --------------------------------
// Überprüfung des Topics und setzen der Farbe je nach übermittelten Topic
if (topString == "/haus/moritz/ledkugel/RED") LEDredValue = round(msgString.toInt() * 2.55);
if (topString == "/haus/moritz/ledkugel/GREEN") LEDgreenValue = round(msgString.toInt() * 2.55);
if (topString == "/haus/moritz/ledkugel/BLUE") LEDblueValue = round(msgString.toInt() * 2.55);
} // void callback()
Hat keiner eine Idee, was ich hier übersehe ?
Was sagt das LOG aus OpenHab? Kommen da Befehle an?
Könnte ein RAM Problem sein, deswegen wollt ich nicht mit dem MQTT anfangen und hab Modbus genommen.
Ja im Event log stehen die Events für die Temperatur und Funk drin.
Ich denke ich werde einfach im Arduino Code alle 4 Stunden den Code neu starten mit:
void (softReset){
asm volatile (" jmp 0");
}
Das funktioniert mehr oder weniger, ist aber kein vollständiger Reset! Das springt in den Reset-Vektor und startet das Programm von vorne, aber die Register werden dabei nicht auf 0 zurückgesetzt.
Ein anderer Weg ist einen Funktionszeiger aufzurufen der NULL ist. Das macht das gleiche.
Man kann auch einen I/O Pin auf den Reset Pin legen:
Einen richtigen Software-Reset löst man aus indem man den Watchdog Timer started und in eine Endlosschleife geht:
http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_softreset
http://www.xappsoftware.com/wordpress/2013/06/24/three-ways-to-reset-an-arduino-board-by-code/
Der soft Reset funktioniert sehr gut, da ich jetzt keine Probleme mehr habe, jedoch bleibt immer noch die Frage offen wodurch diese Probleme auftreten.
Würd mal frech behaupten, wegen try and error.
Was ich auf die Schnelle in deinem Code für die Loop gesehen habe:
message_buff[i] = '\0';
Das schreibt unter Umständen hinter dein alloziiertes Array weil nach der Schleife das i nochmal eins hochgezählt wird. Das könnte zu Abstürzen führen.
Edit Ich sehe gerade noch, dass du die Länge der Nachricht überhaupt nicht abfängst sondern einfach die Payload in den alloziierten Buffer von 100 Bytes kopierst. Auch wenn die Länge viel größer ist!! Damit zerschießt du dir dein ganzes Programm. Du musst das Kopieren der Payload auf 99 Bytes begrenzen.
Gruß
Jarny
PS:
Ausserdem vergleicht man Strings in C nicht mit dem == Operator sondern mit strcmp
Jarny:
Ausserdem vergleicht man Strings in C nicht mit dem == Operator sondern mit strcmp
C Strings ja. Aber er hat die Arduino String Klasse drin. Ist zwar nicht empfehlenswert, aber es da geht es mit ==
Wenn schon String Klasse, dann sollte man aber auf toCharArray() verzichten und statt dessen c_str() nehmen, wenn man nur lesend auf den String zugreifen muss:
http://www.cplusplus.com/reference/string/string/c_str/
Das wurde erst später hinzugefügt ist ist dummerweise nicht dokumentiert. Aber es ist unendlich besser, da man einfach einen Zeiger auf das interne Array bekommt (den man nicht verändern kann) und nicht extra ein Array anlegen muss
Jarny:
Was ich auf die Schnelle in deinem Code für die Loop gesehen habe:message_buff[i] = '\0';
Das schreibt unter Umständen hinter dein alloziiertes Array weil nach der Schleife das i nochmal eins hochgezählt wird. Das könnte zu Abstürzen führen.
Edit Ich sehe gerade noch, dass du die Länge der Nachricht überhaupt nicht abfängst sondern einfach die Payload in den alloziierten Buffer von 100 Bytes kopierst. Auch wenn die Länge viel größer ist!! Damit zerschießt du dir dein ganzes Programm. Du musst das Kopieren der Payload auf 99 Bytes begrenzen.
Gruß
JarnyPS:
Ausserdem vergleicht man Strings in C nicht mit dem == Operator sondern mit strcmp
Frage: Wie setze ich das im Code um ... habe es gerade probiert und sehe nur Fehler ... wahrscheinlich weil ich es falsch gemacht habe
Außerdem an Serenifly: Das mit dem c_str() verstehe ich auch nicht so ganz
Einfach length - 1 machen
for(i=0; i < length - 1; i++)
Dann wird das letzte Byte nicht beschrieben
Das mit dem c_str() verstehe ich auch nicht so ganz
Es steht doch dort:
Returns a pointer to an array that contains a null-terminated sequence of characters (i.e., a C-string) representing the current value of the string object.
Anstatt also extra ein Array anzulegen und den Inhalt des String Objekts mit toCharArray() da reinzukopieren, hast du direkt lesenden Zugriff auf das interne Array des Objekts.
Statt dem hier:
char rcID1[6];
rcID.toCharArray(rcID2,6);
if (rcCommand == "ON") RCsender.switchOn(rcID1, 1);
Kannst du also einfach das machen:
if (rcCommand == "ON") RCsender.switchOn(rcID.c_str(), 1);
c_str() ist die Standard C++ Funktion um aus einem String Objekt einen C-String zu machen. Die Arduino Macher haben aber gemeint sie müssen da wieder was besonderes machen und haben die Sache dabei für lesenden Zugriff unnötig kompliziert gemacht und vor allem so dass man extra Speicher braucht. Später hat dann auch jemand c_str() implementiert, aber in der Doku ist es leider nicht aufgeführt.
Das spielt bei 12 Bytes zwar keine so große Rolle (die String Klasse selbst ist da viel schlimmer), aber es ist trotzdem einfacher.
Serenifly:
Kannst du also einfach das machen:if (rcCommand == "ON") RCsender.switchOn(rcID.c_str(), 1);
c_str() ist die Standard C++ Funktion um aus einem String Objekt einen C-String zu machen. Die Arduino Macher haben aber gemeint sie müssen da wieder was besonderes machen und haben die Sache dabei für lesenden Zugriff unnötig kompliziert gemacht und vor allem so dass man extra Speicher braucht. Später hat dann auch jemand c_str() implementiert, aber in der Doku ist es leider nicht aufgeführt.
Das spielt bei 12 Bytes zwar keine so große Rolle (die String Klasse selbst ist da viel schlimmer), aber es ist trotzdem einfacher.
Wenn ich das so mache bekomme ich diese Fehlermeldung:
In file included from Basestation.ino:3:
PubSubClient.h:72: warning: '__progmem__' attribute ignored
Basestation.ino: In function 'void loop()':
Basestation.ino:179: warning: deprecated conversion from string constant to 'char*'
Basestation.ino:184: warning: deprecated conversion from string constant to 'char*'
Basestation.ino:187: warning: deprecated conversion from string constant to 'char*'
Basestation:247: error: call of overloaded 'switchOn(const char*, int)' is ambiguous
/Users/moritzwirger/Documents/Arduino/libraries/RCSwitch/RCSwitch.h:61: note: candidates are: void RCSwitch::switchOn(int, int) <near match>
/Users/moritzwirger/Documents/Arduino/libraries/RCSwitch/RCSwitch.h:63: note: void RCSwitch::switchOn(char*, int) <near match>
/Users/moritzwirger/Documents/Arduino/libraries/RCSwitch/RCSwitch.h:67: note: void RCSwitch::switchOn(char*, char*) <near match>
/Users/moritzwirger/Documents/Arduino/libraries/RCSwitch/RCSwitch.h:69: note: void RCSwitch::switchOn(char, int) <near match>
Basestation.ino:266: warning: deprecated conversion from string constant to 'char*'
Basestation.ino:280: warning: comparison between signed and unsigned integer expressions
Basestation.ino: In function 'void callback(char*, byte*, unsigned int)':
Basestation.ino:305: warning: comparison between signed and unsigned integer expressions
Aua. Das ist sehr doof. Dann kommt die Lib nicht damit zurecht dass das ein Zeiger auf const Daten ist. Das mit dem "ambigious overloaded functions" ist wahrscheinlich eher eine Folge-Fehler. Dass er da so viel meckert ist sehr verwirrend und erschwert die Fehlersuche. Liegt aber wahrscheinlich nur daran, dass c_str() eben einen const char* liefert (da man den String nicht verändern darf).
Ich war davon ausgegangen dass das gehen müsste, weil es z.B. mit Serial geht. Aber wenn man nachschaut, dann ist der Parameter der entsprechenden print() Funktion const.
Das würde aber auch nicht passieren wenn Programmierer (gerade bei Libraries) bei Zeigern mehr auf const-correctness achten würden. Anders herum (einen char* an eine Funktion übergeben die einen const char* nimmt), geht es nämlich.
Sorry für die Verwirrung
Wäre es denn möglich die Lib anzupassen ?
Serenifly:
Sorry für die Verwirrung
Nicht schlimm ... man will ja auch was lernen
Außerdem seitdem ich
for(i=0; i < length; i++)
zu
for(i=0; i < length - 1; i++)
geändert habe fehlt bei der ankommenden Nachricht ein Zeichen oder Buchstabe
ARG! Dann wieder weg. Geht so nicht, da sich length auf die Quelle bezieht. Boundary check dann vielleicht so:
for(i=0; i < length && i < sizeof(message_buff) - 1; i++)
Wäre es denn möglich die Lib anzupassen ?
Theoretisch ja. Die char* Parameter der Methoden auf const char* ändern. Das sein mehrere da die Methoden überladen sind und die Argumente an andere Methoden weitergereicht werden.
Aber besser und einfacher ist es man verzichtet auf die String Klasse und verwendet gleich C Strings. Dann muss man auch nichts wandeln
Serenifly:
Aber besser und einfacher ist es man verzichtet auf die String Klasse und verwendet gleich C Strings. Dann muss man auch nichts wandeln
Wäre das umsetzbar und wenn ja wie ?
PS: Danke für die super Hilfe Serenifly