Fernbedienung ohne Funktion

Hallo zusammen,

ich habe hier einen Sketch für ein Modelleisenbahn Gebäude.
In einer festgelegten Reihenfolge gehen in den einzelnen Räumen die Lichter an/aus.
Auch eine TV-Simulation über eine RGB-LED ist vorhanden.
Mit Hilfe einer IR-Fernbedienung lässt sich zudem eine Sounddatei mit Hintergrundgeräuschen ein- und ausschalten, die Lautstärke verändern und den Sound an den Anfang zurückspulen (DFPlayer Mini MP3 Player).
Alles läuft soweit fehlerfrei.
Allerdings mit einer Ausnahme. In der Zeit, wo die TV-Simulation läuft, lässt sich die Fernbedienung nicht nutzen. Keine Taste funktioniert.
Sobald die TV-Simulation beendet ist (der Fernseher also ausgeschaltet wurde :slight_smile: ), funktioniert die Fernbedienung wieder einwandfrei.

Habt ihr eine Idee, woran das liegen könnte?

Vielen Dank schon mal für eure Unterstützung!

Gruß

Andreas

Bahnhof_25_mit_MP3-Player_mit_Fernbedienung.ino (73.3 KB)

Deine for-Schleife blockiert den Prozessor und das delay darin auch. Das musst Du aufbrechen.
Du hast doch loop als Schleife. Mache das mit millis().

Gruß Tommy

Du kannst für deine TV-Simulation auch einen extra Controller (ATtiny85) verwenden.
Der werkelt dann ohne die anderen Funktionen zu stören.

wie auch Tommy:

