Seite 1 von 1

Script toggln mit Taster Pi-Pico

Verfasst: Sonntag 19. März 2023, 12:24
von Golfer83
Hi, ich habe ein kleines Projekt mit einem Raspberry PI Pico.
ich lasse mir auf einem 1.3" Oled die Uhrzeit mit DS3231 anzeigen und habe 2 Temperaturfühler DS18B20 verbaut.
nun möchte die Anzeigen mittels Taster "umschalten" was auch klappt aber nur 8x und dann kommt folgender Fehler:

RuntimeError: maximum recursion depth exceeded

Das "umschalten erfolgt in der while Schleife:

Code: Alles auswählen

 if button_pin.value() == False and not button_down:
        oled.fill(0)
        exec(open("KW_Temp.py").read())
        break

Re: Script toggln mit Taster Pi-Pico

Verfasst: Sonntag 19. März 2023, 12:35
von sparrow
Wie kommst du denn darauf das so zu lösen?

Ich bin in der micropython-Welt nicht vertaut, aber

a) importier das Modul und rufe die Funktion daraus auf
oder
b) überleg dir, ob überhaupt ein eigenes Modul nötig ist.

While-Schleifen in der micropython-Umgebung sind auch nicht ganz trivial. Das kann man aber erst sehen, wenn du sie auch zeigst.

Re: Script toggln mit Taster Pi-Pico

Verfasst: Sonntag 19. März 2023, 12:35
von Sirius3
Rekursion als Ersatz für Schleifen ist selten eine gute Idee, vor allem nicht bei Micro-Processoren, die nur wenig Speicher haben. Benutzt eine Schleife.

Re: Script toggln mit Taster Pi-Pico

Verfasst: Sonntag 19. März 2023, 15:58
von Dennis89
Hallo,

du könntest auch auf eine steigende oder fallende Flanke des Tasters reagieren:
https://docs.micropython.org/en/latest/ ... ne.Pin.irq

Wenn du dann noch das Problem hast, dass der Taster prellt, dann findest du hier Hilfe:
viewtopic.php?p=409121#p409121

Grüße
Dennis

Re: Script toggln mit Taster Pi-Pico

Verfasst: Dienstag 16. Mai 2023, 18:34
von Golfer83
Habe alles nochmal überarbeitet und es sieht jetzt so aus:

Code: Alles auswählen

from machine import Pin, SoftI2C
from SH1106 import SH1106_I2C
#from SSD1306 import SSD1306_I2C
#import framebuf
from time import sleep
import sev2seg32
import writer
import urtc
import utime
import machine, onewire, ds18x20, time
from ds18x20 import DS18X20
from onewire import OneWire

ds1_pin = 10 #KüWa-Temp
ds2_pin = 8 #Außen-Temp
ds1 = DS18X20(OneWire(machine.Pin(ds1_pin)))
ds2 = DS18X20(OneWire(machine.Pin(ds2_pin)))
roms1 = ds1.scan()
roms2= ds2.scan()

button_presses = 0 # the count of times the button has been pressed
last_time = 0 # the last time we pressed the button

builtin_led = machine.Pin(25, Pin.OUT)
button_pin = machine.Pin(7, machine.Pin.IN, machine.Pin.PULL_UP)

def button_pressed_handler(pin):
    utime.sleep_ms(150)  			#Entrpellen
    global button_presses, last_time
    new_time = utime.ticks_ms()
    if (new_time - last_time) > 200: 
        button_presses +=1
        last_time = new_time

button_pin.irq(trigger=machine.Pin.IRQ_RISING, handler = button_pressed_handler)

old_presses = 0

i2c=SoftI2C(sda=Pin(0), scl=Pin(1))
rtc = urtc.DS3231(i2c)
oled = SH1106_I2C(128, 64, i2c, res=None, addr=0x3C) # Init oled display
oled.rotate(180)
oled.fill(0)

