PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Uhren-Testprogramm funktioniert nicht



fredyxx
13.09.2017, 17:26
Hallo,

ich mache zZ meine ersten Versuche damit, das Uhren-IC DS1307 zu testen und habe in dem folgenden Sketch einen Fehler, den ich nicht verstehe:

Ich möchte erreichen, dass die Zeit für die LOOP entweder von den vor dem SETUP definierten Werten Sekunden bis Jahr übernommen wird, oder von der Zeit, die das Uhren-IC aktuell hat.

Dazu gebe ich vom SM in den ersten 5 s nach Programmstart entweder ein "j" oder ein "n" ein, was in die Variable "Zeit_einlesen" übernommen wird.

Schreibe ich in die Zeile 47 "if (Zeit_einlesen == "j")" , was ich für richtig halte, dann wird der anschließende Programmteil nicht durchlaufen ("Zeit_einlesen 2 = ....." wird nicht ausgegeben), obwohl unmittelbar vorher "Zeit_einlesen 1 = j" ausgegeben wird.

Schreibe ich in die Zeile 47 "if (Zeit_einlesen = "j")" , was ich für falsch halte, dann wird der anschließende Programmteil durchlaufen ("Zeit_einlesen 2 = j" wird ausgegeben). Dabei ist es egal, ob ich ein j oder ein n schicke.

Wo ist da der Wurm drin?

vG

fredyxx




/*

Mit diesem Programm wird das Uhren - IC getestet

*/

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgramm.h"
#endif
#include <Wire.h>
#include <DS1307.h>


int Sekunden = 1; // Vorgaben für den Durchlauf von SETUP
int Minuten = 1;
int Stunden = 1;
int Tag_der_Woche = 1; // Mo = 1; Di = 2
int Tag = 1;
int Monat = 1;
int Jahr = 17;

String Zeit_einlesen;

void setup() {



Serial.begin(9600); // Serielle SS für Ausgabe
while (!Serial) {
// wait for serial port to connect. Needed for native USB port only
} // >>>> Ende while

delay(5000); // 5 Sekunden lang Zeit für die Eingabe j oder n

Serial.println ("");
Serial.println (" j = Zeit aus dem Programm uebernehmen; n = Zeit vom Uhren-IC");
Serial.println ("");


Zeit_einlesen = Serial.readString(); //

Serial.print("Zeit_einlesen 1 = ");
Serial.println(Zeit_einlesen);

if (Zeit_einlesen == "j") {

Serial.print("Zeit_einlesen 2 = ");
Serial.println(Zeit_einlesen);

RTC.stop();
RTC.set(DS1307_SEC, Sekunden); // Sekunden setzen
RTC.set(DS1307_MIN, Minuten); // Minuten setzen
RTC.set(DS1307_HR, Stunden); // Stunden setzen
RTC.set(DS1307_DOW, Tag_der_Woche); // Tag der Woch setzen
RTC.set(DS1307_DATE, Tag); // Datum Tag setzen
RTC.set(DS1307_MTH, Monat); // Datum Monat setzen
RTC.set(DS1307_YR, Jahr); // Datum Jahr setzen
RTC.start();

} // >>>> Ende if (Zeit_einlesen == "j")



} // >>>> Ende Setup


void loop() {
// Daten vom Uhrenbaustein lesen

Serial.print(RTC.get(DS1307_HR, true));
Serial.print(":");
Serial.print(RTC.get(DS1307_MIN, false));
Serial.print(":");
Serial.print(RTC.get(DS1307_SEC, false));
Serial.print(" ");
Serial.print(RTC.get(DS1307_DATE, false));
Serial.print("/");
Serial.print(RTC.get(DS1307_MTH, false));
Serial.print("/");
Serial.print(RTC.get(DS1307_YR, false));
Serial.println();

if ( RTC.get(DS1307_SEC, false) == 30) {

Serial.println("JA"); // gibt immer bei 30 Sekunden ein JA aus!!
}

delay(1000); // 1 Sekunde warten


}

Sisor
13.09.2017, 20:58
Hallo!

Mit "if (Zeit_einlesen = "j")" schreibst du den Wert "j" in Zeit_einlesen und fragst dann, ob Zeit_einlesen ungleich Null ist. Dies ist immer der Fall, da "j" ein (konstanter) String ist.

Das Problem ist, dass der eingelesene String zusätzliche Sonderzeichen enthalten kann. Wenn du in dem Arduino-Monitor einen Wert eingibst, schließt du die Eingabe mit einem Return ab. Dies ist dann Teil des Strings, der übermittelt wird.

