ich habe mit einem ESP8266 und der PxMatrix Library auf einer 64x32 RGB-LED-Matrix u.a. einen Scrolltext als Funktion programmiert. Die Scroll-Funktion blockiert dabei den restlichen Programmablauf im loop() nicht.
#define matrix_with 64
void scrollText(String text, uint8_t yPos, uint8_t delay)
{
uint16_t text_length = text.length();
int endPos = -(matrix_width + text_length * 5);
display.setTextWrap(false);
display.setRotation(0);
display.setFont(&Font5x7Katze); //eigener Font
static int xPos = matrix_width;
if (millis() - clkTimeScrolling > delay)
{
display.setTextColor(display.color565(0,0,0)); // Text an alter Position loeschen
display.setCursor(xPos + 1,yPos);
display.println(text);
display.setTextColor(myYELLOW);
display.setCursor(xPos,yPos);
display.println(text); // Text an neuer Position neu schreiben
xPos--;
if (xPos <= endPos) xPos = matrix_width;
clkTimeScrolling = millis();
}
}
Der Aufruf erfolgt im loop() z.B. mittels scrollText("Text ",7,20); .
Damit scrollt der Text dann oben im Matrix-Display durch.
Jetzt möchte ich die Funktion aber ein weiteres Mal mit Angabe einer anderen y-Position aufrufen so, dass auch unten ein zweiter anderer Text durchscrollt.
Allerdings funktioniert das nicht so einfach, da es dabei natürlich Probleme mit der gemeinsam genutzten xPos Variablen und dem if (xPos <= endPos) xPos = matrix_width; gibt,.
Kann mir jemand einen Tipp geben, wie man realisiert, dass jeder Funktionsaufruf quasi seine eigene xPos Variable führt?
Dabei werden die relevanten Daten in eine separate Struktur ausgelagert, deren Adresse der Funktion übergeben wird. Die Funktion arbeitet dann mit getrennten (globalen) Variablen, die unabhängig voneinander verändert werden können.
Auf diese Weise können sowohl das Delay wie auch die eigentliche Zeit, zu der die einzelnen Texte zuletzt tatsächlich gescrollt wurden, getrennt verarbeitet werden.
Ggf. könnte man die allgemeinen Settings und das "Vorgeplänkel"
uint16_t text_length = text.length();
int endPos = -(matrix_width + text_length * 5);
display.setTextWrap(false);
display.setRotation(0);
display.setFont(&Font5x7Katze); //eigener Font
static int xPos = matrix_width;
auch noch "hinter" die millis()-Bedingung verschieben; das ist ja nur erforderlich, wenn man tatsächlich Text ausgeben möchte und verbraucht ansonsten unnötigerweise Prozessorzeit.
Das hat mit ein paar Anpassungen prima geklappt. Danke nochmal für den Tipp mit der Struktur!
Das xpos in der Struktur musste ich z.B. von uint8_t nach int ändern, das es auch negative Werte größer 255 annehmen können muss (wenn der Text quasi links in voller Länge aus dem Matrix Display raus gescrollt ist).
#define matrix_width 64
uint16_t myYELLOW = display.color565(255, 255, 0);
struct ScrollType {
int xpos;
uint8_t ypos;
unsigned long Delay;
unsigned long lastScrollTime;
};
ScrollType A = {0,7,20,0};
ScrollType B = {0,32,30,0};
void scrollText(ScrollType &Scroller, String Text) {
if (millis()-Scroller.lastScrollTime > Scroller.Delay) {
// Text an alter Position loeschen (Text einfach in Hintergrundfarbe schreiben)
display.setTextColor(display.color565(0,0,0));
display.setCursor(Scroller.xpos + 1,Scroller.ypos);
display.println(Text);
// Text an neuer Position neu schreiben
display.setTextColor(myYELLOW);
display.setCursor(Scroller.xpos,Scroller.ypos);
display.println(Text);
Scroller.lastScrollTime = millis();
Scroller.xpos--;
if (Scroller.xpos == -(matrix_width + Text.length() * 5)) Scroller.xpos = matrix_width;
}
}
void loop() {
scrollText(A, textData1);
scrollText(B, textData2);
}
Jetzt habe ich noch eine Frage:
Die zu scrollenden Texte werden in meinem Fall dynamisch aus MQTT Abfragen gewonnen - ändern sich also immer mal.
Ich habe das so gelöst, dass ich den Text (String Text) aus der Struktur raus genommen habe und diesen nun stattdessen neben "ScrollType &Scroller" mittels "String Text" direkt an die Funktion übergeben kann.
Der Funktionsaufruf im loop() erfolgt also jetzt mit scrollText(A, textData1); bzw. scrollText(B, textData2); wobei textData1 und textData2 aus MQTT-Abfragen gewonnen werden.
Macht man das so? Oder gibt's da ein besseres Vorgehen?
Theoretisch könnte ich ja alle Werte (ypos, delay, lastScrollTime) nicht aber die anfangs diskutierte xpos so behandeln, oder?
Die Strings infoText1 und infoText2 werden dynamisch aus MQTT-Abfragen gewonnen.
Auf diese Art und Weise könnte ich doch zur Laufzeit dynamisch alle Elemente der Struktur ändern, richtig?
Ist das OK so, oder würde es ein Profi anders machen?
Wenn Du die Zeichenketten dynamisch zur Laufzeit generieren willst, würde ich auf den Typ String für Text verzichten und dafür mit '\0' terminierte char-Arrays (sogenannte c-strings) benutzen. Evtl. kann Dir dabei auch snprintf helfen. Zur Arbeit mit Zeichenketten habe ich mal ein Tutorial geschrieben.
Wenn Du auf char-Arrays in der Struktur umstellst, musst Du
entweder eine feste Länge vorgeben (maximale Anzahl der Zeichen plus 1 für die 0-Terminierung
oder
das flexible char-Array an das Ende der Struktur verschieben (und das bei der Initialisierung berücksichtigen!)
Fest:
#define MAXTEXTLAENGE 80
Struct ScrollType {
char text[MAXTEXTLAENGE+1];
uint16_t color;
int xpos;
uint8_t ypos;
unsigned long Delay;
unsigned long lastScrollTime;
};
ScrollType A = {"Scrolltext oben" ,myYELLOW,0,7,15,0};
ScrollType B = {"Scrolltext unten",myCYAN,0,32,25,0};
Flexibel:
Struct ScrollType {
uint16_t color;
int xpos;
uint8_t ypos;
unsigned long Delay;
unsigned long lastScrollTime;
char text[];
};
ScrollType A = {myYELLOW,0,7,15,0, "Scrolltext oben" };
ScrollType B = {myCYAN,0,32,25,0,"Scrolltext unten"};
Die Länge des Textes ermittelt man dann mit
int Laenge = strlen(A.text);
siehe dazu auch das von @Tommy56 erstellte umfangreiche Tutorial!
Die sicherste Möglichkeit ist wohl die Verwendung einer festen Länge und das programmtechnische Sicherstellen, dass diese Länge nicht überschritten wird. Das vermeidet dynamische Speicherzuweisungen für die char arrays (wie sie auf jeden Fall bei den Strings vorkommen). Allerdings kann man (siehe @Tommy56 s Tutorial) mit der Methode "reserve(groesse)" für Strings deren maximal zur Verfügung zu stellenden Speicher festlegen, um die Fehlerquelle "Speicherfragmentierung" zu umgehen.
vielen Dank für Eure Unterstützung! Ich habe das mit viel Interesse gelesen.
Zu der char-Sache habe ich mit meinem Projekt zusammenhängend noch Fragen.
ich verwende neben anderen Libraries den ESPAsyncWebServer. Da gibt's dann u.a. folgenden Code (hier nur relevante Auszüge):
Die Strings "mqttLocXTopic" werden dann an mehreren Stellen weiter (z.B. beim MQTT-Client Subscribe) genutzt.
Warum geht man hier eigentlich den Weg über das Festlegen der const char*, die man dann später in den request-> Methoden nutzt?
Könnte man nicht gleich z.B. if (request->hasParam("mqttLoc1Topic")) { schreiben? Es handelt sich doch sowieso um unveränderte Konstanten.
Außerdem wird eben ganz viel mit String gearbeitet, was ja laut Euren Hinweisen eigentlich nicht so gut ist.
Ich habe auch das Problem, dass der ESP8266, wenn ich im Webinterface sehr lange MQTT-Topics eingebe, manchmal abstürzt. Ich verwende noch viele Strings mehr, bei denen ich Werte aus der Webmaske verarbeite.
Könnte das Abstürzen wegen der intensiven Verwendung von String sein?
Kann (oder sollte) man das besser machen (Verwendung von char-Arrays mit vordefinierter maximaler Größe oder so)?
Vielen Dank schon mal wieder und viele Grüße
Daniel
besten Dank. Das mit dem String.reserve() werde ich mal einbauen. Sollte ich da die Reservierung jeweils so groß wählen, wie ich denke, dass ein String maximal werden könnte?
Was das const vs. "xyz" angeht meine ich, dass es in vielen Beispielen mit dem AsyncWebServer so gemacht wird:
Erst
const char* PARAM_INPUT_65 = "mqttLoc1Topic";
und dann
if (request->hasParam(PARAM_INPUT_65)) {
Man könnte doch aber gleich
if (request->hasParam("mqttLoc1Topic")) {
im Code schreiben, oder?
Warum geht man den Umweg über die vorherige Zuweisung des