while button_presses == 0:
    oled.fill(0)
    datetime = rtc.datetime()
    font_writer = writer.Writer(oled, sev2seg32)
    #Uhrzeit
    font_writer.set_textpos(20, 30)
    if (datetime.hour) < 10:
        font_writer.printstring("0"+str(datetime.hour))
    else:
        font_writer.printstring(str(datetime.hour))
    font_writer.set_textpos(73, 30)
    if (datetime.minute) < 10:
        font_writer.printstring("0"+str(datetime.minute))
    else:
        font_writer.printstring(str(datetime.minute))
    
    font_writer.set_textpos(60, 23)
    font_writer.printstring(":")
    
    if (datetime.day) < 10:
        oled.text("0",3,7)
    oled.text(str(datetime.day) + " / " + str(datetime.month) +  " / " + str(datetime.year),14,7)
    
    if (datetime.second) < 10:
        oled.text("0"+str(datetime.second),112,52)
    oled.text(str(datetime.second),112,52)
    
    oled.show()
    sleep(1)
    # only print on change in the button_presses value
    if button_presses != old_presses:
        print(button_presses)
        builtin_led.toggle()
        old_presses = button_presses
        while button_presses == 1:
        #if button_presses == 1:
            ds1.convert_temp()
            time.sleep_ms(750)
            for rom in roms1:
                oled.fill(0)
                oled.text("Kuehlwassertemp.",2,10)
                font_writer = writer.Writer(oled, sev2seg32)
                font_writer.set_textpos(24, 33)
                font_writer.printstring(str("%3.1f"%ds1.read_temp(rom))) #3 stellen vorkomma, 1 stelle nachkomma
                font_writer.set_textpos(90, 33)
                font_writer.printstring("C")
                oled.text("O",82,38)
                oled.show() 
                sleep(3)
        while button_presses == 2:        
        #if button_presses == 2:
            ds2.convert_temp()
            time.sleep_ms(750)
            for rom in roms2:
                oled.fill(0)
                oled.text("Aussentemperatur",0,10)
                font_writer = writer.Writer(oled, sev2seg32)
                font_writer.set_textpos(24, 33)
                font_writer.printstring(str("%3.1f"%ds2.read_temp(rom))) #3 stellen vorkomma, 1 stelle nachkomma
                font_writer.set_textpos(90, 33)
                font_writer.printstring("C")
                oled.text("O",82,38)
                oled.show() 
                sleep(3)
        while button_presses == 3:
            button_presses = 0
Es funktioniert zwar aber manchmal eben nicht ....
Taster mit Pull-UP 10K Widerstand versehen, Leitung der DS18B20 Temp.-Sensoren doppelt geschirmt.

Vll. hat einer ne Idee woran es klemmen könnte.

Danke

Re: Script toggln mit Taster Pi-Pico

Verfasst: Dienstag 16. Mai 2023, 19:09
von Dennis89
Hallo,

was funktioniert denn nicht? Das drücken des Tasters während der Code vielleicht zufällig im 'sleep' einer Schleife hängt?


Grüße
Dennis

Re: Script toggln mit Taster Pi-Pico

Verfasst: Dienstag 16. Mai 2023, 19:28
von Golfer83
Also wenn ich den taster betätige springt er auch zur nächsten Schleife aber manchmal aben nicht und es wird die "Hauptschleife" abgespielt welches die Uhr ist und es reagiert kein Tastendruck. Muss dann immer den Stecker ziehen und wieder einstecken was sich doof wärend der Fahrt macht :-/

Re: Script toggln mit Taster Pi-Pico

Verfasst: Dienstag 16. Mai 2023, 22:59
von Dennis89
Hm dann müsste der Thread von IRQ eine Exception geworfen haben, damit der Rest aber noch weiterläuft? Aber da antwortet bestimmt noch jemand mit mehr Erfahrung.
Sonst müsstest du das Ganze am PC laufen lassen und schauen ob du Fehler bekommst? Ansonsten gibt es auch eine kleine Platine mit SD-Karten-Slot , darauf könnte man eventuell Fehler loggen. Aber vielleicht sieht auch einer noch den Fehler.