Vorschlag:
Zeit_einlesen = Serial.readString().trim() (https://www.arduino.cc/en/Tutorial/StringLengthTrim);

fredyxx
13.09.2017, 22:50
Danke für die Antwort, aber damit erhalte ich die Fehlermeldung:
no match for 'operator=' (operand types are 'String' and 'void')

Nun dämmert es bei mir, dass ich das Problem schon mal hatte. Finde aber die Stelle noch nicht wo und wie ich das gelöst hatte.

vG

fredyxx

Sisor
13.09.2017, 22:59
Na gut, dann so:
Zeit_einlesen = Serial.readString();
Zeit_einlesen.trim();

Che Guevara
14.09.2017, 09:22
Hi,

Ich habe selber keinen arduino aber ich würde mal checken was deine eingabefunktion ausgibt. Du liest einen String ein, kann sein dass der mit CR und lf terminiert wird, dann müsstest du dieses später mit abfragen oder wegschneiden.
Nur so eine Idee! ;)

Gruß
Chris

fredyxx
14.09.2017, 09:38
Na gut, dann so:
Zeit_einlesen = Serial.readString();
Zeit_einlesen.trim();

Danke, so funtioniert es

vG
fredyxx

HaWe
14.09.2017, 11:09
Danke für die Antwort, aber damit erhalte ich die Fehlermeldung:
no match for 'operator=' (operand types are 'String' and 'void')

Nun dämmert es bei mir, dass ich das Problem schon mal hatte. Finde aber die Stelle noch nicht wo und wie ich das gelöst hatte.

vG

fredyxx

Die Arduino-C++-Klasse String (groß geschrieben) ist das größte Übel seit wir von den Bäumen herunter gekommen sind, wahrscheinlich sogar, seit wir die Ozeane verlassen haben.
Es hat nichts, aber auch rein gar nichts mehr zu tun mit Standard-C(++) Datentypen wie char* a, char a[] oder string a (klein geschrieben).

probier's mal mit Abfrage des 1. Zeichens in deinem "String":
(einfaches Hochkomma für Zeichen, keine Doppel-Anführungszeichen für Zeichenketten!)
if(Zeit_einlesen[0]=='j') .......


edit:
ah, hat sich zeitlich überschnitten!

fredyxx
14.09.2017, 11:09
[QUOTE=HaWe;639090]
probier's mal mit Abfrage des 1. Zeichens in deinem "String":
(einfaches Hochkomma für Zeichen, keine Doppel-Anführungszeichen für Zeichenketten!)
if(Zeit_einlesen[0]=='j') .......

Danke , das klappt auch!

Was bedeutet die [0] ??

vG
fredyxx

HaWe
14.09.2017, 12:23
[QUOTE=HaWe;639090]
probier's mal mit Abfrage des 1. Zeichens in deinem "String":
(einfaches Hochkomma für Zeichen, keine Doppel-Anführungszeichen für Zeichenketten!)
if(Zeit_einlesen[0]=='j') .......

Danke , das klappt auch!

Was bedeutet die [0] ??

vG
fredyxx

strings jeder Art sind grundsätzlich arrays, an jeder Stelle steht ein einzelner Buchstabe.
Buchstaben sind meist Bytes (unsigned char), bei Arduino seltsamerweise signed char (auf anderen Systemen oder als Steuerzeichen auch teilw. int, führt hier aber zu weit ).
Die Nummerierung startet in C mit der Null, daher ist array[0] die erste "Zelle", array[1] die 2., usw., bei einem string oder array der Länge n ist also array[n-1] der letzte Buchstabe, der direkt zum string gehört.
Es folgen dann bei C-strings noch das terminierende Byte 0 ( '\0'), und ggf voher noch das "Enter-Zeichen" (carriage-return, line-feed), wenn du nach deiner string-Eingabe am Schluss die Enter-Taste gedrückt hast, in C auf AVR und ARM und Linux ist das das '\n' (alles andere führt hier zu weit).
\0 ist der ASCII-Wert 0, nicht das Zahlsymbol 0,
\n ist der ASCII-Wert 10.

Für dich ist vor allem wichtig, dass wenn du nur 1 Zeichen per Serial eingibst und mit ENTER abschließt, dieses Zeichen an der 1. Array-Stelle gespeichert wird, also array[0], und dass aber noch weitere Steuerzeichen folgen können, die das System drangehängt hat.

(edited: )
Leider kann man sich aber (wie erwähnt) bei Arduino nie recht drauf verlassen, was das alles genau ist. Wenn du also ein 'j' eingibst + ENTER, kann dein String-array so aussehen
j \0
-oder-
j \n \0
-oder (passiert, wenn man lange genug wartet bevor man dann irgendwann ENTER drückt), auch nur:-
j
- IMMER aber steht das j an 1. Stelle, also in der Zelle array[0].


