TimerWipy



  • Hi , can someone explain how the method
    ''''timerchannel.irq(*, trigger, priority=1, handler=None)''' works?
    I have to use TimerWiPy.PERIODIC.
    I don't undestand also handler function.



  • @robert-hh Hi, sorry for my insistence on this topic.
    I 'm not able to use thread but I read about it and I think that it's better for my code if I use them . So do you know if is it possibile to use thread to read from two I2C through the code that I posted before?
    thank you.



  • @robert-hh I solved the problem creating a new function to compare data acquire by sensor with reference data and then it power on pwm. And in this way the period remain 50 ms.
    thank you



  • @vflorio94 For a consistent operation at a period of 0.02 or 50 ms it is required that the handler takes not longer than that, and that is executes the OWM changes at almost the same time within the handler. Looking at your code, I see at least two things that could affect timing:
    a) You write to a file. File i/O is buffered. As long as the data is written into the buffer, it's fast. When the buffer will be written, it can be slow.
    b) You call lib_imu2.get_euler() several times in the timer handler. I do not know the complexity of that call, but if it includes access to external hardware, then it may take some time. Better call it once, store the result and use that in consecutive statements.



  • @robert-hh Thank you, I solved this problem. I put data as metod.
    Now I have a new problem because I want to read data from sensor and after a comparisono with reference data I want to activate pwm. I did it in this way.
    But the problem is that the period change a lot, sample time decrease to 0.04.
    Is there another way to modify this code for activate pwm that can be better??
    I need if it is possible that period remain more possible like 0.02.

    thank you

    import machine 
    import pycom
    from machine import Timer
    from machine import idle
    import time
    import lib_imu
    import lib_imu2
    from machine import I2C
    from math import sqrt
    from math import fabs
    from machine import PWM
    from machine import SD
    import gc
    import os
    sd = SD()
    os.chdir('/sd')
    from machine import idle
    
    #
    # add the other imports here that you need
    # fileIMU1 = open('/sd/dati_IMU1.txt', 'w')
    #
    # acquire  values. The paramters of the constructor are:
    #
    class Acquire:
        def __init__(self, filename_imu1, data, period=0.02, runtime = 60):
            self.fileIMU1 = open(filename_imu1, "w")
            self.temp = Timer.Chrono()
            self.busy = True
            self.data = data
            self.runtime = runtime
            self.temp.start()
            self.alarm = Timer.Alarm(self.sample_and_write, period , periodic=True)
            # and add all other init tasks you need
    
        def stop(self):
            self.alarm.cancel()
            pwm_c2.duty_cycle(0)
            pwm_c5.duty_cycle(0)
            self.fileIMU1.close()
            #self.fileIMU2.close()
    
        def sample_and_write(self, alarm):
            passed = self.temp.read()
            # check, if done, if the time for sampling is expired, then stop
            if passed >= self.runtime:
                self.stop()
                self.busy = False
                print('end')
            else:
                # get data and write
                #print(passed, fabs(lib_imu2.get_euler()[2]))
                self.fileIMU1.write(repr(passed) + repr(lib_imu2.get_euler()) + "\n")
                 # avanti
                if fabs(lib_imu2.get_euler()[2]) > ( fabs(dati_rif[2]) + 0.2):
                    if fabs(lib_imu2.get_euler()[2]) < (fabs(dati_rif[2]) + 0.4):
                        pwm2 = PWM(0, frequency=1000 )
                        pwm_c2.duty_cycle(0.5)
                        pwm_c5.duty_cycle(0) 
                    elif fabs(lib_imu2.get_euler()[2]) >= (fabs(dati_rif[2]) + 2): 
                        pwm2 = PWM(0, frequency=2000 )
                        pwm_c2.duty_cycle(0.6)
                        pwm_c5.duty_cycle(0) 
                # indietro  
                elif fabs(lib_imu2.get_euler()[2]) <  fabs(dati_rif[2]) + 0.2:
                    if fabs(lib_imu2.get_euler()[2]) > (fabs(dati_rif[2]) + 0.4):
                        pwm5 = PWM(0, frequency=1000 )
                        pwm_c2.duty_cycle(0)
                        pwm_c5.duty_cycle(0.5) 
                    elif fabs(lib_imu2.get_euler()[2])< (fabs(dati_rif[2]) + 2): 
                        pwm5 = PWM(0, frequency=2000 )
                        pwm_c2.duty_cycle(0)
                        pwm_c5.duty_cycle(0.5)
                # and what else you have to do.
    
    #
    # now here the skeleton main_
    #
    sd = SD()
    os.chdir('/sd')
    
    # set up the hardware
    ''' SET UP IMU 0X29 '''
    # Scala Berg: imu L5 - lib_imu - acc
    lib_imu.set_acc()
    time.sleep(0.01)
    lib_imu.set_configmode()
    time.sleep(0.01) 
    lib_imu.set_units()
    time.sleep(0.01)
    lib_imu.set_mode('ndof')
    time.sleep(0.01)
    ''' SET UP IMU 0X28 '''
    # imu t1 - lib_imu2 - eul
    lib_imu2.set_configmode() 
    time.sleep(0.01)
    lib_imu2.set_units()
    time.sleep(0.01)
    lib_imu2.set_mode('ndof')
    time.sleep(0.01)
    ''' PWM '''
    pwm2 = PWM(0, frequency=500 )
    pwm5 = PWM(0, frequency=500 )
    #This sets up the timer to 
    #oscillate at the specified frequency. timer is an integer from 0 to 3. 
    # frequency is an integer from 1 Hz to 78 KHz 
    pwm_c5 = pwm2.channel(5, pin='P12') #dietro
    pwm_c2 = pwm5.channel(2, pin='P5')   # avanti
    pwm_c5.duty_cycle(0)
    time.sleep(0.01)
    pwm_c2.duty_cycle(0)
    time.sleep(0.01)
    
    ''' START''' 
    with open("/sd/Rif.txt", "r") as f:
        line = f.read()
        # now you have a list of one string per line
        dati_rif = [float(el) for el in line.strip("()\n ").split(",")]  # reference data
    print('start')
    acquire = Acquire('Acquisition_Vibration.txt', dati_rif[2], 0.02, 30)
    
    #
    # now get the data, until it's done
    #
    while acquire.busy:
        idle()
    #
    # whatever you have to do for cleanup, do it here
    #```


  • @vflorio94 That's a question of scope and visibility in Python.
    So most simple but unelegant method is to have that data global. So it is created outside every function. Then at any part of your code you can declare is a global to access it. Example:

    # at the top level of your code:
    data_buffer = bytearray(100)
    data_ptr_in = 0
    data_ptr_out = 0
    
    #
    # 
    class MyClass:
        def my_func(self):
            # declare symbols as global
            global data_buffer, data_ptr_in, data_ptr_out
            data_buffer[data_ptr_in] = some_data
            data_ptr_in = (data_ptr_in + 1) % len(data:buffer)
    

    You can also define functions (or better to say methods) to pass along data, and you can use property and setter and getter methods of classes, ...



  • @robert-hh sorry, I describe the problem in a bad way. I have another question.
    I have a data that I want to use inside the function 'sample_and_write', but I have some difficulties to pass it from main to the class '''Acquire''' and in the end to the function. Is it possible ? can you help me?





  • @robert-hh yes, I checked and you are right. And about command to activate pwm responding to data acquire by sensor?



  • @vflorio94 said in TimerWipy:

    self.pwm = PWM(0.02, frequency=500 )

    The first argument of the class instantiation is a timer ID, not a time or the like. The value range is 0 - 3. See: https://docs.pycom.io/firmwareapi/pycom/machine/pwm
    So it should be:
    self.pwm = PWM(0, frequency=500 )



  • @robert-hh Hello. I have to ask you a new thing correlate with this code that you wrote previously.
    I want to implement the code with another part in which I desire to activate PWM depending on the data sample and wrote by IMU. So, the question is:
    Is it correct if I introduced a control on the data that I wrote and the command to activate PWM in the fuction 'sample and write'?
    And have I also to complete class acquire like this?

    self.pwm = PWM(0.02, frequency=500 ) #This sets up the timer to 
            #oscillate at the specified frequency. timer is an integer from 0 to 3. 
            # frequency is an integer from 1 Hz to 78 KHz 
    

    I know that I have also to set PWM channel where I put set-up Hardware parameters.
    I'll be newly grateful if you can help me.



  • @robert-hh I understand all. finally it works without any interrupt
    than you a lot.



  • @vflorio94 Timer.Alarm() has to be called only once.



  • @vflorio94 You do not have to call alarm. Actually, it is not a function, it is a timer object. The callback function is called by the Timer periodically without further action by your main program. You main code has to prepare the data & objects needed by the callback and then start the timer. Typically, all of that would be enclosed in a class, and the main program would just instantiate this class. That could look like this:

    #
    from machine import Timer, idle
    #
    # add the other imports here that you need
    
    #
    # acquire  values. The paramters of the constructor are:
    #
    class Acquire:
        def __init__(self, filename_imu1, period=0.2, runtime = 60):
            self.fileIMU1 = open(filename_imu1, "w") # or append
            self.temp = Timer.Chrono()
            self.busy = True
            self.runtime = runtime
            self.temp.start()
            self.alarm = Timer.Alarm(self.sample_and_write, period ,periodic=True)
            # and add all other init tasks you need
    
        def stop(self):
            self.alarm.cancel()
            self.fileIMU1.close()
    
        def sample_and_write(self, alarm):
            passed = self.temp.read()
            # check, if done, if the time for sampling is expired, then stop
            if passed >= self.runtime:
                self.stop()
                self.busy = False
            else:
                # get data and write
                print(passed, self.runtime)
                self.fileIMU1.write(repr(passed) + "," + lib_imu.get_euler_int() + "\n")
                #
                # and what else you have to do.
    
    #
    # now here the skeleton main_
    #
    
    # set up the hardware
    #
    
    # and start
    acquire = Acquire("my_data_file", 0.2, 20)
    #
    # now get the data, until it's done
    #
    while acquire.busy:
        idle()
    #
    # whatever you have to do for cleanup, do it here
    #
    


  • @robert-hh the function can be like this?

    import machine 
    import pycom
    from machine import Timer
    import time
    import lib_imu
    import lib_imu2
    from machine import I2C
    from math import sqrt
    from machine import PWM
    from machine import SD
    import gc
    import os
    sd = SD()
    os.chdir('/sd')
    
    PERIOD= 0.02 #sample time
    
    lib_imu.set_configmode()
    time.sleep(0.01) 
    lib_imu.set_units()
    time.sleep(0.01)
    lib_imu.set_mode('ndof')
    time.sleep(0.01)
    
    lib_imu2.set_configmode() 
    time.sleep(0.01)
    lib_imu2.set_units()
    time.sleep(0.01)
    lib_imu2.set_mode('ndof')
    time.sleep(0.01)
    fileIMU1 = open('/sd/dati_IMU1.txt', 'w')
    #fileT_IMU1 = open("/sd/datiT_IMU1.txt", "w")
    fileIMU2 = open('/sd/dati_IMU2.txt', 'w')
    #fileT_IMU2 = open("/sd/datiT_IMU2.txt", "w")
    file_tempi = open('/sd/tempi.txt', 'w')
    list_eul = [ ]
    list_eul2 = [ ]
    list_tempi = []
    
    def sample_and_write(timer_obj):   #timer_obj??
        #list_tempi.append(temp.read())
        list_eul.append(lib_imu.get_euler_int())
        #list_tempi.append(temp.read())
        list_eul2.append(lib_imu2.get_euler_int())
        pass
    
    def acquisition(sec):
        t = Timer.Chrono()
        t.start()
        while 1: 
            t = t.read()
            if  t <= sec :
                alarm = Timer.Alarm(sample_and_write, PERIOD, periodic = true)
            else:
                alarm.cancel()
                fileIMU1.write(  repr(list_eul) + '\t')
                fileIMU2.write( repr(list_eul2) + '\t')
                del list_eul
                del list_eul2
    


  • @robert-hh thank you, now i'm trying to modify my function.

    • so, is 'timer_obj' my time of acquisition?
    • is alarm the command that I have to use in main?
      thank you a lot


  • @vflorio94 With that method you can achieve, that a certain function, the handler, is called when the time expires. That is what you need for your project. The setting of timer.PERIODIC means, that it is called over & over again. Like:

    from machine import timer
    PERIOD = 0.2  # call every 0.2 seconds
    
    def sample_and_write(timer_obj):
    # this is a function which reads form the sensor and writes
    # to disk. timer_obj identifies the timer, which calls this function
    # Can be ignored
        pass
    
    alarm = Timer.Alarm(sample_and_write, PERIOD, periodic=True)
    

    And when you want to stop it, call

    alarm.cancel()
    

    Edit: It should be positively noted that the callback is no interrupt. This means you are free for almost any Python operation, especially allocating memory.



Pycom on Twitter