Simple python script using 100% CPU on rPi3B+ - python

I have raspberry pi model 3b+ with a HC-SR04 ultrasonic distance sensor (there is also a couple of ds18b20 and a DHT21 but I think they're unrelated to my problem).
I have found a python script to make measurements from it and modified it to my needs - mostly to take a couple of reading spanned in time, take an average from it and map the value to range from 0 to 100, as the percentage and commit it to the influx database for grafana and domoticz.
The code:
#source: https://tutorials-raspberrypi.com/raspberry-pi-ultrasonic-sensor-hc-sr04/
#Libraries
import RPi.GPIO as GPIO
import time
from influxdb import InfluxDBClient
import sys
# https://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s#Python
import requests
client = InfluxDBClient(database='pellet')
series = []
#GPIO Mode (BOARD / BCM)
GPIO.setmode(GPIO.BCM)
#set GPIO Pins
GPIO_TRIGGER = 23
GPIO_ECHO = 22
#set GPIO direction (IN / OUT)
GPIO.setup(GPIO_TRIGGER, GPIO.OUT)
GPIO.setup(GPIO_ECHO, GPIO.IN)
def distance():
# set Trigger to HIGH
GPIO.output(GPIO_TRIGGER, True)
# set Trigger after 0.01ms to LOW
time.sleep(0.00001)
GPIO.output(GPIO_TRIGGER, False)
StartTime = time.time()
StopTime = time.time()
# save StartTime
while GPIO.input(GPIO_ECHO) == 0:
StartTime = time.time()
# save time of arrival
while GPIO.input(GPIO_ECHO) == 1:
StopTime = time.time()
# time difference between start and arrival
TimeElapsed = StopTime - StartTime
# multiply with the sonic speed (34300 cm/s)
# and divide by 2, because there and back
distance = (TimeElapsed * 34300) / 2
return distance
def pellet(dist):
# zmierzona odleglosc
# dist = distance()
# do zmierzenia poziom maksymalny
# 63 - do pokrywy
in_min = 63
# do zmierzenia poziom minimalny
in_max = in_min + 100
#wyjscie jako procent, od 0 do 100
out_min = 100
out_max = 0
# map z arduino: https://www.arduino.cc/reference/en/language/functions/math/map/
return (dist - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
def loop():
# nie wiecej jak 200 iteracji
loop = 200
# suma
total = 0
# tabelka z pojedynczmi wynikami
measurements = []
# liczba pomiarow do zrobienia
counter = 10
counter1 = 0
# czas pomiedzy pomiarami
sleep =30
#
while loop > 0:
loop -= 1
time.sleep(sleep)
# koniec, jesli wykonano liczbe pomiarow
if counter == 0:
#print(total/10)
return pellet(total/10), measurements
break
if loop == 0 and counter1 != 0:
return pellet(total/counter1), measurements
break
if loop == 0 and (counter1 == 0 or total == 0):
GPIO.cleanup()
sys.exit()
dist = distance()
# jesli wynik jest zly
if dist < 63 or dist > 163:
print("nie ok")
continue
counter -= 1
measurements.append(dist)
counter1 += 1
total += dist
print("total po ",counter1 , "sek: ", total, "dist: ", dist)
print(total/10)
#return total/10
if __name__ == '__main__':
try:
#dist = distance()
#print ("Measured Distance = %.1f cm" % dist)
#print (pellet(dist))
loop=loop()
print("avg :", loop[0])
#print("measurs :", loop[1])
#print("test :", loop[1][2])
if (1):
point = {
"measurement": "pellet",
"tags": {
"location": "piwnica",
"type": "hc-sr04"
},
"fields": {
"value": loop[0],
"raw_measurement1": loop[1][0],
"raw_measurement2": loop[1][1],
"raw_measurement3": loop[1][2],
"raw_measurement4": loop[1][3],
"raw_measurement5": loop[1][4],
"raw_measurement6": loop[1][5],
"raw_measurement7": loop[1][6],
"raw_measurement8": loop[1][7],
"raw_measurement9": loop[1][8],
"raw_measurement10": loop[1][9]
}
}
series.append(point)
client.write_points(series)
url = 'http://localhost:8080/json.htm?type=command&param=udevice&nvalue=0&idx=13&svalue='+str(loop[0])
r = requests.get(url)
GPIO.cleanup()
# Reset by pressing CTRL + C
except KeyboardInterrupt:
print("Measurement stopped by User")
GPIO.cleanup()
The problem is I noticed that the CPU temperature graph was elevated, with many short valleys to the about correct temperature.
When I ssh'd to the pi and run htop I saw that it was this script that is using 100% cpu.
But the weirdest thing is that the script is running in crontab every 15 minutes since yesterday, from about 14:30 and raise CPU temp started today at around 11:00.
I'm not a developer or a programmer and I just mostly copied the code from around the web so I don't know if this is some part of the code that did this (but why after 21 hours?) or what and why, and how to debug and fix it.
so it isn't just enviromental thing as the pi is in the attic where is about 5C to 10C.
Thank you for your help.

Here:
while GPIO.input(GPIO_ECHO) == 0:
StartTime = time.time()
this says "if the pin is 0, save the time, if the pin is zero, save the time, if the pin...." incessantly. You'll want to wait a little time after each check
while GPIO.input(GPIO_ECHO) == 0:
time.sleep(0.001) # 1 ms
StartTime = time.time()
The check itself probably takes ~us, so this will reduce CPU usage by 99%. You might want to do the same for the pin==1 case, depending on how accurate you need the times to be.

While it is impossible to know for sure where the issue lies without debugging directly on your system, and a glance at the code reveals several possible bugs in the logic, the one place that is most likely to cause the issue is the distance function.
As #mdurant already pointed out, your read loops will jump the CPU usage to 100%, but I suspect there is also another issue:
The trigger code and the read code are time sensitive!
The problem is, we don't know how much time actually passes between
# set Trigger to HIGH
GPIO.output(GPIO_TRIGGER, True)
# set Trigger after 0.01ms to LOW
time.sleep(0.00001)
GPIO.output(GPIO_TRIGGER, False)
and:
# save StartTime
while GPIO.input(GPIO_ECHO) == 0:
StartTime = time.time()
# save time of arrival
while GPIO.input(GPIO_ECHO) == 1:
StopTime = time.time()
While this simple algorithm - pulse trigger, count return interval will work on a microcontroller like Arduino, it is not reliable on a full blown computer like Raspberry Pi.
Microcontrollers run a single thread, with no OS or task scheduling, so they run code in real time or as close to it as possible (borrowing a few interrupts here and there).
But in your case you are running an interpreted language on a multitasking operating system, without explicitly giving it any high priority.
This means, your process could be suspended just enough time to miss the return "ping" and get stuck in the first loop.
This may only happen rarely when something else puts a load on the Pi, which would explain why you only noticed the issue after 21 hours running.
You should implement some form of timeout for GPIO reading loops and return an error value from the distance function if that timeout is reached to ensure you do not have an infinite loop when something goes wrong with the hardware, or you miss the return ping due to scheduling issues.
I suggest something like this:
def distance():
MAX_READ_ATTEMPTS = 10000 #this is a random value I chose. You will need to fine-tune it based on actual hardware performance
# set Trigger to HIGH
GPIO.output(GPIO_TRIGGER, True)
# set Trigger after 0.01ms to LOW
time.sleep(0.00001)
GPIO.output(GPIO_TRIGGER, False)
# lets not have any unneeded code here, just in case
# save StartTime
while GPIO.input(GPIO_ECHO) == 0 and retry_counter < MAX_READ_ATTEMPTS:
retry_counter += 1
StartTime = time.time()
# maximum number of retries reached, returning with error condition
if retry_counter == MAX_READ_ATTEMPTS:
return -1
reatry_counter = 0
# save time of arrival
while GPIO.input(GPIO_ECHO) == 1 and retry_counter < MAX_READ_ATTEMPTS:
retry_counter += 1
StopTime = time.time()
# maximum number of retries reached, returning with error condition
if retry_counter == MAX_READ_ATTEMPTS:
return -1
# time difference between start and arrival
TimeElapsed = StopTime - StartTime
# multiply with the sonic speed (34300 cm/s)
# and divide by 2, because there and back
distance = (TimeElapsed * 34300) / 2
return distance
I am intentionally not adding a delay between reads, because I am not sure what the tolerance for this measurement is in terms of timing, and sleep functions can't guarantee an exact delay (again, due to OS / CPU scheduling).
A brief 100% CPU load should be worth it to ensure accurate and valid measurements, as long as it is not kept up for too long, which our retry counting method should prevent.
Note that my only experience with ultrasonic sensors is using Arduino where a special pulseIn function is used that takes care of the implementation details of measurement so this solution is mostly an educated guess and I have no way of testing it.

Related

Using Arduino Ultrasonic Sensor with Pyfirmata

I'm trying to use pyfirmata to use an Arduino Ultrasonic sensor. I used Arduino Uno board and HC-SR04 Ultrasonic sensor. Here is the code I'm using. The code ran smoothly, it's just that it seems the echo pin failed to get an impulse from the trigger ultrasonic sound, so it keeps on getting False (LOW reading) and thus giving me false distance reading. Does anyone have a solution for this problem?
import pyfirmata
import time
board = pyfirmata.Arduino('COM16')
start = 0
end = 0
echo = board.get_pin('d:11:i')
trig = board.get_pin('d:12:o')
LED = board.get_pin('d:13:o')
it = pyfirmata.util.Iterator(board)
it.start()
trig.write(0)
time.sleep(2)
while True:
time.sleep(0.5)
trig.write(1)
time.sleep(0.00001)
trig.write(0)
print(echo.read())
while echo.read() == False:
start = time.time()
while echo.read() == True:
end = time.time()
TimeElapsed = end - start
distance = (TimeElapsed * 34300) / 2
print("Measured Distance = {} cm".format(distance) )
I've tried changing the time.sleep() to several value and it still doesn't work. It works just fine when I'm using Arduino code dirrectly from Arduino IDE.
I haven't done the exact math but given a range of 50cm you're at about 3ms travel time. That would mean you need to turn off the pulse and poll the pin state within that time.
That's not going to happen. The echo probably arrives befor you have turned off the emitter through PyFirmata. You should do the delay measurement on the Arduino.
I solve this false data problem by counting. I observe that false data comes after 2 or 3 sec. So if it takes More than 2 or 3 sec I clear count and restarts it from 0;
Sudo code:
cnt = 0;
if sensorvalue <= 20 && sensorvalue <= 30:
cnt++;
if cnt>=5:
detected = true;
cnt =0;
if cnt<5 && lastDecttime>2 (2 sec):
cnt = 0; // Here we handle the false value and clear the data
I'm currently trying to work this exact problem out. I can get the sensor to work using the Arduino IDE directly, but not with python and pyfirmata. I am getting some output, but its mostly non-sensical.
Here's an example output I'm getting, while keeping the sensor at the same distance from my object:
817.1010613441467
536.828875541687
0.0
546.0820078849792
0.0
0.0
1060.0213408470154
Regarding your code, the only thing I can see that you could do differently is to use the board.pass_time function instead of time.sleep(). Let me know if you get anywhere!
import pyfirmata as pyf
import time
def ultra_test():
board = pyf.Arduino("COM10")
it = pyf.util.Iterator(board)
it.start()
trigpin = board.get_pin("d:7:o")
echopin = board.get_pin("d:8:i")
while True:
trigpin.write(0)
board.pass_time(0.5)
trigpin.write(1)
board.pass_time(0.00001)
trigpin.write(0)
limit_start = time.time()
while echopin.read() != 1:
if time.time() - limit_start > 1:
break
pass
start = time.time()
while echopin.read() != 0:
pass
stop = time.time()
time_elapsed = stop - start
print((time_elapsed) * 34300 / 2)
board.pass_time(1)

Python send email when sensor has certain water level

I am developing an application on a raspberry pi with an HC-SR01 sensor. For now I have a python script that checks the water level every second.
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO_TRIGGER = 16
GPIO_ECHO = 18
GPIO.setup(GPIO_TRIGGER, GPIO.OUT)
GPIO.setup(GPIO_ECHO, GPIO.IN)
def distance():
# set Trigger High
GPIO.output(GPIO_TRIGGER, True)
# set Trigger after 0.1ms low
time.sleep(0.00001)
GPIO.output(GPIO_TRIGGER, False)
startTime = time.time()
endTime = time.time()
# store start time
while GPIO.input(GPIO_ECHO) == 0:
startTime = time.time()
# store arrival
while GPIO.input(GPIO_ECHO) == 1:
endTime = time.time()
# elapsed time
TimeElapsed = endTime - startTime
# multiply with speed of sound (34300 cm/s)
# and division by two
distance = (TimeElapsed * 34300) / 2
return distance
while True:
dist = distance()
print ("Entfernung = %.1f cm" % dist)
time.sleep(1)
This is working fine and now I want to send an E-Mail to myself then the water level is above a certain limit. The sending of the E-Mail is not the problem but the logic when to send it.
If I put a sendEmail() function in the while loop I would get an E-Mail every second when the water level is reached. So the following wouldn't do it:
#global varaible
alarm_waterlevel = 170
#in while loop
if dist > alarm_waterlevel
sendMail()
So I'm looking for a clever solution to only send the mail once the water level is reached.
I was thinking about a global variable and check if the water level has dropped below a certain point before triggering the mail again. Something like this:
#global varaible
alarm_waterlevel = 170
mail_sent = false
#in while loop
if dist >= alarm_waterlevel && mail_sent == false
sendMail()
mail_sent = true
if dist <= alarm_waterlevel - 10
mail_sent = false
Do you think this is a fault tolerant solution? Any good advices out there to help me get around with this?
Your approach looks reasonable to me, for a Raspberry Pi solution ;)
Another thing that you could think of is adding maybe three different warning levels or states that your system can be in.
L1: send once, 80% full
L2: send every 5 minutes, 90 % full
L3: send every minute, > 100 %
And you have to think of when to change the states. You could add something like a hysteresis. That could be elegant so you don't have to use three different height levels.
If the water level increases you could switch the state, e.g. at 80 % but only switch back to a lower warning state when it is below 70 %. The idea is that your system is not constantly changing states when the water level is between 78 % and 82 %.
Another thing that you could think of is like a projection by looking at the derivative (first or maybe additionally second) of the water level. Then you could warn even earlier when the water level is rising quickly but it is still below a critical threshold.
I think you have a general logic problem in your if statement.
You want to send an e-mail when the distance is greater than or equal to your alert level.
I think your ultrasonic sensor measures from top to bottom, so your if statement should be:
size_water_container = 200 #Just a estimadet value
if dist < (size_water_container - alarm_waterlevel) and waterlevel_before > (size_water_container - alarm_waterlevel):
sendMail()
If i am wrong, than ignore it :)
But now to your Question.
If you permanently query the distance, than you have to permanently save your waterlevel_before, but not in the if statement. Just do it afterwards
alarm_waterlevel = 170
size_water_container = 200
waterlevel_before = 0
while True:
dist = distance()
if dist < (size_water_container - alarm_waterlevel) and waterlevel_before > (size_water_container - alarm_waterlevel):
sendMail()
waterlevel_before = dist
time.sleep(1)