ANSI C- oder C++ -Funktionen entfernen oder verarbeiten die string-Steuerzeichen mit ihren recht gut dokumentierten Standard-Funktionen, Arduino verwendet dazu die seltsam-verqueren und nicht bis maximal hundsmiserabel dokumentierten String-Class Funktionen wie trim().

Sisor
14.09.2017, 12:57
Die Arduino-C++-Klasse String (groß geschrieben) ist das größte Übel seit wir von den Bäumen herunter gekommen sind, wahrscheinlich sogar, seit wir die Ozeane verlassen haben.
Es hat nichts, aber auch rein gar nichts mehr zu tun mit Standard-C(++) Datentypen wie char* a, char a[] oder string a (klein geschrieben).

Was bewegt dich zu solchen haarsträubenden Falschaussagen?
String-Objekte (https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/WString.h) sind in Arduino als Wrapper um einen Buffer vom Typ char* realisiert, um den Umgang mit Zeichenketten zu vereinfachen und wesentliche Funktionalität wie Vergleich, Modifikation, Verkettung etc. bereitzustellen.

HaWe
14.09.2017, 13:01
es steht nirgends in der Arduino-Dokumentation erklärt.
Wer das rauskriegen will, muss die Arduino-C++ String lib-sourcecode-Kauderwelschs durchpflügen, dass ist Arduino-Standard-Nutzern nicht zuzumuten, und insb. ist es absolut konfus, weil Arduino in C++ geschrieben ist (nicht in C), und in C++ die library <string> existiert mit ihrem Datentyp string, und nicht char* wie in ANSI-C <string.h> bzw. <cstring>.
string und char* sind aber nicht identisch und auch nicht kompatibel zu einander, und auch String ist nicht kompatibel zu char*.

ps,
versuche mal, die C-Funktionen wir strncpy, strstr oder strtok auf String anzuwenden!
Und dann wie vergleichsweise C++-Funktionen wie string::substring, string::copy, string::compare auf String anzuwenden sind.

Sisor
14.09.2017, 15:31
string und char* sind aber nicht identisch und auch nicht kompatibel zu einander

'string' ist in Arduino nicht definiert! Folgendes produziert den Fehler 'error: 'string' was not declared in this scope':

void setup() {
string s = "Kompiliert nicht!";
}
void loop() {}


... und auch String ist nicht kompatibel zu char*...
versuche mal, die C-Funktionen wir strncpy, strstr oder strtok auf String anzuwenden!
Folgender Code zeigt die 'Kompatibilität' von String und C-Funktionen, die mit char* arbeiten:

void setup() {
String s1 = "Hallo Welt!";

Serial.begin(9600);
Serial.println(s1);

String s2 = "Ha";
s2 += "We";
strncpy(s1.c_str()+s1.indexOf('W'), s2.c_str(), s2.length() );
Serial.println(s1);
}

void loop() {} [Ausgabe: Hallo Welt! Hallo HaWe!]

PS: Sorry fredyxx fürs Thread-Hijacken.

HaWe
14.09.2017, 16:16
'string' ist in Arduino nicht definiert! Folgendes produziert den Fehler 'error: 'string' was not declared in this scope':

void setup() {
string s = "Kompiliert nicht!";
}
void loop() {}

Folgender Code zeigt die 'Kompatibilität' von String und C-Funktionen, die mit char* arbeiten:

void setup() {
String s1 = "Hallo Welt!";

Serial.begin(9600);
Serial.println(s1);

String s2 = "Ha";
s2 += "We";
strncpy(s1.c_str()+s1.indexOf('W'), s2.c_str(), s2.length() );
Serial.println(s1);
}

void loop() {} [Ausgabe: Hallo Welt! Hallo HaWe!]

PS: Sorry fredyxx fürs Thread-Hijacken.

das güldet nicht! ;)
du verwendest eine (non-Standard-C) Arduino-String-Class-eigene interne Umwandlungsmethode String::c_str(), um erst Strings in char* umzuformen, und erst dann wendest du cstring- (string.h) Funktionen darauf an!

Wirklich kompatibel zu ANSI-C char* wäre es nur, wenn du Strings an sich mit cstring-Funktionen verwenden könntest, also z.B.

String str1= "To be or not to be";
String str2;
String str3;

/* copy to sized buffer (overflow safe): */
strncpy ( str2, str1, sizeof(str2) );

/* partial copy (only 5 chars): */
strncpy ( str3, str2, 5 );
str3[5] = '\0'; /* null character manually added */

puts (str1);
puts (str2);
puts (str3);


Output WÄRE (!):

To be or not to be
To be or not to be
To be

(aus cplusplus.com, verändert)


Viel SINNVOLLER wäre es allerdings gewesen, C++ string als Grundlage zu nehmen,
alle string-class-Methoden auf String zu vererben,
und sie dann durch die Arduino-eigenen String-Methoden zu erweitern.

