Pico, Circuitpython, Speed

Registriere dich jetzt, um exklusive Vorteile zu genießen! Als registriertes Mitglied kannst du Inhalte herunterladen und profitierst von einem werbefreien Forum.
Mach mit und werde Teil unserer Community!
  • Liebes Forum,

    ich habe mit einem Pico, einem RGB-Keypad 4x4, einem Oled- Display und einem Encoder einen Midi-Controller gebaut. Ich habe Circuitpython verwendet, weil das für mich am einfachsten war. Der Controller hat zwar noch nicht alle Funktionen, läuft aber störungsfrei.

    Nur, er ist zu langsam. Wenn ich z.B. Noten senden will, dann werden diese zwar erzeugt, aber nicht zeitgleich.


    Jetzt habe ich gelesen, dass ein compiliertes Skript schneller arbeitet. Ich habe nun die Frage,

    - gibt es eine Möglichkeit, mein fertiges Skript zu compilieren, um so Geschwindigkeitsgewinne zu erzielen, oder

    - muss ich das Ganze nochmal über die Arduino-IDE neuprogrammieren? Reicht das schon und kann ich die bisher verwendeten Bibliotheken verwenden? (Mit Arduino habe ich etwas Erfahrung)

    - Oder muss ich das Ganze mit einer anderen (welcher und wie) in Python oder so neuschreiben? Wo kann ich das lernen?


    Für Hilfe wäre ich echt dankbar.

    lG

    Norbert (Micki)


  • Ein kompiliertes Python-Programm startet schneller, das läuft nicht schneller, denn das einzige was man sich spart ist die Kompilation zur Laufzeit. Das macht man hauptsächlich um Speicher zu sparen.


    Python-Bibliotheken kannst Du von C++ aus nicht verwenden bzw. macht das bei MicroPython & Co keinen Sinn.


    Neben MicroPython & Derivaten ist C++ üblich, man kann aber auch andere Programmiersprachen verwenden. Bei anderen Programmiersprachen hat man dann aber das Problem, dass man auf weniger Community und Erfahrung zurückgreifen kann.

    „Eat the rich — the poor are full of preservatives.“ — Rebecca ”Becky” Connor, The Connors

  • Hallo,


    wie sieht denn das Programm aus?

    Eventuell kann man auch daran schon etwas verbessern.


    Grüße

    Dennis

    🎧 Mein Auto springt, mein Toaster kocht, es zwickt mich im Genick. Meine Frau ist eingelocht, die Spülmaschine tickt. Meine Telefonapperat brüllt mich seit Tagen an, er ist schon lange abgestellt im Bett liegt Peter Pan. Die Uhr geht falsch, die Haustür singt, mein Spiegel schlägt zurück - Ich werde noch verrückt, was solls ich bin entzückt. Die Badewanne zieht nicht ab ihr glaubt nicht was ich seh' - Sie ist voll Himbeerengelee 🎧

  • Hallo blackjack und Dennis89,

    erstmal Dank für eure schnelle Antwort.

    Ich lade den Sketch heute Abend mal hoch. Ich schreibe hier von nem Tablet. Da hab ich den Sketch nicht drauf.

    Die Startgeschwindigkeit ist mir egal, aber die Performance muss stimmen. Ich hab mal etwas ähnliches mit nem Arduino gemacht. Der war echt schneller. Aber da liefen die Komandos parallel zu den Tasteninformationen zu der Klangerzeugung. Da konnte ich die Tastaturdaten nicht verbiegen, wie ich wollte, nur ergänzen mit Controllerbefehlen.

    In Threads hatte ich gelesen, die Verwendung von kompierten Programmen bringe eine enorme Geschwindigkeitssteigerung (bis 500 fach).Soviel werde ich nicht brauchen, aber 10fach wäre schön.

    Ich glaube, mein Sketch ist sehr anfängermäßig. Wenn ihr den kritisiert, dann bitte nur bezogen auf Geschwindigkeitsprobleme. Natürlich lerne ich auch gerne "schöner" zu programmieren. Aber bitte nicht schimpfen 👍🌈

    LG

    Micki

  • So, hier der Skript-Text und das Pinout.

    Vielleicht kann mir jemand eine Logik erklären, wie globale Variablen richtig gesetzt werden und nicht mehrfach definiert werden müssen.

    Aber das ist ein Nebenproblem. Wie gesagt, es geht um Geschwindigkeit.

    Wäre toll, wenn es eine einfache Lösung gäbe.


    Zum Aufbau:

    der Sketch hat zZt 5 Modi (0-4).

    0 sendet Noten, 1 Dur-Akkorde, 2 Scalen-Akkorde, 3 8 Dur und 8 Mollakkorde und 4 bringt Funktionen (zZt nur Transposition(-10, +10, -1, +1))


    Die Modi werden über den Encoder aufgerufen und per EncSw drücken bestätigt.

    Das Keypad löst in Abhängigkeit des Modus entweder Töne aus oder steuert Parameter (zZt nur Transpose).

    Ich hoffe, der Sketch ist verständlich. Für eure Mühen meinen Dank.

    Micki

  • Moinsen,


    Ich kenne mich nun mit Circuitpython nicht so gut aus, aber du lässt bei den vielen gestaffelten IF Abfragen viel viel Zeit liegen.

    Auch wenn schon eine Bedingung wie zB :


    Python
        if (i==0):
            Text3=("Taste 1   ")

    erfüllt ist, werden auch noch die anderen IFs abgefragt, obwohl das gar nicht mehr notwendig ist. Denn in einem Durchlauf kann ja immer nur ein Zustand zutreffend sein.

    Ob man dann schon mit zB einen Break aus dieser einen übergeordneten Schleife herausspringen kann, und ob das Sinn macht, in µPython würde sich diese Methode alternativ zu ELIF anbieten.


    Eine weitere Form wäre diese ganzen MODI Abfragen ab Zeile 284 anders zu organisieren.
    Das man hier Unterfunktionen nutzt und den Übergabewert an diese Fuktion als LIST Zähler übergibt.
    Beispiel :

    Code
    Tonfolge = [(48, 90 ), # Mode 0
                (48, 48 + 4, 48 + 7 , 90)) # Mode 1
    
    def MODI(table):
        for val in table[:-1]:
             midi.send(NoteOff(val + i, table[-1])
    
    while True:
        MODI(Tonfolge[Modus])

    Damit könnte man die Ausführung erheblich beschleunigen, weil die Tuple mit der Stelle gleich dem Modus an diese Funktion übergeben wird, und dann wird nur die letzte Stelle = 90 aus der Betrachtung herausgelöst, und alle davor folgenden Werte in der Schleife "val" von der Stelle 0 bis zur vorletzten Stelle durchlaufen.

    Woher nun "Modus kommt und wie das interagiert ist gar nicht so einfach zu überblicken. Aber diese Anwendungsform alle Festwerte über eine LIST mit Tuple bereitzustellen, welche ohne IF abfrage nur zugewiesen aufgerufen werden müssen, bringt einfach nur Geschwindigkeit.

    Ob das so in Circuitpython zu 100 % genauso funktioniert wie in µPython kann ich dir leider nicht sagen. Ich stehe damit ( Circuitpython ) auf Kriegsfuß.

    Franky

  • Micki Globale Variablen setzt man richtig in dem man sie gar nicht benutzt. Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht in einer Funktion die üblicherweise `main()` heisst. Alles was eine Funktion/Methode ausser Konstanten benötigt, wird als Argument(e) übergeben. Globale Variablen sind ja an sich schon unübersichtlich, aber dann auch noch eine die überall einfach global existiert und verwendet wird `i` zu nennen habe ich das erste mal gesehen. (Stimmt nicht so ganz — bei klassischen BASIC-Programmen mit Zeilennummern habe ich das natürlich schon gesehen.)


    Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).


    Man nummeriert keine Namen. Dann will man sich entweder sinnvollere Namen überlegen, oder gar keine Einzelwert und -namen sondern eine Datenstruktur. Oft eine Liste.


    Man muss nicht jedes kleine Zwischenergebnis an einen Namen binden.


    So einige Namen sind auch nicht gut. Neben dem `i` beispielsweise so etwas wie `Zahl`. Wenn dann da ein Kommentar dran steht der sagt, dass da der Modus verglichen wird, sollte `Zahl` vielleicht einfach `modus` heissen und schon kann man sich den Kommentar sparen.


    Da ist viel Code der nach kopiert und leicht angepasst aussieht. Das macht man nicht. Dafür gibt es Schleifen und/oder Funktionen, beziehungsweise kann man da sicher auch Sachen in Datenstrukturen aus dem Code herausziehen.


    Eine Funktion sollte jeweils eine in sich geschlossene Tätigkeit durchführen. Wenn man Funktionen hat die aufgrund eines Arguments (hier noch eine globale Variable) einen jeweils anderen Weg durch die Funktion geht, dann ist der Code falsch aufgeteilt. Statt beispielsweise mehrere Funktionen zu haben die pro Modus jeweils etwas anderes macht, bräuchte man eine Funktion pro Modus, beziehungsweise wahrscheinlich ein Objekt pro Modus. So das alles für einen Modus übersichtlich zusammengefasst ist, und man die Modi leicht ändern, erweitern, oder herausnehmen kann.

    „Eat the rich — the poor are full of preservatives.“ — Rebecca ”Becky” Connor, The Connors

  • Boah, danke, viel Stoff. Ich werd das morgen mal durcharbeiten.


    Variablen wie" Text4" sind entstanden, weil ich keine schnelle Funktion gefunden habe, mit der ich Display-Elemente löschen könnte. Ich schreib also das ganze Display mit den jeweils aktuellen Werten und die Ziffern sagen mir den Ort, wo die Infos im Display stehen werden


    Das "i" stammt aus einem Beispiel-sketch für Tastenabfrage und -beleuchtung und ist (richtig) kopiert.


    Wie ich mit Tupel die if-Abfrage vermeiden kann, guck ich mir an. Aber ein Hinweis: Es können ja mehrere Tasten gleichzeitig gedrückt sein. Da muss doch jede Taste abgefragt werden, oder nicht?


    Die Sache mit "Modus" und "Zahl" stimmt. Das war Müdigkeit.


    Was ich nicht kapiere ist: Bei den Variablen geht es ja hauptsächlich um Elemente, die die Tonausgabe verändern. Und die werden zu unterschiedlichen Zeiten und Zusammenhängen definiert. Und der Parameter "Transposition" wird eben nicht in der Schleife definiert, sondern im Modus 4, aber dann in der Schleife zB im Modus 3 angewendet. Da sind doch globale Variablen prima, denk ich. Vielleich kennt ihr ja nen Trick, wie man sowas lösen kann.


    Nundenn, ich werd mal schaun und berichten.

    1000 Dank für eure Tips! 😂🌈

    Schlaft schön 💐

  • Micki Da in der Funktion immer alle Texte geschrieben werden, nacheinander, wären das keine Einzelwerte sondern besser eine Liste. Und so wie ich das sehe wird die Funktion zu oft aufgerufen.


    Bei der Tastenabfrage wäre es effizienter wenn man sich die 16 Bit merkt, und mit der „exklusiv oder“-Bitverknüpfung (Operator ``^``) prüft welche Bits, also Tasten sich seit dem letzten mal geändert haben (und auch ob sich überhaupt welche geändert haben), und dann kann man beide Werte Bit für Bit durchgehen und überall wo sich etwas geändert hat, schauen was im neuen Wert dort steht, und hat da damit auch gleich ob die Taste jetzt neu gedrückt ist, oder losgelassen wurde. Und *das* würde man dann am besten in eine Generatorfunktion verfrachten und ist damit dann schon mal mehrere globale Variablen losgeworden.


    Der ”Trick” Daten und dazugehörende Funktionen zusammenzubringen sind Klassen. Also letztlich wie in C++.

    „Eat the rich — the poor are full of preservatives.“ — Rebecca ”Becky” Connor, The Connors

  • Moinsen Micki


    diese ganze Displaydarstellung wenn du ein festes Zeilenraster verwendest kannst du wie __blackjack__ schon sagte in eine Funktion auslagern, dazu wieder eine LIST und eine Funktion verwenden.


    Ebenso macht es wenig Sinn wie in den Zeilen

    Python
    oled.fill(0) # Originalcode / ggf fehlerhaft
    
    oled.text("Controller 2023 25", 0, 12, 1)
    time.sleep(4)
    oled.show()

    ein Sleep zu verwenden, wenn die Displayaktuallisierung = oled.show() verzögert wird. Ich denke du beabsichtigst diesen Inhalt für 4 Sekunden darzustellen als Startbildschirm, damit dieser 4 Sekunden vor Beginn des Programmlaufes angezeigt wird ?


    Python
    from utime import sleep
    
    Display_lines[0] = 'Controller 2023 25'
    renew_display(oled, Display_Lines)
    sleep(4)

    So könnte das mit der zuvor erstellten Funktion aussehen.
    Um eine einzelne Zeile wieder zu löschen müsste man nur:

    Python
    Display_lines[2] = '' # entspricht der Zeile mit der Position x0 y24
    renew_display(oled, Display_Lines)

    Damit erspart man sich nicht nur die vielen globalisierten Variablen, sondern hat auch eine festes Objekt = LIST welches den aktuellen Displayinhalt aufbewahrt, bzw. zwischenspeichert.

    Damit kein Zeilenüberlauf bei zu langen Texten entsteht könnte man diese Strings auch noch in der Länge beschneiden, könnte man diesen String einer Zeile noch auf die maximal Länge beschneiden.

    Python
    text = 'bla123bla456bla789'
    print(text[:1]) # würde jetzt nur das erste 'b' anzeigen
    print(text[:5]) # würde jetzt 'bla12' ausgeben

    dieses [:<Stelle>] kann man somit dazu nutzen, eine String auf eine bestimmte Länge einzukürzen.


    Wie Dennis89 und __blackjack__ immer wieder betonen, mit Funktionen, welche eine feste Aufgabe haben, denen alle notwendigen Dinge / Parameter übergeben werden, und einen ganz klar definierten spezifischen Umfang haben, aber auch nicht überladen sind mit zu viel Schnickschnack macht das Arbeiten und spätere Korrekturen einfacher. Zudem kann man sich viele Einzelabfragen mit IF ersparen, wenn die Datenbasis in LISTs [ variable Inhalte ] oder Tuple [ konstante Werte ] gesteckt werden.


    Was ich jetzt in diesem Zusammenhang nicht verstehe ist die Auswertung mehrerer gleichzeitig gedrückter Taten. Diese Kombinationsmöglichkeiten sind auf Grund deines Codes nicht ersichtlich, welche Kombinationen zulässig sind.

    Hier wäre eine klare Beschreibung der Arbeitsparameter von Nöten.

    Franky

    Edited once, last by Franky07: Codefehler Korrigiert. ().

  • Hi Franky, danke für deinen Kommentar. Das werde ich gleich angehen. Dank für die Beispiele😘😘😘An meinem Fehler zum Begrüßungsschirm erkennst du meinen Status 😌


    Gleichzeitiges Tastendrücken sehe ich z.B. im Modus 0 um 2Klänge oder Akkorde zu spielen.

    In den Chord-Modi auch. So ist C + am ein C6.

    Aber möglicherweise möchte ich auch ein paar Tasten und ein paar Funktionen mischen können ohne den Modus zu wechseln. zB: 1. Taste = Ton, dann 2.Taste = Pitch + 2 Oktaven in Zeit t.


    Wie der Controller mal fertig aussieht, weiß ich noch nicht. Das hängt eben stark an der Performance. Jetzt lern ich mit eurer Hilfe erst mal besser programmieren und dann mal sehen.


    Auf alle Fälle sende ich ein freudiges Danke für deine Mühen😂🌈💐

  • Die Positionen für die Texte könnte man auch ausrechnen (ungetestet):

    Python
    def update_display(oled, texts, number):
        oled.fill(0)
        for text, (x, y) in zip(
            texts, ((i // 3 * 75, i * 12 % 36) for i in range(6))
        ):
            oled.text(text, x, y, 1)
        oled.text(str(number), 48, 0, 1)
        oled.show()

    Oder wenn man das in eine Klasse steckt am besten auch nur *einmal* als Konstante auf der Klasse definiert, und statt `oled`, `texts`, und `number` (und `I2c`) dann nur noch ein Name für das Display im Hauptprogramm:

    Falls es Sinn macht, könnte man auch noch ein ”dirty”-Flag einbauen so das `update()` nur etwas tut wenn sich der Inhalt auch geändert hat.

    „Eat the rich — the poor are full of preservatives.“ — Rebecca ”Becky” Connor, The Connors

  • Hi blackjack,

    dir auch nochmal ein Danke.

    Das mit den 2Bit für die 16 Tasten hab ich mir auch schon gedacht und wird in dem Teil den ich beim PicoRGB Keypad abgestaubt habe ja auch schon angewendet. (Ich habs noch nicht verstanden,) werde das aber angehen.

    Das die Oled-Ausgabe zu oft angesprungen wird, stimmt. Jetzt ist das eben immer dann, wenn sich ein Wert geändert hat. Mal sehen, was mir dazu einfällt.

    Eure Vorschläge finde ich toll. Bin mal gespannt, was rauskommt.

    Ich melde mich 🌈👍

  • Moinsen,

    was mir ebenfalls aufgefallen:


    Was für eine Wahnwitz ;)


    Python
    def ModusName(number):
        menue_text = ('Noten', ('Chords'), ('Chd-Scale'), ('Dur+Moll'), ('Functions')
        if 0<= number <= 4:
            return menue_text[number]
        return ''
    
    Display_lines[3] = ModusName(<Quelle der Nummer>)

    Jede Ziffer in der übergebene Zahl zwischen 0 und 4 befüllt die Zeile 4 mit dem zugeordneten Textinhalt, und alles andere würde eine löschen bzw. leere Zeile bewirken.


    Es geht somit komplett ohne IF was einfach nur wieder Geschwindigkeit bedeutet

    Franky

  • Moinsen Micki


    Wie hiess der Ballermann Song "Es ist Wahnsinn ... " ;)

    Du musst dich echt nicht wundern das bei den vielen IF auf IF und IF deine Laufzeit flöten geht:


    Python
    def button_select(number):
        if 0 <= number <= 15:
            return f'Taste {number + 1:2d}   '
        # hier kann noch Code folgen, wenn diese Bedingung nicht erfüllt ist, was dann passieren soll
    
    Display_lines[3] = button_select(i)
    renew_display(oled, Display_Lines)

    Falls du noch eine Ausnahme benötigst einfach dahinter / darunter weiter machen.
    Dieses Return mit dem schon formatierten Text zur Displayzeile 4 beendet damit vorzeitig diese Funktion. Wenn diese Bedingung nicht erfüllt ist geht es danach weiter als wäre nichts gewesen und man spart sich auch eine weiteres ELSE oder ELIF.
    Die Ausgabe :<Stellen>d bewirkt eine rechtsbündige Formatierung für 2 VK-Stellen.

    Franky

  • Hallo Junx,


    das war ein aufregender Tag. Ich hab ein paar Macken behoben (Zahl/Modus, if-Abfragen) und viel getestet. Dabei ist mir aufgefallen, dass ich mein Hauptproblem (Speed) am Besten im Modus 0 betrachten kann.


    Ich hab dann mal einen Sketch erzeugt, in dem alle anderen Modi ausgeschlossen wurden - kaum Erfolg.


    Dann hatte ich die Idee, alle nicht wichtigen Funktionen auszuschalten - wie z.B. das Display und siehe da: das wars. Ich hab zwar noch keine genauen Daten, aber der Speedgewinn war enorm. Bei den weiteren Tests fiel mir auf, dass insbesondere die Text-Ausgaben in der inneren Schleife (Zeile 445 ff, with device) die Bremser waren.


    Meine Fragen wäre nun:

    - Habt ihr eine Idee, wie man eine Textausgabe auf das OLED hinbekommt, ohne den Hauptprozess zu verzögern? Es wäre ja doch schön zu sehen, unter welchem Modus man gerade arbeitet und welche Taste man gerade drückt.

    - Außerden möchte ich mal genauer wissen, wieviel Zeit für welchen Prozess draufgeht. Beim Arduino gabt es Millis. Gibt es was ähnliches auch beim Pico?

    - Und, ich würde den Pico gerne sauber runterfahren, wenn ich fertig bin. Kennt ihr eine Routine?


    An den anderen Vorschlägen werde ich weiterarbeiten. Da gibt es für mich eine Menge zu lernen.

    Ok, anliegend das aktuelle Skript

  • Moinsen Micki

    Meine Fragen wäre nun:

    - Habt ihr eine Idee, wie man eine Textausgabe auf das OLED hinbekommt, ohne den Hauptprozess zu verzögern? Es wäre ja doch schön zu sehen, unter welchem Modus man gerade arbeitet und welche Taste man gerade drückt.

    - Außerden möchte ich mal genauer wissen, wieviel Zeit für welchen Prozess draufgeht. Beim Arduino gabt es Millis. Gibt es was ähnliches auch beim Pico?

    - Und, ich würde den Pico gerne sauber runterfahren, wenn ich fertig bin. Kennt ihr eine Routine?

    Zur Frage 1: Diese OLEDs 0,96" gibt es in drei Ausführungen ! Ohne eine Spezifizierung kann man hier nur raten.
    Es gibt die reine 5 Volt Variante die auch notdürftig auch mit 3,3 V Vc und 3,3 Volt Pegeln was auf die Matschscheibe zaubert, aber nur wegen der Pegelakzeptanz. Diese sind bei auch nur Vc 3,3 Volt extrem langsam.
    Dann gibt es diese Displays, die eine 3,3/5,0 V Spezifizierung haben, auf die explizit hingewiesen wird. Diese kann man bis SCL- Takt 400 kHz betreiben, was schon einmal ein Fortschritt wäre, um deinen Speed Problem aus dem Weg zu gehen.
    Und dann gibt es diese Displays als "Only 3,3 V" Variante. Diese mit guter Kühlung, also nicht eingepfercht in einem Kasten mit anderen Abwärme produzierenden Dingen kann man via SCL-Takt stabil bis 580 kHz fahren. Allerdings wie bei jedem OLED mit reduzierter Lebensdauer. 400 kHz sind hierbei im sicheren Bereich.

    Alternativ du hast eine Thread im zweiten Core welcher via Timer Event ( dann aber nur über eine Klase ) das Display automatisch und regelmäßig aktualisiert. Je nach Umfang der Displayfüllung erreicht man mit einem Single Thread hier Aktualisierungsraten von unter 100 ms also 10 + Aktualisierungen pro Sekunde.
    Zur zweiten Frage:
    Normal steht in der Bibliothek TIME die Funktionen um ticks_xx zur Verfügung. Damit kann man in ticks_ms in Millisekunden oder mit ticks_us in Miicrosekunden , oder ticks_cpu die benötigten CPU Takte erfassen. [ µPython ]
    Jetzt bin ich noch nicht dahinter gestiegen wie man in Circuitpython die CPU Frequenz abfragen / manipulieren kann. In µPython aber auch in Circuitpython läuft die CPU verlangsamt auf 125 MHZ statt mit 133 MHz. In [µPython] kann man das mit machine.freq(133_000_000) machen, das man hier schon einmal paar Sekündchen herausholt.

    Zu deiner Letzten Frage: Der PICO ist ein µController ohne BS ( Betriebssystem ) und muss nicht herunter gefahren werden. Wenn du keine File-Zugriffe auf das VS machst ist es normal nicht notwendig hier besondere Vorsichtsmaßnahmen zu ergreifen.

    Franky

  • Hi Franky,


    nochmal vielen Dank.

    Ich habe rausbekommen, wie man Zeit misst (time.monotonic()).

    Zur Ausgabe eines Midi-Befehls brauch der Pico maximal 0,007 Sekunden, also 7 Tausendstel. Das ist spitze.

    Für die Ausgabe eines Textes an das Oled 0,3 Sekunden, Das ist tödlich. Wenn man jetzt die Frequenz verdoppelt, dann ist man auch nur bei 0,15 Sek, noch zu langsam. Und Risiken scheu ich, wie die Pest.

    Der andere Weg, das Display alle 0,1 Sek neu zu beschreiben, ohne den Prozess zu stören erscheint mir besser und wäre auch ausreichend für die Kontrolle. Kennst du dazu irgend ein Beispiel? (Ich hab keine Ahnung). Oder weißt du, wo ich vielleicht suchen kann ?


    Dir und auch den anderen nochmals Danke. Ich bin heute echt weitergekommen !!!

    LG

    Micki

  • Moinsen Micki

    was meinst du mit verdoppeln ?

    Wenn du den I²C Bus ansprichst, dann sind 400 kHz statt 100 kHz ( Standard bei Circuitpython ) schon einmal nur 1/4tel. Und der Rest das hängt an dieser grottigen Bibliothek von Circuitpython. DIeser Aufbau über BUSIO und den ganzen anderen Geböns ist einfach nur grotten Langsam.

    Das die einfaches Threading nicht unterstützen, musst du damit vorlieb nehmen .

    Franky