Ich hatte gerade ein paar Minuten Zeit und habe dein Code mal etwas umstrukturiert(in der Hoffnung, mir würde etwas unlogisches auffallen), ich hoffe die Funktion ist noch gleich.
Das hat sich leider etwas länger gezogen als gedacht, daher poste ich jetzt einfach mal meinen Zwischenstand, der sicherlich noch optimiert werden kann und ganz ungetestet ist:

Code: Alles auswählen

from time import sleep, sleep_ms, ticks_diff, ticks_ms

import machine
import sev2seg32
import urtc
import writer
from ds18x20 import DS18X20
from machine import Pin, SoftI2C
from onewire import OneWire
from SH1106 import SH1106_I2C

KUELWASSER_PIN = 10
AUSSEN_TEMPERATUR_PIN = 8
LED_PIN = 25
BUTTON_PIN = 7
SDA_PIN = 0
SCL_PIN = 1
# In millisecond
BOUNCE_TIME = 100


class InfoSystem:
    def __init__(self, display, rtc, led, kuelwasser_sensor, aussen_temperatur_sensor):
        self.display = display
        self.clock = rtc
        self.led = led
        self.kuehlwasser_sensor = kuelwasser_sensor
        self.aussen_temperatur_sensor = aussen_temperatur_sensor
        self.button_state = 0
        self.change_infosystem = None
        self.timestamp = ticks_ms()

    def update_button_state(self):
        while not ticks_diff(ticks_ms(), self.timestamp) >= BOUNCE_TIME:
            pass
        self.timestamp = ticks_ms()
        self.led.toggle()
        self.button_state += 1 if self.button_state < 3 else 0
        self.change_infosystem = True

    def show_date_time(self):
        while True:
            self.display.fill(0)
            datetime = self.clock.datetime()
            font_writer = writer.Writer(self.display, sev2seg32)
            font_writer.set_textpos(20, 30)
            font_writer.printstring("{:02d}".format(datetime.hour))
            font_writer.set_textpos(73, 30)
            font_writer.printstring("{:02d}".format(datetime.minute))
            font_writer.set_textpos(60, 23)
            font_writer.printstring(":")
            self.display.text(
                "{:02d} / {:02d} / {}".format(
                    datetime.day, datetime.month, datetime.year
                ),
                14,
                7,
            )
            self.display.text("{:02d}".format(datetime.second), 112, 52)
            self.display.text(str(datetime.second), 112, 52)
            self.display.show()
            if self.change_infosystem:
                self.change_infosystem = False
                break
            sleep(1)

    def show_cooling_water(self):
        while True:
            self.update_display(self.kuehlwasser_sensor, "Kuehlwassertemperatur")
            if self.change_infosystem:
                self.change_infosystem = False
                break

    def show_outside_temperature(self):
        while True:
            self.update_display(self.aussen_temperatur_sensor, "Aussentemperatur")
            if self.change_infosystem:
                self.change_infosystem = False
                break

    def update_display(self, sensor, description):
        sensor.convert_temp()
        sleep_ms(750)
        for rom in sensor.scan():
            self.display.fill(0)
            self.display.text(description, 0, 10)
            font_writer = writer.Writer(self.display, sev2seg32)
            font_writer.set_textpos(24, 33)
            font_writer.printstring("{:.3f}".format(sensor.read_temp(rom)))
            font_writer.set_textpos(90, 33)
            font_writer.printstring("C")
            self.display.text("O", 82, 38)
            self.display.show()
            sleep(3)