DAS wäre C++ kompatibel (da man alle C++ string Methoden weiterhin auch in String zur Verfügung HÄTTE) und durch zusätzliche Methoden Arduino-Anfänger-geeignet.

Sisor
14.09.2017, 16:55
du verwendest eine (non-Standard-C) Arduino-String-Class-eigene interne Umwandlungsmethode String::c_str(), um erst Strings in char* umzuformen, und erst dann wendest du cstring- (string.h) Funktionen darauf an!
Schwachsinn! String ist ein Wrapper für einen char* Buffer in Objektform. c_str() ist eine Gettermethode, um genau diesen Buffer zu bekommen. Es findet keine Umformung / Umwandlung statt.

HaWe
14.09.2017, 17:12
Schwachsinn! wenn was auch immer immer benötigt wird, um String zu char* und string.h Funktionen kompatibel zu machen, dann ist es eben nicht a priori kompatibel!

Mxt
14.09.2017, 17:21
Wirklich kompatibel zu ANSI-C char* wäre es nur, wenn du Strings an sich mit cstring-Funktionen verwenden könntest, also z.B.


Das wäre einfach zu erreichen (durch eine zusätzliche Memberfunktion in der Stringklasse). Aber da muss man die Arduinoentwickler in Schutz nehmen, dass sie den Zugriff mit ".c_str()" machen, genau wie std::string auch. Microsoft hat das in den 1990ern in seiner CString Klasse gemacht.
https://msdn.microsoft.com/en-us/library/aa300569(v=vs.60).aspx
Das produziert mehr Probleme als es löst, deswegen macht man das heute nicht mehr so.

Zur Kritik an der Arduino String Klasse
https://forum.arduino.cc/index.php?topic=364772.0

When Arduino 1.0 was released, the "String" class was faulty due to compiler/library problems and could easily create wrong results and programs "hanging".

This problem was fixed at some point, I think with release of IDE 1.0.5.

So at least the "String" class is working "error free" now, if that was your question.

But of course "String" is BAD BAD BAD, because of:
- using more RAM than actually needed to solve any problem
- encourages programmers to write programs that eat up more RAM than is actually available
- slowing down program execution which might be bad if every microsecond is counting in your application
- fragments RAM, so that after some time of running an application the RAM could be fragmented too much, so that no "String" of certain length can be allocated any longer.


In C++ ist std::string nur ein typedef für std::basic_string<char>, wo schon der Name andeutet, dass da char drinstecken...
http://en.cppreference.com/w/cpp/string/basic_string

HaWe
14.09.2017, 17:28
dass da überall irgendwo char* (edit: Pointer auf nullterminierte strings) drin stecken, ist doch sonnenklar!
Das Problem fängt dann an, wenn man - wie in C oder C++ üblich - direkt cstring/string.h oder string class Funktionen auf diese verqueren Arduino-String Chimären anwenden will!
Man hätte sich fürs eine oder andere eindeutig entscheiden müssen, und es spricht ja nichts dagegen, vorhandene string.h oder string lib Funktionen durch eigene libs zu erweitern - aber was man jetzt mit String hat, ist ein Humbug ohne Ende, weder Fisch noch Fleisch, und Arduino-User gewöhnen sich Syntaxabnormitäten an, mit denen sie auf jeder anderen C/++ Plattform komplett scheitern!
DAS ist der wirkliche Schwachsinn!

Mxt
14.09.2017, 17:37
Ja, ich halte die Arduino String Klasse, auch im Kontext dieses Threads, für keine gute Wahl und würde dem Fragesteller auch davon abraten.

Was echte C++ Strings angeht hängt die Verwendbarkeit vom Bord ab. Das hier


#include <string>

void setup() {
std::string s = "Hallo";
}

void loop() {
}

kompiliert bei mir ohne Probleme


Der Sketch verwendet 8112 Bytes (3%) des Programmspeicherplatzes. Das Maximum sind 262144 Bytes.
Globale Variablen verwenden 3436 Bytes (5%) des dynamischen Speichers, 62100 Bytes für lokale Variablen verbleiben. Das Maximum sind 65536 Bytes.

Arduino 1.8.4 mit Teensyduino 1.39, Boardeinstellung Teensy 3.2 72 MHz Faster

HaWe
14.09.2017, 17:41
Leider ist der Datentyp, den Serial.getString() zurückliefert, aber Arduino-String, nicht char* und auch nicht std::string. Leider.

Bezüglich der Frage des OP, was die [0] zu bedeuten hat, möchte ich jetzt gerne hier abschließen und auf meine Erklärung verweisen, damit er sie wiederfindet:

https://www.roboternetz.de/community/threads/71068-Uhren-Testprogramm-funktioniert-nicht?p=639093&viewfull=1#post639093