funktionen auf esp 32 mit timer aufrufen

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
sauterle
User
Beiträge: 78
Registriert: Mittwoch 27. Juli 2022, 21:33

Hallo , ich hoffe ,das ich hir mit meinem micropyton problen richtig bin
ich versuche mit einem esp 32 Microcontroler auf micropython mit hilfe eines hardware timers im 2s takt den ds18b20 temperatur sensor auszulesen
das Programm hier:

import machine, onewire, ds18x20, time
from machine import Timer

ds_pin = machine.Pin(4)
messtemp = ds18x20.DS18X20(onewire.OneWire(ds_pin))


roms = messtemp.scan()

period=0
def temperatur():
global period
if period==1:
period= 0
for rom in roms:
print(round((messtemp.read_temp(rom)),2))
if period==0:
period= 1
messtemp.convert_temp()


timer= machine.Timer(0)
timer.init(mode = Timer.PERIODIC , period = 1000 , callback = temperatur() )

aber im 1 s takt kommt immer die fehlermeldung: TypeError: 'NoneType' object isn't callable
nehme ich die klammern hinter dem temperatur in der timer-initialisierungszeile hingegen weg kommt diese fehlermeldung: TypeError: function takes 0 positional arguments but 1 were given
der fehler kann

der Fehler muss irgendwo bei dem Timer liegen, da wenn der timer -Teil durch eine while-true schleife mit sleep befehlen ersetzt wird das programm funktioniert:
import machine, onewire, ds18x20, time
from machine import Timer

ds_pin = machine.Pin(4)
messtemp = ds18x20.DS18X20(onewire.OneWire(ds_pin))


roms = messtemp.scan()

period=0
def temperatur():
global period
if period==1:
period= 0
for rom in roms:
print(round((messtemp.read_temp(rom)),2))
if period==0:
period= 1
messtemp.convert_temp()


# timer= machine.Timer(0)
# timer.init(mode = Timer.PERIODIC , period = 1000 , callback = temperatur )
while True:
temperatur()
time.sleep(1)

viele Grüsse
sauterle
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Lies doch mal die Fehlermeldung im (ansonsten richtigen) Fall ohne Klammern, und die Dokumentation zum Timer. Dann wird es hoffentlich klar. Pro-Tipp: Argumente, die man nicht braucht, kann man einfach ignorieren.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@sauterle: Zusätzliche Anmerkungen zum Quelltext: Es ist üblich pro Modul einen eigenen Import zu machen. `time` wird auch schon im ersten Beispiel importiert, aber gar nicht verwendet.

In der Regel entscheidet man sich auch ob man Objekte aus einem Modul über den Modulnamen anspricht *oder* die Objekte direkt in den Namensraum holt. Also nicht mal `machine.Pin` und mal `Timer` aus `machine` importiert. Und das dann nicht einmal verwendet, weil im Code dann doch `machine.Timer` steht. Der importiere `Timer` aber *auch* benötigt wird, weil darüber eine Konstante angesprochen wird.

Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Was soll `messtemp` heissen? `temperature_sensors` oder einfach nur `sensors` wenn es keine anderen Sensoren im Programm gibt, sagen dem Leser etwas ohne das er raten muss.

`temperatur` wäre ein guter Name für einen Wert der eine Temperatur repräsentiert, aber nicht für eine Funktion. Funktionen und Methoden sind üblicherweise nach der Tätigkeit benannt die sie durchführen, damit man sie leichter von eher passiven Werten unterscheiden kann, und damit der Leser weiss was dort passiert.

Timer.PERIODIC ist der Default-Modus, den braucht man nicht angeben.

Wenn eine Variable nur die Werte 0 und 1 annehmen kann und man damit eigentlich nur zwei Zustände unterscheiden will, dann ist das ein Fall für Wahrheitswerte.

`period` ist auch ein schlechter Name, was man auch daran sieht, dass der Timer den Namen ja auch verwendet, für eine Zeitangabe und nicht für 0 und 1.

