Forum: Mikrocontroller und Digitale Elektronik sketch stört webserver - wer kann helfen?


von Hardy N. (hardy_n)


Angehängte Dateien:

Lesenswert?

Hallo an alle,

ich experimentiere seid Tagen an einer Lösung und finde den Fehler 
nicht.

Habe auf einem ESP32 mit Display (WIFI Kit 32 ESP32 WIFI drahtlos mit 
0,96 Zoll OLED Display CP2102 Entwicklungsboard für Arduino Nodemcu) 
eine Stoppuhr am laufen. Das fktn einwandfrei allein.

Nun wollte ich einen Webserver mit dem man versch. Pins steuern kann 
drauf packen. Das fktn aber NUR wenn ich den loop der Stoppuhr 
auskommentiere.

Sind beide loops aktiv fktn nur die Stoppuhr auf dem Display und auch 
seriel, der Webserver ist dann nicht erreichbar.

Könnte sich jemand bitte mal den Code anschauen und evtl. Ursachen 
feststellen?

Hinweis: Der Webserver ist nur ein Zwischenschritt. Die fertige Lösung 
soll so fktn dass 3 ESP32 an denen Lichtschranken fungieren - Pins am 
Stoppuhren-ESP32 steuern und somit die Zeitmessung starten/stoppen.

Falls jemand eine noch schlankere Lösung weiß, bitte vorschlagen. 
Zunächst wüsste ich eben nur gern, warum die jetzige Konstellation nicht 
fktn.

VG Hardy

: Bearbeitet durch User
von g457 (Gast)


Lesenswert?

> [..] fktn [..] versch. [..] fktn [..] fktn [..] Könnte sich jemand bitte
> mal den Code anschauen und evtl. Ursachen feststellen? [..]
> fktn [..] fktn.

Klar, dzu mst d in br e hdn.

von Hardy N. (hardy_n)


Lesenswert?

g457 schrieb:
> Klar, dzu mst d in br e hdn.

Was willst Du mir damit sagen?

von Belgacom (Gast)


Lesenswert?

Hardy N. schrieb:
> g457 schrieb:
>> Klar, dzu mst d in br e hdn.
>
> Was willst Du mir damit sagen?

Vermutlich, dass sich dein Text ziemlich blöd liest.

von Hardy N. (hardy_n)


Lesenswert?

Belgacom schrieb:
> Hardy N. schrieb:
>> g457 schrieb:
>>> Klar, dzu mst d in br e hdn.
>>
>> Was willst Du mir damit sagen?
>
> Vermutlich, dass sich dein Text ziemlich blöd liest.

..weil ich funktioniert abgekürzt habe???

Du bist aber seltsam drauf.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

... du must delay unblocked implementieren!


mt

beispiel:

//usage:
// WAITBLOCK(MSEC,T2B,100) {digitalWrite(LED_PIN, 
digitalRead(LED_PIN)^1); //...} //fast flashed led

//options
#define USEC micros() //timer tick base
#define MSEC millis()
#define T2B uint16_t  //timer data type
#define T4B uint32_t

//create an unique name from given parameter and line number
#define UNIQUE(name) (name ## _LINE_)

//creates an unique timer for each instance
#define WAITBLOCK(tb, dt, t)                    \
  static dt UNIQUE(tmr) = (dt) tb;              \
  if (tb - UNIQUE(tmr) >= t)

: Bearbeitet durch User
von Joachim B. (jar)


Lesenswert?

Hardy N. schrieb:
> ..weil ich funktioniert abgekürzt habe???

ja

aber nun gut
delay(1000);

wenn in einer Funktion sinnlose Wartezeit vertrödelt wird, wie kommst du 
darauf das andere Funktionen noch funktionieren?

statt sinnlos delay(1000); zu vertrödeln könnte ja den anderen Aufgaben 
nachgegangenen werden, z.B. auf dem Webserver.

Also besser programmieren lernen.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Dein Problem ist ein strukturelles:
1
while (client.connected()) {  
2
  ...
3
}
4
5
while (digitalRead(0) == HIGH) {
6
    ...
7
}

Diese beiden Stellen blockieren den Programmablauf. Das Arduino 
Framework unterstützt die Entwicklung von Multi-Taskin mit der Methode 
der Endlichen Automaten (Zustandautomaten).

