The bigger picture is that I am creating a PyQt5 app through which I want to save and plot the Modbus data. So later I can use them for PID auto-tuning. PID auto-tuner requires that the data are measured with precision of at least 0.05 seconds. And also data points need to be spread equally like this:
M wait 0.05s then M wait 0.05s then M
and not like this:M wait 0.08s then M wait 0.03s then M
(M = measure data)
.
I have tried implementing Threading.Timer
to read data every 0.05 seconds.
The problem is that the precision of the timer is too low.
This is the code on which I were testing the Threading.Timer
precision:
from threading import Timer
import timeit
starttime = timeit.default_timer()
def f():
Timer(0.05, f).start()
global starttime
print("The time difference is :", timeit.default_timer() - starttime)
starttime = timeit.default_timer()
#Here I would read the data
f()
The code produces this output:
...
The time difference is : 0.07623
The time difference is : 0.07707
The time difference is : 0.07684
The time difference is : 0.07557
...
If I were to read data with this precision. It would create huge time difference in the long run.
Ideally the code would read data every 0.05s
which is 20
reads per seconds. But with this precision it will read data on average every 0.07s
and interpret it as 0.05s
.
The time difference each second would be 0.07 * 20 - 0.05 * 20 = 0.4 seconds
which is unacceptable because after one minute the time shift would be 24
seconds.
Threading.Timer
object? Or what other methods / tools should I use?I have measured how long does it take to read one value from Modbus Server using this code:
from pyModbusTCP.client import ModbusClient
import timeit
c = ModbusClient("localhost")
c.open()
for i in range(0,10):
starttime = timeit.default_timer()
data_now = c.read_holding_registers(0, 1)
print("Reading 1 register takes:", timeit.default_timer() - starttime)
Output:
...
Reading 1 register takes: 0.000297100
Reading 1 register takes: 0.000307600
Reading 1 register takes: 0.000271699
...
What I am using:
You are overengineering. Just make an FPS lock. If reading takes more than 0.05, you have to recalculate backward (notimplemented). If you measure quick, recalculate against your desired 0.05s and wait that time. With this method, you can achieve exact 0.05s intervals. It cannot work if reading the register takes longer than your period
This is working example with FPS lock. Nothing fancy. Set precision to create fake reading latency. Set period for your purpouses (i set 1s, you want 0.05s)
import time
import random
def time_keeper(lost_by_reading,period=1):
if lost_by_reading>period:
pass # a problem because reading takes longer than period
elif lost_by_reading<period:
time.sleep(period-lost_by_reading)
def mock_register_reading(precision=1000):
rand_sleep = random.randint(0,10)/precision
time.sleep(rand_sleep)
_measure = time.time()
period = 1
for i in range(1000):
start = time.time()
mock_register_reading()
## just for log
print('measured after',time.time()-_measure, ', ERROR: ',period-(time.time()-_measure))
_measure = time.time()
### end
finish = time.time()
reading_time = finish-start
time_keeper(reading_time,period = period)
NOTE: I advise you not to use threading in regard to modbus. From my own experience, modbus and threading are not friends and for example reading register via threads only leads to catastrophy (just in case you will have an idea to make threads every 0.5 to read)