def main():
    kuehlwasser_sensor = DS18X20(OneWire(machine.Pin(KUELWASSER_PIN)))
    aussen_temperatur_sensor = DS18X20(OneWire(machine.Pin(AUSSEN_TEMPERATUR_PIN)))

    builtin_led = machine.Pin(LED_PIN, Pin.OUT)
    i2c = SoftI2C(sda=Pin(SDA_PIN), scl=Pin(SCL_PIN))
    rtc = urtc.DS3231(i2c)
    oled = SH1106_I2C(128, 64, i2c, res=None, addr=0x3C)
    oled.rotate(180)
    oled.fill(0)
    info_system = InfoSystem(
        oled, rtc, builtin_led, kuehlwasser_sensor, aussen_temperatur_sensor
    )
    button = machine.Pin(BUTTON_PIN, machine.Pin.IN, machine.Pin.PULL_UP)
    button.irq(trigger=machine.Pin.IRQ_RAISING, handler=info_system.update_button_state)
    while True:
        if info_system.button_state == 0:
            info_system.show_date_time()
        elif info_system.button_state == 1:
            info_system.show_cooling_water()
        elif info_system.button_state == 2:
            info_system.show_outside_temperature()


if __name__ == "__main__":
    main()
Ich hab mit dem Sensor leider keine Erfahrung, und habe fast über all deine 'sleep' 's mal drin gelassen. Die Entprellzeit kannst du jetzt über eine Konstante einstellen. Ich will den IRQ nicht durch ein 'sleep' unterbrechen, sondern ich will das alles registriert wird, aber in einem bestimmten Zeitraum nichts passiert.

Mal zusammen gefasst, man will keine keine globalen Variabeln, das macht ein Programm sehr unübersichtlich, schlecht wartbar und bei größeren Programmen wird man bei der Fehlersuche verrückt. Wenn man sich einen Zustand merken will, dann braucht man eine Klasse.
Code wiederholt man nicht gern, sondern steckt den lieber in Funktionen und ruft die mit passenden Argumenten auf.
Strings puzzelt man nicht mit '+' zusammen, MP hat die 'format'-Methode, da kann man die Ausgabe gleich mit führender 0 formatieren, dann fällt deine Abfrage weg. Ich bin mir nicht sicher, aber ich glaube aktuelle MP-Versionen haben sogar 'f'-Strings.
Wenn du Namen sprechend wählst, dann sparst du dir die Kommentare, die den Namen erklären.

Grüße
Dennis

Re: Script toggln mit Taster Pi-Pi

Verfasst: Dienstag 16. Mai 2023, 23:23
von __deets__
Das Problem wird ein prellender Taster sein, der dazu führt, dass die viel zu restriktive Logik, die auf 3 prüft, nicht mehr greift. Weil ein physikalischer Druck auf den Taster *zwei* Additionen ausgelöst hat. Und schon steht das Ding bei 4.

Dennis Code ist anders kaputt, er hört auf zu inkremetieren.

Code: Alles auswählen

self.button_state = (self.button_state + 1) % 3
ist die richtige Vorgehensweise.

Das ganze Geschleife und gewarte ist alles redundant. Statt kompliziert in while schleifen zu stecken, die dann innerhalb eine abbruchbedingung und Wartezeit haben, macht man *eine* Hauptschleife, die je nach Zustand verzweigt. Und einmal wartet.

Re: Script toggln mit Taster Pi-Pico

Verfasst: Mittwoch 17. Mai 2023, 05:48
von Dennis89
Guten Morgen,

ach Mist, klar, bei mir bleibt 'button_state' bei 3 hängen, weil ich immer 0 addiere, anstatt auf 0 zurück setzt 🤦‍♂️
Danke für die Verbesserung.

Code: Alles auswählen

from time import sleep, sleep_ms, ticks_diff, ticks_ms

import machine
import sev2seg32
import urtc
import writer
from ds18x20 import DS18X20
from machine import Pin, SoftI2C
from onewire import OneWire
from SH1106 import SH1106_I2C

KUELWASSER_PIN = 10
AUSSEN_TEMPERATUR_PIN = 8
LED_PIN = 25
BUTTON_PIN = 7
SDA_PIN = 0
SCL_PIN = 1
# In millisecond
BOUNCE_TIME = 100