Das Prinzip wird hier anhand eines fiktiven C Programm erklärt: 
http://stefanfrings.de/net_io/protosockets.html (für dich ist nur der 
Absatz "Zustandsautomat" relevant).

Ein konkretes Beispiel findest du in dem Buch 
http://stefanfrings.de/mikrocontroller_buch/Einstieg%20in%20die%20Elektronik%20mit%20Mikrocontrollern%20-%20Band%203.pdf 
Kapitel 9.

Dann ist mir noch aufgefallen, dass du viele relativ grosse 
Zeichenketten im RAM hast. Benutze PROGMEM, PSTR() und F() damit die 
Strings nur im Flash Speicher liegen, nicht im RAM. 
https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html

von Hardy N. (hardy_n)


Lesenswert?

Ok, hab alle Hinweise gelesen. Vielen Dank dafür erstmal.

Ich bin wirklich ein kopletter Anfänger und war froh es bis hier 
geschafft zu haben. :-)

Ich gucke erstmal, ob ich es mit Eurer Hilfe hinbekomme.

Melde mich dann.

VG

: Bearbeitet durch User
von Hardy N. (hardy_n)


Lesenswert?

Joachim B. schrieb:
> aber nun gut
> delay(1000);
>
> wenn in einer Funktion sinnlose Wartezeit vertrödelt wird, wie kommst du
> darauf das andere Funktionen noch funktionieren?
>
> statt sinnlos delay(1000); zu vertrödeln könnte ja den anderen Aufgaben
> nachgegangenen werden, z.B. auf dem Webserver.

hab delay(1000); auskommentiert. daran liegt es nicht.

danke aber für den Hinweis.

von Hardy N. (hardy_n)


Lesenswert?

Apollo M. schrieb:
> ... du must delay unblocked implementieren!
>
> mt
>
> beispiel:
>
> //usage:
> // WAITBLOCK(MSEC,T2B,100) {digitalWrite(LED_PIN,
> digitalRead(LED_PIN)^1); //...} //fast flashed led
>
> //options
> #define USEC micros() //timer tick base
> #define MSEC millis()
> #define T2B uint16_t  //timer data type
> #define T4B uint32_t
>
> //create an unique name from given parameter and line number
> #define UNIQUE(name) (name ## LINE)
>
> //creates an unique timer for each instance
> #define WAITBLOCK(tb, dt, t)                    \
>   static dt UNIQUE(tmr) = (dt) tb;              \
>   if (tb - UNIQUE(tmr) >= t)

da stehe ich komplett auf dem Schlauch...

Dafür reicht mein Verständnis noch nicht aus. Da muss ich erstmal 
lesen...

Vielen Dank für den Hinweis.

von Hardy N. (hardy_n)


Lesenswert?

Stefanus F. schrieb:
> Dein Problem ist ein strukturelles:while (client.connected()) {
>   ...
> }
>
> while (digitalRead(0) == HIGH) {
>     ...
> }

Vielen Dank auch Dir. Ich lese mich da mal ein und teste dann mal 
etwas...

Eine Frage, Du hast bestimmt auch die anderen Hinweise gelesen:

Bevor ich mich nun um die Themen kümmere, was sagst Du zum Hinweis von:

Apollo M. schrieb:
> ... du must delay unblocked implementieren!

Weil, wenn ich erstmal grob weiß, woran es liegt, bin ich auch gern 
bereit mir die nacht um die Ohren zu schlagen. Will eben, wenns geht, 
aber gleich das "richtige" Thema angehen.

...kann natürlich aucch sein, dass beides Einfluss auch den/die Fehler 
hat.

VG Hardy

von Belgacom (Gast)


Lesenswert?

Hardy N. schrieb:
> Belgacom schrieb:
>> Hardy N. schrieb:
>>> g457 schrieb:
>>>> Klar, dzu mst d in br e hdn.
>>>
>>> Was willst Du mir damit sagen?
>>
>> Vermutlich, dass sich dein Text ziemlich blöd liest.
>
> ..weil ich funktioniert abgekürzt habe???
>
> Du bist aber seltsam drauf.