Das Programm dürfte da auch noch einen Fehler haben weil `period` in zwei aufeinanderfolgenden ``if``\s getestet wird, im ersten Fall aber die Bedingung so gesetzt wird, dass das zweite ``if`` garantiert immer wahr ist an der Stelle. Ich denke nicht, dass das so beabsichtigt wahr, sondern das eigentlich pro Aufruf nur einer der beiden Fälle abgearbeitet werden soll. Und dann kann man das negieren von `period` auch *einmal* nach den Fallabfragen machen, statt in beiden Zweigen den Wert zu setzen.

`round()` verwendet man üblicherweise nicht für die Ausgabe sondern nur wenn man mit gerundeten Werten weiter rechnen will. Bei Textausgaben, beziehungsweise generell bei Umwandlungen in Text, verwendet man entsprechende Formatanweisungen.

Zwischenstand (ungetestet):

Code: Alles auswählen

from ds18x20 import DS18X20
from machine import Pin, Timer
from onewire import OneWire


def handle_temperature_sensors(_timer=None):
    global in_sensor_read_phase

    if in_sensor_read_phase:
        for rom in roms:
            print(f"{sensors.read_temp(rom):.2f}")
    else:
        sensors.convert_temp()

    in_sensor_read_phase = not in_sensor_read_phase


sensors = DS18X20(OneWire(Pin(4)))
roms = sensors.scan()
in_sensor_read_phase = False
timer = Timer(0)
timer.init(period=1000, callback=handle_temperature_sensors)
Hier ist jetzt noch das ``global`` unsauber und auch die globalen Variablen auf die nur lesend zugegriffen wird, sollten nicht sein.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
sauterle
User
Beiträge: 78
Registriert: Mittwoch 27. Juli 2022, 21:33

hallo
ich habe das selbe problem wie oben beschrieben wieder ,mit der Änderung das es dieses mal eine Funktion aus einer Klasse ist.
da ich das oben beschriebene problem nicht lösen konnte bin ich es umgangenund habe daher imemer noch keine Lösung!

mit sleep 1s geht es wunderbar

Code: Alles auswählen

from machine import Pin,Timer
from time import sleep

class board_reading:
    def __init__(self):
        self.data_string=None
    def read(self):
        self.datasstring="d=4356" # im "programm"aus dem dieser auschnitt kommt erden hier in kuzenabständen messwerte in strinform an v
                                            # an denen alle sekunde einer in einen auf einem display anfezeigten graphen eingefügt wirdn
        print("Hello World") #zum testen ob die funktion wenn sie aufgerufen wird in diesem test "program"etwas macht

        
read=board_reading()

# readtimer=Timer(1)    
# readtimer.init(mode = Timer.PERIODIC , period = 2000 , callback = read.read)
# 
while True:
    sleep(1)
    read.read()

ergebnis : Hello World
Hello World
Hello World
usw.



verwende ich jedoch den Timer:

Code: Alles auswählen

from machine import Pin,Timer
from time import sleep

class board_reading:
    def __init__(self):
        self.data_string=None
    def read(self):
        self.datasstring="d=4356" # im "programm"aus dem dieser auschnitt kommt erden hier in kuzenabständen messwerte in strinform an v
                                            # an denen alle sekunde einer in einen auf einem display anfezeigten graphen eingefügt wirdn
        print("Hello World") #zum testen ob die funktion wenn sie aufgerufen wird in diesem test "program"etwas macht

        
read=board_reading()

readtimer=Timer(1)    
readtimer.init(mode = Timer.PERIODIC , period = 2000 , callback = read.read)

#while True:
#    sleep(1)
#    read.read()
kommt so lange im 1 s takt die Fehlermeldung: TypeError: function takes 1 positional arguments but 2 were given
TypeError: function takes 1 positional arguments but 2 were given
TypeError: function takes 1 positional arguments but 2 were given

bis ich den mikrokontroller neustarte!


bitte verzeiht mir falls es ein total dummer anfängerfehler ist.

LG sauterle :wink:
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@sauterle: Es ist halt immer noch der gleiche Fehler das `Timer` da was übergibt und dafür ein Argument vorhanden sein muss, auch wenn Du damit dann nichts machst.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
sauterle
User
Beiträge: 78
Registriert: Mittwoch 27. Juli 2022, 21:33