Accurate sleep/delay within Python while loop

I have a while True loop which sends variables to an external function, and then uses the returned values. This send/receive process has a user-configurable frequency, which is saved and read from an external .ini configuration file.
I've tried time.sleep(1 / Frequency), but am not satisfied with the accuracy, given the number of threads being used elsewhere. E.g. a frequency of 60Hz (period of 0.0166667) is giving an 'actual' time.sleep() period of ~0.0311.
My preference would be to use an additional while loop, which compares the current time to the start time plus the period, as follows:
EndTime = time.time() + (1 / Frequency)
while time.time() - EndTime < 0:
sleep(0)
This would fit into the end of my while True function as follows:
while True:
A = random.randint(0, 5)
B = random.randint(0, 10)
C = random.randint(0, 20)
Values = ExternalFunction.main(Variable_A = A, Variable_B = B, Variable_C = C)
Return_A = Values['A_Out']
Return_B = Values['B_Out']
Return_C = Values['C_Out']
#Updated other functions with Return_A, Return_B and Return_C
EndTime = time.time() + (1 / Frequency)
while time.time() - EndTime < 0:
time.sleep(0)
I'm missing something, as the addition of the while loop causes the function to execute once only. How can I get the above to function correctly? Is this the best approach to 'accurate' frequency control on a non-real time operating system? Should I be using threading for this particular component? I'm testing this function on both Windows 7 (64-bit) and Ubuntu (64-bit).
If I understood your question correctly, you want to execute ExternalFunction.main at a given frequency. The problem is that the execution of ExternalFunction.main itself takes some time. If you don't need very fine precision -- it seems that you don't -- my suggestion is doing something like this.
import time
frequency = 1 # Hz
period = 1.0/frequency
while True:
time_before = time.time()
[...]
ExternalFunction.main([...])
[...]
while (time.time() - time_before) < period:
time.sleep(0.001) # precision here
You may tune the precision to your needs. Greater precision (smaller number) will make the inner while loop execute more often.
This achieves decent results when not using threads. However, when using Python threads, the GIL (Global Interpreter Lock) makes sure only one thread runs at a time. If you have a huge number of threads it may be that it is taking way too much time for the program to go back to your main thread. Increasing the frequency Python changes between threads may give you more accurate delays.
Add this to the beginning of your code to increase the thread switching frequency.
import sys
sys.setcheckinterval(1)
1 is the number of instructions executed on each thread before switching (the default is 100), a larger number improves performance but will increase the threading switching time.
You may want to try python-pause
Pause until a unix time, with millisecond precision:
import pause
pause.until(1370640569.7747359)
Pause using datetime:
import pause, datetime
dt = datetime.datetime(2013, 6, 2, 14, 36, 34, 383752)
pause.until(dt)
You may use it like:
freqHz=60.0
td=datetime.timedelta(seconds=1/freqHz)
dt=datetime.now()
while true:
#Your code here
dt+=td
pause.until(dt)
Another solution for an accurate delay is to use the perf_counter() function from module time. Especially useful in windows as time.sleep is not accurate in milliseconds. See below example where function accurate_delay creates a delay in milliseconds.
import time
def accurate_delay(delay):
''' Function to provide accurate time delay in millisecond
'''
_ = time.perf_counter() + delay/1000
while time.perf_counter() < _:
pass
delay = 10
t_start = time.perf_counter()
print('Wait for {:.0f} ms. Start: {:.5f}'.format(delay, t_start))
accurate_delay(delay)
t_end = time.perf_counter()
print('End time: {:.5f}. Delay is {:.5f} ms'.
format(t_end, 1000*(t_end - t_start)))
sum = 0
ntests = 1000
for _ in range(ntests):
t_start = time.perf_counter()
accurate_delay(delay)
t_end = time.perf_counter()
print('Test completed: {:.2f}%'.format(_/ntests * 100), end='\r', flush=True)
sum = sum + 1000*(t_end - t_start) - delay
print('Average difference in time delay is {:.5f} ms.'.format(sum/ntests))`

Single Ultrasonic Sensor for Raspberry pi 2B+ not functioning from Pi Terminal

I have been working with 4-pin HC-SRO4 ultrasonic sensors, up to four at a time. I have been developing code to make 4 of these sensors work simultaneously and after reorganizing wires for installation on a project and using the basic code to run one, I cannot make the sensor function. Code follows:
import RPi.GPIO as GPIO
import time
TRIG1 = 15
ECHO1 = 13
start1 = 0
stop1 = 0
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO.setup(TRIG1, GPIO.OUT)
GPIO.output(TRIG1, 0)
GPIO.setup(ECHO1, GPIO.IN)
while True:
time.sleep(0.1)
GPIO.output(TRIG1, 1)
time.sleep(0.00001)
GPIO.output(TRIG1, 0)
while GPIO.input(ECHO1) == 0:
start1 = time.time()
print("here")
while GPIO.input(ECHO1) == 1:
stop1 = time.time()
print("also here")
print("sensor 1:")
print (stop1-start1) * 17000
GPIO.cleanup()
After changing wires, sensors, and other components within the circuit (including the GPIO pins) I have looked at the code, and added print statements to the terminal to see which parts of the code are running. The 1st print statement
print("here")
executes consistently, but the second print statementprint("also here") does not, and I am at a loss for an explanation. In other words, why is the second while loop not being executed? Other questions asked here have not worked for my problem. Any help would be greatly appreciated.
Thanks,
H.
Here is a tutorial by Gaven MacDonald that might help with this : https://www.youtube.com/watch?v=xACy8l3LsXI
First of all, the while block with ECHO1 == 0 would loop forever until the ECHO1 becomes 1. In this period of time, the code inside would be executed again and again. You wouldn't want to set the time again and again, so you can do something like this:
while GPIO.input(ECHO1) == 0:
pass #This is here to make the while loop do nothing and check again.
start = time.time() #Here you set the start time.
while GPIO.input(ECHO1) == 1:
pass #Doing the same thing, looping until the condition is true.
stop = time.time()
print (stop - start) * 170 #Note that since both values are integers, python would multiply the value with 170. If our values were string, python would write the same string again and again: for 170 times.
Also, just as a best practice, you should use try except blocks to safely exit the code. Such as:
try:
while True:
#Code code code...
except KeyboardInterrupt: #This would check if you have pressed Ctrl+C
GPIO.cleanup()

Implement sub millisecond processing in python without busywait

How would i implement processing of an array with millisecond precision using python under linux (running on a single core Raspberry Pi).
I am trying to parse information from a MIDI file, which has been preprocessed to an array where each millisecond i check if the array has entries at the current timestamp and trigger some functions if it does.
Currently i am using time.time() and employ busy waiting (as concluded here). This eats up all the CPU, therefor i opt for a better solution.
# iterate through all milliseconds
for current_ms in xrange(0, last+1):
start = time()
# check if events are to be processed
try:
events = allEvents[current_ms]
# iterate over all events for this millisecond
for event in events:
# check if event contains note information
if 'note' in event:
# check if mapping to pin exists
if event['note'] in mapping:
pin = mapping[event['note']]
# check if event contains on/off information
if 'mode' in event:
if event['mode'] == 0:
pin_off(pin)
elif event['mode'] == 1:
pin_on(pin)
else:
debug("unknown mode in event:"+event)
else:
debug("no mapping for note:" + event['note'])
except:
pass
end = time()
# fill the rest of the millisecond
while (end-start) < (1.0/(1000.0)):
end = time()
where last is the millisecond of the last event (known from preprocessing)
This is not a question about time() vs clock() more about sleep vs busy wait.
I cant really sleep in the "fill rest of millisecond" loop, because of the too low accuracy of sleep(). If i were to use ctypes, how would i go about it correctly?
Is there some Timer library which would call a callback each millisecond reliably?
My current implementation is on GitHub. With this approach i get a skew of around 4 or 5ms on the drum_sample, which is 3.7s total (with mock, so no real hardware attached). On a 30.7s sample, the skew is around 32ms (so its at least not linear!).
I have tried using time.sleep() and nanosleep() via ctypes with the following code
import time
import timeit
import ctypes
libc = ctypes.CDLL('libc.so.6')
class Timespec(ctypes.Structure):
""" timespec struct for nanosleep, see:
http://linux.die.net/man/2/nanosleep """
_fields_ = [('tv_sec', ctypes.c_long),
('tv_nsec', ctypes.c_long)]
libc.nanosleep.argtypes = [ctypes.POINTER(Timespec),
ctypes.POINTER(Timespec)]
nanosleep_req = Timespec()
nanosleep_rem = Timespec()
def nsleep(us):
#print('nsleep: {0:.9f}'.format(us))
""" Delay microseconds with libc nanosleep() using ctypes. """
if (us >= 1000000):
sec = us/1000000
us %= 1000000
else: sec = 0
nanosleep_req.tv_sec = sec
nanosleep_req.tv_nsec = int(us * 1000)
libc.nanosleep(nanosleep_req, nanosleep_rem)
LOOPS = 10000
def do_sleep(min_sleep):
#print('try: {0:.9f}'.format(min_sleep))
total = 0.0
for i in xrange(0, LOOPS):
start = timeit.default_timer()
nsleep(min_sleep*1000*1000)
#time.sleep(min_sleep)
end = timeit.default_timer()
total += end - start
return (total / LOOPS)
iterations = 5
iteration = 1
min_sleep = 0.001
result = None
while True:
result = do_sleep(min_sleep)
#print('res: {0:.9f}'.format(result))
if result > 1.5 * min_sleep:
if iteration > iterations:
break
else:
min_sleep = result
iteration += 1
else:
min_sleep /= 2.0
print('FIN: {0:.9f}'.format(result))
The result on my i5 is
FIN: 0.000165443
while on the RPi it is
FIN: 0.000578617
which suggest a sleep period of about 0.1 or 0.5 milliseconds, with the given jitter (tend to sleep longer) that at most helps me reduce the load a little bit.
One possible solution, using the sched module:
import sched
import time
def f(t0):
print 'Time elapsed since t0:', time.time() - t0
s = sched.scheduler(time.time, time.sleep)
for i in range(10):
s.enterabs(t0 + 10 + i, 0, f, (t0,))
s.run()
Result:
Time elapsed since t0: 10.0058200359
Time elapsed since t0: 11.0022959709
Time elapsed since t0: 12.0017120838
Time elapsed since t0: 13.0022599697
Time elapsed since t0: 14.0022521019
Time elapsed since t0: 15.0015859604
Time elapsed since t0: 16.0023040771
Time elapsed since t0: 17.0023028851
Time elapsed since t0: 18.0023078918
Time elapsed since t0: 19.002286911
Apart from some constant offset of about 2 millisecond (which you could calibrate), the jitter seems to be on the order of 1 or 2 millisecond (as reported by time.time itself). Not sure if that is good enough for your application.
If you need to do some useful work in the meantime, you should look into multi-threading or multi-processing.
Note: a standard Linux distribution that runs on a RPi is not a hard real-time operating system. Also Python can show non-deterministic timing, e.g. when it starts a garbage collection. So your code might run fine with low jitter most of the time, but you might have occasional 'hickups', where there is a bit of delay.

Categories