class InfoSystem:
    def __init__(self, display, rtc, led, kuelwasser_sensor, aussen_temperatur_sensor):
        self.display = display
        self.clock = rtc
        self.led = led
        self.kuehlwasser_sensor = kuelwasser_sensor
        self.aussen_temperatur_sensor = aussen_temperatur_sensor
        self.button_state = 0
        self.change_infosystem = None
        self.timestamp = ticks_ms()

    def update_button_state(self):
        while not ticks_diff(ticks_ms(), self.timestamp) >= BOUNCE_TIME:
            pass
        self.timestamp = ticks_ms()
        self.led.toggle()
        self.button_state = (self.button_state + 1) % 3
        self.change_infosystem = True

    def show_date_time(self):
        self.display.fill(0)
        datetime = self.clock.datetime()
        font_writer = writer.Writer(self.display, sev2seg32)
        font_writer.set_textpos(20, 30)
        font_writer.printstring("{:02d}".format(datetime.hour))
        font_writer.set_textpos(73, 30)
        font_writer.printstring("{:02d}".format(datetime.minute))
        font_writer.set_textpos(60, 23)
        font_writer.printstring(":")
        self.display.text(
            "{:02d} / {:02d} / {}".format(
                datetime.day, datetime.month, datetime.year
            ),
            14,
            7,
        )
        self.display.text("{:02d}".format(datetime.second), 112, 52)
        self.display.text(str(datetime.second), 112, 52)
        self.display.show()

    def show_cooling_water(self):
        self.update_display(self.kuehlwasser_sensor, "Kuehlwassertemperatur")

    def show_outside_temperature(self):
        self.update_display(self.aussen_temperatur_sensor, "Aussentemperatur")

    def update_display(self, sensor, description):
        sensor.convert_temp()
        sleep_ms(750)
        for rom in sensor.scan():
            self.display.fill(0)
            self.display.text(description, 0, 10)
            font_writer = writer.Writer(self.display, sev2seg32)
            font_writer.set_textpos(24, 33)
            font_writer.printstring("{:.3f}".format(sensor.read_temp(rom)))
            font_writer.set_textpos(90, 33)
            font_writer.printstring("C")
            self.display.text("O", 82, 38)
            self.display.show()
            if self.change_infosystem:
                break
            sleep(3)


def main():
    kuehlwasser_sensor = DS18X20(OneWire(machine.Pin(KUELWASSER_PIN)))
    aussen_temperatur_sensor = DS18X20(OneWire(machine.Pin(AUSSEN_TEMPERATUR_PIN)))

    builtin_led = machine.Pin(LED_PIN, Pin.OUT)
    i2c = SoftI2C(sda=Pin(SDA_PIN), scl=Pin(SCL_PIN))
    rtc = urtc.DS3231(i2c)
    oled = SH1106_I2C(128, 64, i2c, res=None, addr=0x3C)
    oled.rotate(180)
    oled.fill(0)
    info_system = InfoSystem(
        oled, rtc, builtin_led, kuehlwasser_sensor, aussen_temperatur_sensor
    )
    button = machine.Pin(BUTTON_PIN, machine.Pin.IN, machine.Pin.PULL_UP)
    button.irq(trigger=machine.Pin.IRQ_RAISING, handler=info_system.update_button_state)
    while True:
        if info_system.button_state == 0:
            info_system.show_date_time()
        elif info_system.button_state == 1:
            info_system.show_cooling_water()
        elif info_system.button_state == 2:
            info_system.show_outside_temperature()
        sleep_ms(500)


if __name__ == "__main__":
    main()
Wieviele 'roms' werden denn da in der 'for'-Schleife gelesen? Gibt es da überhaupt mehrere?

Grüße
Dennis

Re: Script toggln mit Taster Pi-Pico

Verfasst: Mittwoch 17. Mai 2023, 10:33
von Sirius3
@Dennis89: die while-Schleife in update_button_state ist falsch, das ist ja nur ein umständlich geschriebenes sleep.
Also, statt zu warten reagiert man sofort und ignoriert weitere Signale, die danach innerhalb der Bound-Time auftreten:

Code: Alles auswählen

    def update_button_state(self):
        now = ticks_ms()
        if self.lasttime + BOUNCE_TIME > now:
            return
        self.lasttime = now
        self.led.toggle()
        self.button_state = (self.button_state + 1) % 3
        self.change_infosystem = True