Ich nicht, sondern Du. Du hast den Text geschrieben.

von Hardy N. (hardy_n)


Lesenswert?

Belgacom schrieb:
> Hardy N. schrieb:
>> Belgacom schrieb:
>>> Hardy N. schrieb:
>>>> g457 schrieb:
>>>>> Klar, dzu mst d in br e hdn.
>>>>
>>>> Was willst Du mir damit sagen?
>>>
>>> Vermutlich, dass sich dein Text ziemlich blöd liest.
>>
>> ..weil ich funktioniert abgekürzt habe???
>>
>> Du bist aber seltsam drauf.
>
> Ich nicht, sondern Du. Du hast den Text geschrieben.

Ich verstehe nur eins nicht: Warum meldest Du Dich überhaupt, wenn Du 
mit meinem Text oder Abkürzungen Probleme hast? Brauchst Dich doch gar 
nicht melden und meinen Beitrag ignorieren! Das kostet uns beiden doch 
nur unnötig Zeit.
Im Übrigen haben andere auf meinen Thread trotz der Abkürzungen 
geantwortet.

Ich wünsche Dir ein schönes WE! (WE- Wochenende)

von Stefan F. (Gast)


Lesenswert?

Hardy N. schrieb:
> da stehe ich komplett auf dem Schlauch...

Kein Wunder, für meinen Geschmack ist das zu viel Makro-Magie. Der Adam 
Dunkels hatte etwas ähnliches mit seinen Protothreads veranstaltet, die 
gefallen mir auch nicht.

Ich gebe Dir mal ein entzaubertes Beispiel:
1
#define LED_ROT 2
2
#define LED_GELB 3
3
#define LED_GRUEN 4
4
5
void setup() 
6
{
7
  pinMode(LED_ROT,OUTPUT);
8
  pinMode(LED_GELB,OUTPUT);
9
  pinMode(LED_GRUEN,OUTPUT);
10
}
11
12
void thread_rot()
13
{
14
  static enum {AUS, WARTE_AUS, EIN, WARTE_EIN} state=AUS;
15
  static unsigned long warteSeit;
16
  switch (state)
17
  {
18
    case AUS:
19
      digitalWrite(LED_ROT,LOW);
20
      warteSeit=millis();
21
      state=WARTE_AUS;
22
      break;
23
24
    case WARTE_AUS:
25
      if (warteSeit-millis() >= 100) // wenn 100ms verstrichen sind
26
      {
27
        state=EIN;
28
      }
29
      break;
30
31
    case EIN:
32
      digitalWrite(LED_ROT,HIGH);
33
      warteSeit=millis();
34
      state=WARTE_EIN;
35
      break;
36
37
    case WARTE_EIN:
38
      if (warteSeit-millis() >= 100) // wenn 100ms verstrichen sind
39
      {
40
        state=AUS;
41
      }
42
      break;
43
  }
44
}
45
46
void thread_gelb()
47
{
48
  // genau der gleiche Code wie für die rote LED, aber mit 250ms.
49
}
50
51
void thread_gruen()
52
{
53
  // genau der gleiche Code wie für die rote LED, aber mit 400ms.
54
}
55
56
void loop() 
57
{
58
  thread_rot();
59
  thread_gelb();
60
  thread_gruen();
61
}

Alle drei Threads blockieren den Programmablauf nicht. Sie hängen nicht 
in Warteschleifen fest, sondern beenden sich bei jedem Aufruf direkt 
wieder.

Das Schlüsselwort "static" vor den lokalen Variablen sorgt dafür, dass 
sie ihren Wert zwischen mehreren Funktionsaufrufen nicht vergessen.

Beim ersten Aufruf ist der Thread im Status "AUS". In diesem Schritt 
wird die LED aus geschaltet und dann die aktuelle Zeit in der Variable 
warteSeit gespeichert. Dann wird zum nächsten Status "WARTE_AUS" weiter 
geschaltet.

Beim zweiten Aufruf ist der Thread im Status "WARTE_AUS". In diesem 
Schritt wird geprüft, ob die gewünschte Wartezeit erreicht (oder gar 
überschritten) wurde. Wenn das der Fall ist, wird auf den nächsten 
Status "EIN" weiter geschaltet.