Vielen dank :D
so get es jetzt:

Code: Alles auswählen

from machine import Pin,Timer
from time import sleep

class board_reading:
    def __init__(self):
        self.data_string=None
    def read(self,x):
        self.datasstring="d=4356" # im "programm"aus dem dieser auschnitt kommt erden hier in kuzenabständen messwerte in strinform an v
                                            # an denen alle sekunde einer in einen auf einem display anfezeigten graphen eingefügt wirdn
        print("Hello World") #zum testen ob die funktion wenn sie aufgerufen wird in diesem test "program"etwas macht

        
read=board_reading()

readtimer=Timer(1)    
readtimer.init(mode = Timer.PERIODIC , period = 2000 , callback = read.read)
/code]

LG sauterle
DoGro
User
Beiträge: 15
Registriert: Montag 23. Mai 2022, 07:18

Hallo,

Ich versuche mich auch in die Timer Klasse einzuarbeiten und wollte sie auf dem RP Pico als Counter verwenden.
Auch hier soll über 5 Sekunden ein Messwert von einem Sensor aufgenommen werden.

Die standard Syntax:

Code: Alles auswählen

from machine import Timer

timer1 = Timer(period=5000, mode=Timer.ONE_SHOT, callback=lambda t:print('Ich werde nur einmal ausgeführt, nachdem 5 Sekunden vergangen sind.'))
Nun habe ich erstmal geprüft, was überhaupt in x gespeichert wird: "Timer(mode=ONE_SHOT, period=268695172, tick_hz=1000000)"

Code: Alles auswählen

def callbacks():
    pass
    
x = Timer(period=5000, mode=Timer.ONE_SHOT, callback=callbacks)
print(x)
für die callback Funktion erhalte ich dann diese Fehlermeldung: "TypeError: function takes 0 positional arguments but 1 were given"

Ich vermute, dass ich auch noch über diesen Punkt stolpere:
__blackjack__ hat geschrieben: Donnerstag 22. September 2022, 00:38 @sauterle: Es ist halt immer noch der gleiche Fehler das `Timer` da was übergibt und dafür ein Argument vorhanden sein muss, auch wenn Du damit dann nichts machst.
Verstehe den Hinweis aber irgendwie überhaupt nicht.

Ich hatte gehofft einfach mit folgendem Code:

Code: Alles auswählen

#Lesen der Messwerte eines Fingersensors des MAX30102
def datencheck(max):
    max.check()
    if max.slot_data_available(0):
        ir_value = max.get_slot_data(0)
        return ir_value
          
timer2 = Timer(period=1000, mode=Timer.PERIODIC, callback=lambda t: datencheck(daten))
Die Funktion des callback zu nutzen, um hier meine lesefunktion des Sensors einzugeben.
Sodass jede Sekunde eine Messwert gelesen wird. Und dann würde ich dieses am liebsten für eine definiert Zeit an Sekunden durchlaufen zu lassen mit:

Code: Alles auswählen

#countdown
start = time.time()
for i in range(120000):
      pass
timer2 = Timer(period=1000, mode=Timer.PERIODIC, callback=lambda t: datencheck(daten))
end = time.time()
retrun end-start
Weiß aber auch nicht wie ich dann den Wert speichern könnte, da er ja nicht im timer2 auftaucht.

Oder ist es sinnvoll mehr mit den ticks_ms funktion zu arbeiten?
https://docs.micropython.org/en/latest/ ... /time.html

Ich bedanke mich schonmal.
DoGro
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Timer-Callbacks werden mit einem Argument aufgerufen. Dein Callback nimmt kein Argument. Python beschwert sich darueber. Das ist, was __blackjack__ und ich gemeint haben.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Beim ``lambda``-Ausdruck wird es ja auch richtig gemacht. Einfach mal den ``lamdba``-Ausdruck in eine normale Funktion umscrheiben und dann mit der Funktion ohne Argument vergleichen.

Was keinen Sinn macht, ist das `callback()` einen Rückgabewert hat. Wer sollte denn wo, was damit machen?
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
DoGro
User
Beiträge: 15
Registriert: Montag 23. Mai 2022, 07:18

Hallo,