Re: Script toggln mit Taster Pi-Pico

Verfasst: Mittwoch 17. Mai 2023, 11:21
von Dennis89
Hallo,

da habe ich mich ja wieder von meiner besten Seite gezeigt.
Dir auch danke für die Verbesserung.

Ich vermute mal, dass der Code für die zwei Sensoren dieser hier ist. 'scan' gibt eine Liste mit den Geräten zurück, die an den Bus angeschlossen sind. Hier haben wir zwei Sensoren, aber die laufen nicht über die gleiche Verbindung(?). Dann kommt immer ein Gerät zurück und wir brauchen keine 'for'-Schleife und die flag zum abbrechen der Schleife würde auch wegfallen. Der verlinkte Code bietet auch noch eine "Single"-Klasse an, die kann man nutzen wenn nur ein Gerät am Bus hängt, das wäre, falls meine Aussage richtig ist, hier doch perfekt. Oder?

Grüße
Dennis

Re: Script toggln mit Taster Pi-Pico

Verfasst: Mittwoch 17. Mai 2023, 12:41
von Golfer83
Hallo danke für das zahlreiche Feedback. Dennis sein Programm läuft so "halb" sobald ich den Taster betätige kommt ein Fehler. Screenshot des Fehlers kommt später.

Weiterhin habe ich herausfinden können das der Taster arg prellt aber nur wenn der Motor des Autos läuft und der Counter weit über 3 geht und dann geht natürlich gar nix mehr. Habe die letzte Zeile mit >=3 angepasst aber die Anzeige wechselt von allein hin und her.

Werde die Tasterleitung schirmen evtl. bringst was.

Danke euch

Re: Script toggln mit Taster Pi-Pico

Verfasst: Donnerstag 18. Mai 2023, 02:41
von __deets__
Hast du da pull-ups/downs verbaut? Sonst ist das eine Antenne. Das zu schirmen ist eher extrem.

Re: Script toggln mit Taster Pi-Pico

Verfasst: Donnerstag 18. Mai 2023, 10:01
von Golfer83
__deets__ hat geschrieben: Donnerstag 18. Mai 2023, 02:41 Hast du da pull-ups/downs verbaut? Sonst ist das eine Antenne. Das zu schirmen ist eher extrem.
Ja habe ich.

Habe die Schaltung im KFZ verbaut und sobald der Motor
läuft springt das hin und her.
Anfangs hatte ich Probleme mit den DS18B20 da die Kabel nicht geschirmt waren welche ich geschirmt habe und dann ging's.

Re: Script toggln mit Taster Pi-Pico

Verfasst: Donnerstag 18. Mai 2023, 17:30
von Golfer83
So .... Habe die Tasterleitung geschirmt und es springt nicht mehr von alleine aber der Taster prellt trotz pulldown arg, liegt das evtl. an dem langen "hub" des Tasters 🤔.
Wie lang kann eine Entprellzei max. sein?

Re: Script toggln mit Taster Pi-Pico

Verfasst: Donnerstag 18. Mai 2023, 21:20
von Golfer83
@Dennis89
Bekomme dein Programm nicht ans laufen, sobald ich den Taster betätige folgene Ausgabe:
"TypeError: function takes 1 positional arguments but 2 were given"

Re: Script toggln mit Taster Pi-Pico

Verfasst: Freitag 19. Mai 2023, 07:38
von Sirius3
`update_button_state` braucht halt ein zusätzliches Argument `pin`.

Re: Script toggln mit Taster Pi-Pico

Verfasst: Freitag 19. Mai 2023, 07:54
von __deets__
Golfer83 hat geschrieben: Donnerstag 18. Mai 2023, 17:30 So .... Habe die Tasterleitung geschirmt und es springt nicht mehr von alleine aber der Taster prellt trotz pulldown arg, liegt das evtl. an dem langen "hub" des Tasters 🤔.
Wie lang kann eine Entprellzei max. sein?
So lange du willst, du kannst das ja ueber Zeitstempel selbst programmieren.