for (unsigned int i = 0; i < elements; i++) {

elements ist gut 900.

for (unsigned int j = count; j > 0; j–) {

j ist bis 198 groß und du wartest dann 198 m delay(20)

dein Code blockiert also und du kannst in der Zeit nichts anderes tun.

Lerne das Beispiel “Blink Without Delay” verstehen und baue um, sodass du ohne delay auskommst.

P.S.: ich bezweifle dass man für das Geflackere von einem TV 900 vorgegebene Muster braucht. Das kannst sicher auch mit adhoc ermittelten random-Zahlen auch realisieren.

Ich würde es so machen (ungetestet):

int ledR = 3;                  // LED Ausgang bestimmen
int ledG = 5;
int ledB = 9;
uint32_t lastMillis;
uint32_t delVal = 0;                // variable LED Wartezeit

void setup() {
  randomSeed(0);                // Zufallszahlengenerator
  pinMode(ledPin1, OUTPUT);     // LED als Ausgang setzen
  pinMode(ledPin2, OUTPUT);  
  pinMode(ledPin3, OUTPUT);
  pinMode(ledPin4, OUTPUT);
}

void loop() {
int val = 0;
uint32_t aktMillis = millis();                     // variable LED Helligkeit
  if (aktMillis - lastMillis >= delVal
  val = random(125,255);       // Zufallszahl zwischen 125 und 255. 0 = LED aus, 255 = LED maximale Helligkeit.
  analogWrite(ledR, val);    // Setzt das PWM Signal auf die Zufallszahl 
   val = random(125,255);       
  analogWrite(ledG, val);    
   val = random(90,255);       
  analogWrite(ledB, val);    
  
  delVal = random(200,500);   // Zufallszahl zwischen 200 und 500. 100 = schnell, 800 = langsam.
}
[code]

Die random müsste man evtl. noch etwas anpassen.

Gruß Tommy

und der nächste Schritt wäre die ganzen Leuchten als Objekte definieren,
für den Fernseher eigene Klasse machen
und damit den Sketch stark vereinfachen

Hallo zusammen,

vielen, vielen Dank für eure Rückmeldungen! Top!

  • Die for-Schleife mit dem delay ist das Problem. OK, verstanden.

  • extra Controller (ATtiny85): Ich versuche es erstmal ohne :slight_smile:

  • @Tommy56: Danke für deinen Beispiel-Sketch. Obwohl ich mit den Zeiten und Random-Werten rum gespielt habe, flackert es mir doch arg stark und zu schnell. Sieht nicht wirklich aus wie eine TV-Simulation. Ich möchte doch lieber bei meinen 900 Mustern :smiley: bleiben.

Um auf "delay" verzichten zu können, ist da "loop als Schleife mit millis()" oder eher "Blink Without Delay" anzuwenden?
Wobei ich bei beiden nicht weiß, wie ich das umsetzen muss :roll_eyes:

Gruß

Andreas

überlege dir mal, wie du eine Blinkleuchte realisieren würdest, weil man im Schmuckladen deines Bahnhofs eingebrochen hat.
--> Blink without delay

also mach erst mal eine Blinkled.
Wenn die Blinkerei läuft, kannst du deinen TV angehen.

Jetzt überleg dir mal was du für deinen TV tun müsstest:

erste For schleife: die regelt den Ablauf, also merk dir das i
zweite For schleife: ist eigentlich jetzt schon ein nonsense, da hätte ein delay(20j) auch gereicht.
Das verwendest du als künftigen Intervall
Auflösen des delay(20) --> ergibt den intervall 20
j

Du hast nun einen Zähler als Statemachine (dein i), und einen Intervall (für dein Blink Without Delay)

ungeprüft unter der Annahme, dass die Funktion so oft wie möglich aufgerufen wird, wenn der TV laufen soll

void simulation_TV ()
{ 
  static uint32_t previousMillis = 0;   // static, damit der Wert nach Ende der Funktion erhalten bleibt, spart uns eine globale Variable.
  static uint16_t intervall = 0;   // ersetzt j
  static uint16_t actual = 0;   // früher i
  if (millis() - previousMillis > intervall)   // analog "Blink Without Delay"
  { 
      intervall = pgm_read_byte(&(brightness[actual][0])) * 20;   // gelesenen Wert gleich vervielfachen, ersetzt unser delay(20)
      byte red   = pgm_read_byte(&(brightness[actual][1]));
      byte green = pgm_read_byte(&(brightness[actual][2]));
      byte blue  = pgm_read_byte(&(brightness[actual][3]));
      analogWrite(redPin,   red);
      analogWrite(greenPin, green);
      analogWrite(bluePin,  blue);
      previousMillis = millis();   // aktuelle Zeit als Aufrufzeit merken
      actual++; // actual fürs nächste mal weiterdrehen. Ist wohl geschmackssache ob du das am ende oder am Anfang machst, jedenfalls wollen wir den nächsten Aufruf mit dem nächsten "i" haben
      if (actual >= elements) actual = 0; 
    }  
  }
}

P.S.:
{198, 1, 1, 1},
{ 1, 1, 1, 1}

ich bezweifle, dass man ein PWM 1 auf einer LED sieht, das heißt hier schaltet der TV für 19820 millis ab und dann noch mal für 120 millis. D.h. du verschwendest hier weitere 4 Byte Progmem. Der Fernseher ist somit "schwarz" obwohl er noch läuft.

hab noch mal drüber gesehen

    for (unsigned int j = count; j > 0; j--) {
      delay(20);
    }

funktioniert genau invers zu meiner Logik.
Du musst also das actual mit actual-- runterzählen lassen,
bei 0 auf elements setzen.
und daher würde sich bei der Deklaration und Initialisierung ein

static uint16_t actual = elements;   // früher i

anbieten.

Hallo noiasca,
vielen Dank für den Sketch und den hilfreichen Bemerkungen dazu.
Einiges ist klarer. Aber noch nicht alles.
Mit delay (20) hatte die TV-Simulation eine Länge von 2 Min und 43 Sek.
Mit "intervall = pgm_read_byte(&(brightness[actual][0])) * 20;" ist nach knapp einer Minute Schluss.
Das verstehe ich nicht.

void simulation_TV ()
{
  static uint32_t previousMillis = 0;   // static, damit der Wert nach Ende der Funktion erhalten bleibt, spart uns eine globale Variable.
  static uint16_t intervall = 0;   // ersetzt j
  static uint16_t actual = elements;   // früher i
  if (millis() - previousMillis > intervall)   // analog "Blink Without Delay"
  {
      intervall = pgm_read_byte(&(brightness[actual][0])) * 20;   // gelesenen Wert gleich vervielfachen, ersetzt unser delay(20)
      byte red   = pgm_read_byte(&(brightness[actual][1]));
      byte green = pgm_read_byte(&(brightness[actual][2]));
      byte blue  = pgm_read_byte(&(brightness[actual][3]));
      analogWrite(redPin,   red);
      analogWrite(greenPin, green);
      analogWrite(bluePin,  blue);
      previousMillis = millis();   // aktuelle Zeit als Aufrufzeit merken
      actual++; // actual fürs nächste mal weiterdrehen. Ist wohl geschmackssache ob du das am ende oder am Anfang machst, jedenfalls wollen wir den nächsten Aufruf mit dem nächsten "i" haben
      if (actual >= elements) actual = 0;
  } 
}

Moin,

als ich deinen Bericht gelesen habe, habe ich mich an ein eigenes Projekt (belebter Leuchtturm) erinnert. Die Idee ist nicht direkt von mir, aber hier bin ich fündig geworden.

Beschreibung des Projektes inkl. Sketches

mit Video:
Video dazu

Vielleicht hilfts ja!

Gruß Ralf

PS: auch hier im Forum: Link

Hast du mein post #8 gelesen bevor du #9 geschrieben hast?
Der Grund ist mir klar, meine Zeiten sind invers gegenüber deinem Ausgangssketch.
Weist du wie du #8 auf #7 anwendest oder bereitet dir das Schwierigkeiten? Wenn ja - welche Schwierigkeit?
Evtl. gehts auch einfach wenn du schon die Intervall-Ermittlung umkehrst.

intervall = (elements - pgm_read_byte(&(brightness[actual][0]))) * 20;

Hoffentlich habe ich dich jetzt nicht restlos verwirrt. Aber dir sollte eigentlich klar sein was dein Ausgangs-Sketch gemacht hat, und warum dann meine Funktion in #7 anders reagiert. Einfach mit dem Finger Zeile für Zeile durchgehen (das hilft wirklich!!!).

Hast du mein post #8 gelesen bevor du #9 geschrieben hast?

Ja, hatte ich.

intervall = (elements - pgm_read_byte(&(brightness[actual][0]))) * 20;

Hat bezüglich Länge der TV-Simulation nichts verändert (unter 1 Min).

Mittlerweile bin ich soweit, hinzunehmen, dass während der TV-Simulation die Fernbedienung nicht funktioniert.
Meine Programmier-Kenntnisse reichen leider nicht aus, hier eine Lösung zu finden.
Durch euch weiß ich zu mindestens, dass die for Schleife mit dem delay das Problem verursacht und mit millis Abhilfe geschaffen werden kann.
Ich weiß halt nicht, wie man mit millis es schafft, die TV-Simulation auf eine bestimmte Länge (z.B. 2,5 Minuten) zu bringen.
Ich danke euch recht herzlich für eure Unterstützung!
Gruß
Andreas

Ich weiß halt nicht, wie man mit millis es schafft, die TV-Simulation auf eine bestimmte Länge (z.B. 2,5 Minuten) zu bringen

.Es hängt weniger an millis() als daran, dass loop so geschrieben ist wird, dass ein einzelner Durchlauf keine (oder kaum) Zeit braucht.
Dann sagt einem die Abfrage
if (millis() - startSimulation < DAUER) ob die Simulation fertig ist oder nicht, und man kann während dieser Simulation auch noch beliebig viel nebenbei erledigen.

Hi

Momentan machst Du
Schleife über x Elemente, Die eben 2 Minuten dauert.
Darin töffte delay(), damit Das auch 2 Minuten dauert.

Was Du möchtest:
LED-Geflacker alle 20ms (Zeit pro Muster variabel) über eine Dauer von 2 Minuten.
Dazu brauchst Du die Zeiten der letzten Änderung und des Start wie die Position der Liste, in Der gerade ‘geflackert’ wird.

// global
boolean geflackersollan = false;
uint32_t laufzeit = (uint32_t)2 * 60 * 1000; //2x 60 Sekunden x 1000ms

void geflacker(void) {
  static uint32_t startzeit = 0;        //Startzeit des Flackern
  static uint16_t position = 0;         //Position in den Flacker-Werten
  static uint32_t lastflacker = 0;      //Start des jetzigen Muster
  static uint32_t flackerdelay = 20;    //Leuchtdauer des jetzigen Muster
  if (geflackersollan) {
    //wir sollen flackern
    if (millis() - startzeit >= laufzeit) {
      //TV-Zeit vorbei
      digitalWrite(TV, LOW);
      geflackersollan = false;
    } else {
      //hier soll geflackert werden
      if (millis() - lastflacker >= flackerdelay) {
        //hier auf das nächste Muster umschalten
        //flackerdelay auf den neuen Wert setzen
        //position auf die Position des nächsten Muster setzen (ggf. Überlauf, dann auf 0 umbrechen)
        lastflacker=millis();  //Startzeit dieses Muster merken
        analogWrite(pinRed, wertRed);
      }
      //hier kommen wir an, wenn wir ein neues Muster gesetzt haben, aber auch, wenn's noch NIchts zu tun gab
    }
  }
}

Start durch setzen von startzeit auf millis() und geflackersollan auf true.
Mir gefällt noch nicht, daß ZWEI Startbedingungen nötig sind - aber das Grundgerüst zum Nachtwächter (nur was machen, wenn auch was zu Machen da ist) ist vorhanden.

Ungetestet, da ich zu faul war, mir noch ein Array aus den Fingern zu saugen, Dessen Länge zu bestimmen, ect.pp.

MfG

Edit siehe #17

Hallo zusammen,

@postmaster-ino, michael_x: Ihr lasst nicht locker :wink: . Hatte mich doch eigentlich damit abgefunden, dass ich das nicht hin bekomme :smiley:

Man kann also die Dauer genau festlegen. Cool. Hab den letzten Sketch sogar verstanden.

Mein großes Problem ist allerdings jetzt, diesen Sketch so umzuschreiben, dass dieser in meinen existierenden Sketch eingebaut werden kann. Denn dort habe ich Befehle wie

  • const byte brightness[4] PROGMEM = {

oder

  • const unsigned int elements = sizeof(brightness) / sizeof(brightness[0]);

bzw. mein derzeitiges void simulation TV () sieht ja noch so aus:

void simulation_TV ()
{  
  for (unsigned int i = 0; i < elements; i++) {
    unsigned int count = pgm_read_byte(&(brightness[i][0]));
    byte red   = pgm_read_byte(&(brightness[i][1]));
    byte green = pgm_read_byte(&(brightness[i][2]));
    byte blue  = pgm_read_byte(&(brightness[i][3]));

    analogWrite(redPin,   red);
    analogWrite(greenPin, green);
    analogWrite(bluePin,  blue);
    
    for (unsigned int j = count; j > 0; j--) {
      delay(20);
    }
  }  
  
}

Gruß
Andreas

Hi

Du hast ein Array, in Dem die Farbwerte und die Dauer gespeichert sind.
Nun musst Du 'nur' beim Aufruf prüfen, OB Du gerade was machen musst, oder nicht.
Wenn 'geflackersollan' false ist - sind wir schon fertig.
Ansonsten: Wenn die Laufzeit der letzten Farbe noch nicht vorbei ist - wieder fertig.
Ansonsten: Neuen Farbwert und Zeit auslesen, Farbe setzen, Startzeit merken.
Ganz nebenbei hier auch prüfen, ob die Gesamt-Laufzeit bereits abgelaufen ist - wenn JA, 'geflackersollan' auf false und die LEDs ausschalten.

Viel mehr ist's wirklich nicht!

Du musst nur die FOR-Schleife in einzelne Aufrufe aufbrechen - bei jedem Aufruf prüfen, OB was zu tun ist - wenn Ja, WAS zu tun ist (andere Farbe oder Alles aus).

Dein delay ergibt sich von alleine, weil wir halt Nichts machen, bis diese Zeit um ist.
Also - wir beenden die Funktion direkt wieder und haben so Zeit für jede Menge andere Dinge - in anderen Funktionen, Die genau So aufgebaut sind und auch jeweils nur etwas machen, wenn die Zeit dafür gekommen ist.

MfG

uint32_t laufzeit = 2 * 60 * 1000;

Achtung Integer-Überlauf

Hi

Danke für den Hinweis - hab in #14 ein (uint32_t) dazugepackt - dadurch wird die Berechnung in 32 Bit ohne Vorzeichen ausgeführt.
(sonst in int16_t (Standard, ohne Angabe ist's INT), wo bei 32767 Schluss ist und wir kämen mit 120'000 weit drüber)

MfG

Du hättest auch einfach

uint32_t laufzeit = 2 * 60 * 1000UL;

schreiben können.

Achtung Falle: Das geht so nur bei Berechnung zur Initialisierung (Compiletime).

Zur Laufzeit muss das beim ersten Bestandteil stehen:

laufzeit = 2UL * 60 * 1000;

Gruß Tommy