ok vielen Dank. Nun habe ich es endlich verstanden. Nun sind die Ausgaben identisch.

Code: Alles auswählen

#Code A
from machine import Timer

timer1 = Timer(period=5000, mode=Timer.ONE_SHOT, callback=lambda t:print('Ich werde nur einmal ausgeführt, nachdem 5 Sekunden vergangen sind.'))

Code: Alles auswählen

#Code B


def callbacks(y):    
    return print('Ich werde nur einmal ausgeführt, nachdem 5 Sekunden vergangen sind.')

timer1 = Timer(period=5000, mode=Timer.ONE_SHOT, callback=callbacks)
Ich habe es aber nun mit dem time Modul realisiert:

Code: Alles auswählen

from machine import Pin, I2C
from MAX30102 import *
import time
import os #zum Daten Speichern

#Konfiguration des Sensors
i2c_channel0 = I2C(0, sda=Pin(16), scl=Pin(17))
#Klasseninstance erzeugen
iR_daten = MAX30102(i2c_channel0)
iR_daten.setup_sensor() # Es werden Standardmäßig beide LEDs angesprochen.

#Abfrage der Sensordaten 
def datenspeicherung(sensor):
    ir_werte =[]
    start = time.ticks_ms()
    zeitdiff = 0
    while zeitdiff < 5000:
        sensor.check()  # muss in einer While-Schleife bleiben
        # Im Slot 0 werden die Daten des IR_Sensors abgefragt.
        # Im Slot 1 werden die LED_Daten hinterlegt
        if sensor.slot_data_available(0) == 1:
            ir_value = sensor.get_slot_data(0)
            ir_werte.append(ir_value)           
        zeitdiff = time.ticks_diff(time.ticks_ms(), start)
    sensor.shutdown(True)
    #Speichern der daten in einer Texdatei
    with open('ir_Daten.txt', 'w') as file:
        for i in ir_werte:
            file.write(str(i) + '\n')
    return ir_werte
    
    #Hauptprogramm
datenspeicherung(iR_daten)
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Der Sternchenimport sollte nicht sein. Wer weiss was man sich da noch so mit in den Namensraum holt. `os` wird importiert, aber nirgends verwendet.

Namen werden klein_mit_unterstrichen geschrieben, also `ir_daten` statt `iR_daten`. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Das sollte in diesem Fall wohl die `datenspeicherung()`-Funktion sein, denn die speichert ja nicht nur Daten, wie der Name suggeriert, oder fragt nur Sensordaten ab, was der Kommentar darüber behauptet, sondern macht jetzt schon fast *alles*.

Wonach wurde denn entschieden was Deutsch und was Englisch benannt wird? An `ir_werte` wird `ir_value` angehängt‽

Code: Alles auswählen

import time

from machine import I2C, Pin
from MAX30102 import MAX30102


def main():
    sensor = MAX30102(I2C(0, sda=Pin(16), scl=Pin(17)))
    sensor.setup_sensor()
    try:
        ir_werte = []
        start_zeit = time.ticks_ms()
        vergangene_zeit = 0
        while vergangene_zeit < 5000:
            sensor.check()
            if sensor.slot_data_available(0) == 1:
                ir_werte.append(sensor.get_slot_data(0))
            vergangene_zeit = time.ticks_diff(time.ticks_ms(), start_zeit)
    finally:
        sensor.shutdown(True)

    with open("ir_Daten.txt", "w", encoding="ascii") as file:
        for ir_wert in ir_werte:
            file.write(str(ir_wert) + "\n")


if __name__ == "__main__":
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
DoGro
User
Beiträge: 15
Registriert: Montag 23. Mai 2022, 07:18

Oh __blackjack__ du bist echt toll mit deinen Erklärungen.

Ich sollte wirklich nochmal mehr auf den PEP8-Style achten.

Das mit der main() Funktion haben wir so nie so verwendet. Versuche ich mir aber mal anzugewöhnen.
Ja das Programm wächst immer so sukzessiv und dann machen manche Namen keinen Sinn mehr.

Ich werde das vor dem nächsten Post auf jeden Fall überarbeiten.

Dankeschön!
Antworten