Beim dritten Aufruf wurde die gewünschte Zeit noch nicht erreicht, daher 
bleibt der Thread im Status "WARTE_AUS".

Beim vierten Aufruf wurde die gewünschte Zeit noch nicht erreicht, daher 
bleibt der Thread im Status "WARTE_AUS".

Beim fünften Aufruf wurde die gewünschte Zeit noch nicht erreicht, daher 
bleibt der Thread im Status "WARTE_AUS".
...

Irgendwann ist es dann so weit, der Status wechselt nach "EIN". Beim 
darauf folgenden Aufruf wird die LED eingeschaltet. Danach wird erneut 
gewartet.

Der entscheidende Trick ist, dass die Thread-Funktion niemals hängen 
bleibt. Jeder einzelne Aufruf dauert nur wenige Mikrosekunden. In der 
Hauptschleife loop() kannst du daher sehr viele solcher Threads 
aufrufen. Es sieht von aussen betrachtet so aus, als ob sie gleichzeitig 
laufen würden.

Bei der Abfrage der verstrichenen Zeit ist es wichtig, dass die Variable 
(warteSeit) den gleichen Typ hat, wie der Rückgabewert von millis() und 
dass man die Differenz mit einer Subtraktion bildet. Diese Methode 
funktioniert sogar korrekt, wenn der Zeit-Zähler von seinem Maximal-Wert 
auf 0 über läuft. Bei allen anderen Methoden, die auf Addition beruhen, 
klappt das nicht.

von Hardy N. (hardy_n)


Lesenswert?

Stefanus F. schrieb:
> Hardy N. schrieb:
>> da stehe ich komplett auf dem Schlauch...
>
> Kein Wunder, für meinen Geschmack ist das zu viel Makro-Magie. Der Adam
> Dunkels hatte etwas ähnliches mit seinen Protothreads veranstaltet, die
> gefallen mir auch nicht.
>
> Ich gebe Dir mal ein entzaubertes Beispiel:

Ok. Ich versuche mal Dein Beispiel auf meinem Sketch anzuwenden. Wird 
allerdings sicher etwas dauern...:-)

Danke für Deine Mühe!

von Stefan F. (Gast)


Lesenswert?

Hardy N. schrieb:
> Wird allerdings sicher etwas dauern.

Ja, es erfordert eine erhebliche Änderung deiner Programmstruktur. Aber 
wenn du den Dreh einmal raus hast, kannst du es beliebig erweitern.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

... noch zwei macros mehr, für die werte leserschaft und jene die noch 
was dazu lernen wollen oder mal grübeln wollen was alles in c so geht!

der vorteil dieser macros ist vorallem, dass eine beliebige anzahl von 
"instancen" erzeugt werden können, sprich jedes macro erzeugt einen 
neuen unabhängigen timer und als beispiel zur anwendung von "k-logic" in 
c.


mt

WAITBLOCK = nonblocking wait
PERIODIC = nonblocking periodic instruction block
XPERIODIC = nonblocking x-time repeating instruction block

// WAITBLOCK(MSEC,T2B,100) {digitalWrite(LED_PIN, 
digitalRead(LED_PIN)^1); //...} //fast flashed led
// PERIODIC(MSEC,T2B,500) {digitalWrite(LED_PIN, 
digitalRead(LED_PIN)^1); //...} //blinky led
// XPERIODIC(MSEC,T2B,500,42) {digitalWrite(LED_PIN, 
digitalRead(LED_PIN)^1); //...} //42-times blinky led

//options
#define USEC micros() //timer tick base
#define MSEC millis()
#define T2B uint16_t  //timer data type
#define T4B uint32_t

//create an unique name from given parameter and line number
#define UNIQUE(name) (name ## _LINE_)

//creates an unique timer for each instance
#define WAITBLOCK(tb, dt, t)                    \
  static dt UNIQUE(tmr) = (dt) tb;              \
  if (tb - UNIQUE(tmr) >= t)

//creates an unique timer for each instance
#define PERIODIC(tb, dt, t)                     \
  static dt UNIQUE(tmr) = (dt) tb;              \
  if ((tb - UNIQUE(tmr) >= t)                   \
  && (UNIQUE(tmr) = tb, 1)) //(UNIQUE(tmr) += t, 1))

//creates an unique timer for each instance, x: repeats 1-255
#define XPERIODIC(tb, dt, t, x)                 \
  static dt UNIQUE(tmr) = (dt) tb;              \
  static uint8_t UNIQUE(xRepeats) = x;          \
  if ((tb - UNIQUE(tmr) >= t)                   \
  && UNIQUE(xRepeats)                           \
  && (UNIQUE(xRepeats)--, UNIQUE(tmr) = tb, 1)) //UNIQUE(tmr) += t, 1))

von Stefan F. (Gast)


Lesenswert?

Apollo M. schrieb:
> noch zwei macros mehr

Also ich habe mal gelernt, dass Quelltext für andere Entwickler gut 
lesbar sein soll. Das ist deiner ganz sicher nicht. Damit setzt du Dir 
selbst ein unrühmliches Denkmal. Deine Nachfolger werden diesen Code 
hassen.

Also wenn ich den Job wechsle, möchte ich nicht, dass mein alter Chef 
dem neuen erzählt "Wir sind froh, dass der Stefanus weg ist, denn was 
der fabriziert hat ist langfristig betrachtet unwartbarer Code - ein 
geschäftliches Risiko.".

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Stefanus F. schrieb:
> Das Prinzip wird hier anhand eines fiktiven C Programm erklärt:
> http://stefanfrings.de/net_io/protosockets.html (für dich ist nur der
> Absatz "Zustandsautomat" relevant).

ich kann zu protothreads noch das darauf basierende cocoOS rtos 
empfehlen, very small footprint, simple, und läuft bei mir auf pic16/18, 
avr, stm, arm oder auch auf jeden host.

http://www.cocoos.net

der stefan hat unter dem link mit viel mühe alles schön erklärt!


mt

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Stefanus F. schrieb:
> Deine Nachfolger werden diesen Code
> hassen.

... kommt auf das niveau an, dass ist schlicht nur eine liga höher.
die macros stehen in einem header und der aufruf ist simple kurz und 
klar!


hast du schon mal tiefer in die gcc lib macros geschaut? da sind diese 
hier nur basic.


mt

von Stefan F. (Gast)


Lesenswert?

Apollo M. schrieb:
> der stefan hat unter dem link mit viel mühe alles schön erklärt!

Danke, aber ich mag die Protothreads trotzdem nicht. Da passiert mir zu 
viel unsichtbares im Hintergrund (Makro-Magie).

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Stefanus F. schrieb:
> Danke, aber ich mag die Protothreads trotzdem nicht. Da passiert mir zu
> viel unsichtbares im Hintergrund (Makro-Magie).

ich habe auch länger gebraucht bis ich mit protothreads warm geworden 
bin :-)
aber hilfe! die sind überall in anwendung z.b. im linux kernel oder 
micro python moduls.
halt immer dann, wenn es um nonblocking protocols and small overhead 
geht.

ich bin gerade richtig begeistert von cocoos, give it a trail!


mt

: Bearbeitet durch User
von Mike R. (thesealion)


Lesenswert?

Stefanus F. schrieb:
> Dann ist mir noch aufgefallen, dass du viele relativ grosse
> Zeichenketten im RAM hast. Benutze PROGMEM, PSTR() und F() damit die
> Strings nur im Flash Speicher liegen, nicht im RAM.
> https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html

Das sollte beim ESP32 doch eigentlich egal sein, da dieser anders 
aufgebaut ist als z.B. AVR Controller. Wenn ich mich richtig erinnere 
werden die von dir genannten Marcos durch das Framework einfach 
"elemeniert".

Ansonsten kann man beim ESP32 auch ganz einfach mit echten FreeRTOS 
Threads arbeiten und so mehrere Dinge wirklich parallel machen. Dafür 
hat man dann andere "Problem" um die man sich kümmern muss.

von Stefan F. (Gast)


Lesenswert?

Mike R. schrieb:
> Wenn ich mich richtig erinnere werden die von dir genannten
> Makros durch das Framework einfach "elemeniert".

Ja, das scheint zu stimmen - wusste ich noch nicht. Beim ESP8266 sind 
diese Makros keine Dummies.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.