Terminate a process from another process - python

I am trying to make an LCD display to display both the current temperature and the humidity at the same time in different lines. I have a sample code as below. The problem is that when the temperature goes above say 50C, I want the humidity reading to stop as well. I have 2 thread running at the same time, reading from both sensors.
#all important imports are here
def main():
# Very simplified code, not actual.
tempP = Process(target=temperature)
humidP = Process(target=humidity)
tempP.start()
humidP.start()
def temperature():
while True:
#ADC printing to LCD code is here
temperatureReading = ADC.channel(0)
if temperatureReading > 50:
humidP.terminate() # Does not work, I still see the humidity on
the screen with overlapping characters

Related

HC-SR04 gets stuck (Python and Raspberry Pi)

I'm trying to use the HC-SR04 ultrasonic sensor on a Raspberry Pi 3 with Python. I got the majority the code from this site. However, When I reduce the sleep time between each measurement of distance, The code gets stuck in the while GPIO.input(ECHO)==0: loop and then that stops everything. Maybe I'm setting the sleep time too low, But I really don't see how that would change anything.
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
TRIG=23
ECHO=24
GPIO.setup(TRIG,GPIO.OUT)
GPIO.setup(ECHO,GPIO.IN)
def pulseIn():
while True:
GPIO.output(TRIG, False)
time.sleep(.000005)
GPIO.output(TRIG, True) # Sending out a trigger pulse for 10 microseconds
time.sleep(.00001)
GPIO.output(TRIG, False)
pulse_start = time.time()
pulse_end = time.time()
while GPIO.input(ECHO)==0: # This is where it keeps getting stuck
pulse_start = time.time()
#if pulse_start-pulse_end>.5: # This is one of my attempts to fix the problem.
# It caused some bad output values
# continue
while GPIO.input(ECHO)==1:
pulse_end = time.time()
return pulse_end-pulse_start
time.sleep(2) # Giving the sensor some time to warm up
for i in range(100):
print(pulseIn()*17500)
time.sleep(.001) # It seems to work when I set this to 1, but I would prefer if it worked faster
Raspberry pi, unlike Arduino or other microcontroller platform, is a computer based on a Microprocessor and therefore has an operating system running on it to control software/hardware aspects of its operation. Multitasking Operating systems have schedulers that essentially provides all running processes, a chunk of hardware(cpu) to process instructions.
The caveat that comes with this is that, an instruction may not be executed immediately if the scheduler gives priority to other processes.
The following chunk of code would likely cause issues since getting the pulse is a time sensitive operation.
while GPIO.input(ECHO)==0:
pulse_start = time.time()
while GPIO.input(ECHO)==1:
pulse_end = time.time()
return pulse_end-pulse_start
Your loop would likely run infinitely if the sensor is done sending the low pulse and process start reading it then.
You could also get wildly incorrect values attributing to such delays.
So, what's the solution ?
If I were you, I would use a microcontroller to read the values from the sensor and then read the recorded data from it using I2c or any other communication protocol supported.
However you could also add a counter in your while loops to handle such cases like it is described here:
https://raspberrypi.stackexchange.com/questions/82833/hc-sr04-randomly-stops-taking-readings
From the datasheet we can see that this sensor has a maximum distance range of 4 meters. Hence, as you decrease the delay between two consecutive measurements, you might be sending another ultrasonic signal from the sensor when the previous signal has not arrived. Datasheet recommends 60 ms measurement cycle for this sensor. Your measurement cycle is much smaller than the recommended value.
Sensor datasheet:
https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf
This is a common problem with hc-src04. It stops working somehow. You should create another counter inside while loop. And then you should check that time to break inner while loop. Here is how I do it.
def distance(myStartingTime):
calculation_starting_time = time.time()
GPIO.output(trigger_pin,True)
time.sleep(0.00001)
GPIO.output(trigger_pin, False)
start = time.time()
end = time.time()
while GPIO.input(echo_pin) == 0:
calculation_starting_time = time.time()
if calculation_starting_time - myStartingTime > 1:
break
start = time.time()
while GPIO.input(echo_pin) == 1:
calculation_starting_time = time.time()
if calculation_starting_time - myStartingTime > 1:
break
end = time.time()
difference = end-start
distance = (difference*34300)/2
return distance
while True:
start = time.time()
my_distance = distance(start)
print("dist: ", my_distance)

Python random.randint stops randomizing after a few loops

I'm running a python script that will display messages on a board. One of the subroutines that I've created is supposed to grab a random line from a small text file, and display that line. It mostly works, except after looping a few times, it gets stuck on the same number, and just displays the same thing over and over.
I am running this in Python 2.7, on a Raspberry Pi in Raspbian. I am using this github as the base for the project, and added lines of my own to it:
https://github.com/CalebKussmaul/Stranger-Things-Integrated
This is part of a halloween display that will be Stranger Things-themed, so the preloaded messages have a reference to the show. I noticed this issue the other day, and have been pouring over the internet to try and figure out what the problem could be. I've tried doing different methods of selecting a randomized number, including some in some similar (but different) threads on this site. All of them produce exactly the same issue.
Below is the subroutine I created:
def preloaded_messages():
print "Preloaded Messages thread is loaded."
global displaying
while True:
if not displaying:
with open('preloaded_messages.txt') as f:
lines = len(f.readlines())
rgn = random.randint(1,lines)
msg = linecache.getline('preloaded_messages.txt', rgn)
print "rng: ", rgn
print "total lines: ", lines
print "line: ", msg
print "displaying from preloaded_messages.txt: ", msg
display(msg)
time.sleep(10)
And here's my preloaded_messages.txt file:
help me
im trapped in the upside down
leggo my eggo
friends dont lie
run /!
hopper is alive
rip barb
demogorgon is coming /!
mouthbreather
When I run it, my output is like this:
rng: 6
total lines: 9
line: hopper is alive
rng: 2
total lines: 9
line: im trapped in the upside down
rng: 9
total lines: 9
line: mouthbreather
...
rng: 9
total lines: 9
line: mouthbreather
the first few times are always random (and the number of times it successfully randomizes varies), but when it gets on 9, it just stays there for as long as I let it run. I am at a loss as to why it works the first few times, but not once it gets to 9.
EDIT: Interestingly, as I've been writing this, I also tried adding a blank line at the end, and while it looked like it'd be stuck again, as it did that one three times in a row, then it finally moved to others. I'm not sure how that changes things. And ideally, I'd rather not have the blank line in there, as it eats up time displaying nothing. So it'd be nice to fix the issue. Anyone have any ideas?
It is reseeding the random generator. See line 49 of stranger.py in the https://github.com/CalebKussmaul/Stranger-Things-Integrated: random.seed(i).
The color_of function should be written as:
def color_of(i):
"""
This function generates a color based on the index of an LED. This will always return the same color for a given
index. This allows the lights to function more like normal christmas lights where the color of one bulb wont change.
:param i: index of LED to get color of
:return: a pseudorandom color based on the index of the light
"""
_random = random.Random(i)
rgb = colorsys.hsv_to_rgb(_random.random(), 1, 1)
return int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255)
To create its own Random instance with the given seed rather than reseeding the Random instance that is a singleton in the random module.
This appears to work for me. Note that I'm seeding the RNG.
import time
import random
from datetime import datetime
def preloaded_messages():
print("Preloaded Messages thread is loaded.")
displaying = False
while True:
if not displaying:
with open('preloaded_messages.txt') as f:
random.seed(datetime.utcnow())
text = f.read().splitlines()
msg = random.choice(text)
print("line: ", msg)
# print("displaying from preloaded_messages.txt: ", msg)
time.sleep(10)
if __name__ == "__main__":
preloaded_messages()

jack audio - sync application to master

I'm wondering about the correct way of keeping my application in sync with a timebase master application using the jack audio api.
Let's say I have Hydrogen drum machine running in master mode and I want to print a message on every 1/4 note Hydrogen is playing.
This is what I would do intuitive (using python):
#!/usr/bin/env python3
import time
import jack
client = jack.Client('klicker')
def print_msg (last_tick):
state, pos = client.transport_query()
if state == jack.ROLLING:
if pos['tick'] < last_tick:
print ("klick")
return pos['tick']
with client:
last_tick = 0
while True:
last_tick = print_msg (last_tick)
time.sleep(0.00002)
So I'm running a loop with very little sleep time and check in every iteration if the current beat is already over.
This seems a little bit dirty and imprecise to me. So what would the right way of solving this problem?
Finally, I found a solution which semms to be more precise:
First, we want to use the process callback instead of an infinite loop.
def process (f):
Every time this callback is called, f number of frames are processed. The sample rate tells us how many frames we will process in 1 second. By multiplying this with 60 and dividing by the number of beats per minute we get the number of frames which are processed in one beat.
#!/usr/bin/env python3
import jack
client = jack.Client('Klick')
frame_counter = 0
#client.set_process_callback
def process (f):
global frame_counter
state, pos = client.transport_query()
if state == jack.ROLLING:
frame_counter += f
# how many frames are get processed in 1 beat?
frames_per_beat = client.samplerate * 60 / pos['beats_per_minute']
# did we process enough frames for 1 beat?
if frame_counter >= frames_per_beat:
print ('klick')
frame_counter -= frames_per_beat
with client:
client.activate()
input()

Python, RPi + MCP3008 + 2xForce Sensitive Resistor - Square

I have a MCP3008 connected to RPi and 2 x Force Sensitive Resistor - Square connected to the MCP3008. The sensors are laying side by side horisontal on my desk and I can read and print the data (0-100) from both sensors (sensor1 and sensor2) separately. I just can't come up with any idea of python code to detect when I touch sensor1 (left) and move my finger to sensor2 (right).
In addition I need to know how many milliseconds it takes from that I touch sensor1 until I lifted from sensor2.
from time import sleep
from gpiozero import MCP3008
sensor1 = MCP3008(1) # Pin 2 on the ADC
sensor2 = MCP3008(2) # Pin 3 on the ADC
# Read data from the ADC
def getData(readSensor):
value = readSensor
rawValue = value.value
return rawValue
while True:
print('Sensor1 = {0:.0f}'.format(getData(sensor1)*100))
print('Sensor2 = {0:.0f}'.format(getData(sensor2)*100))
print('')
sleep(0.1)
You are going to need to timestamp the events and then use the time stamps to determine what action to take. Something like the below might help you.
if getData(sensor1) > TOUCH_THRESHOLD:
sensor1LastPressedAt = time.time()
Do the same for sensor2 and compare the time stamps.
(TOUCH_THRESHOLD is the value that you measure to be someone touching the button)

How can I read a string from a file, convert it to int, store the value in memory and then access the value and print it on screen?

I need to read a temperature reading from a DS18B20 sensor using Raspberry Pi 3 and Python.
The problem is the refresh rate of the sensor (~1 sec)
I need to read from sys/bus/w1/devices/28-041670f43bff/w1_slave and use the integer i get to display a temperature on a 7 segment display connected directly to my GPIOs (not using any hardware multiplexing - i2c....etc)
In order to display a two digit temperature, I need to turn on and off the digits really fast (faster than the sensor refreshes)
This is the small piece of code used to get the integer temperature:
def temperature():
with open ("/sys/bus/w1/devices/28-041670f43bff/w1_slave") as q:
r=q.read()
temp=r[69:71]
t=int (temp)
return t
But i need to call this function many times per second in order to get a good display on the 7 segment display.
This is how i thought of doing it:
#the temperature() function returns a two digit int
while True:
GPIO.output(31,0)
GPIO.output(temp[temperature()/10], 1) # temp is a dictionary used to know which segments to light up to show numbers
time.sleep(0.0005)
GPIO.output(31,1)
GPIO.output(37,0)
GPIO.output(temp[temperature()%10], 1)
time.sleep(0.0005)
GPIO.output(37,1)
But this code just makes one digit light up, wait ~1sec, light up the other digit, wait ~1sec.....and so on.
Any ideas of how to do this are very appreciated.
Rather than implement this functionality on your own, you should instead use the libraries out there that address this particular bit of your code inherently. In this case, I'd suggest you use W1ThermSensor. You can find the documentation at:
https://github.com/timofurrer/w1thermsensor
and you can install it using:
pip install w1thermsensor
It does support the DS18B20, and offers an exact analogue to your use case in the README.
From the docs for the package:
from w1thermsensor import W1ThermSensor
sensor = W1ThermSensor()
temperature_in_celsius = sensor.get_temperature()
temperature_in_fahrenheit = sensor.get_temperature(W1ThermSensor.DEGREES_F)
temperature_in_all_units = sensor.get_temperatures([
W1ThermSensor.DEGREES_C,
W1ThermSensor.DEGREES_F,
W1ThermSensor.KELVIN
])
In many cases, particularly for popular hardware devices, you'll find that there are libraries already available to use within python, and that will all you to quickly move on to writing the bits of code unique to your own particular needs.
Note: According to the technical discussion in the following link, if the DS18B20 is set to 12-bit temperature resolution, the temperature conversion will take 750 ms, or 3/4 of a second. If you set the hardware to do 9-bit resolution, the conversion time in hardware is 93.75 ms. I suspect this is the root of your once-per-second issue.
https://www.maximintegrated.com/en/app-notes/index.mvp/id/4377
There is some discussion of this issue in this Question:
https://raspberrypi.stackexchange.com/questions/14278/how-to-change-ds18b20-reading-resolution
See the second Answer, regarding the configDS18B20 utility.
With the resolution set to 9-bit, you may be able to adjust the w1thermsensor RETRY_DELAY_SECONDS / RETRY_ATTEMPTS value combination in the source code and get what you need. It's unclear to me if the retry delay has any affect on the actual polling of the device. It looks like it is there for device finding. Though, as I said, that interval may impact polling a single device. I simply didn't read through the source code enough to see when and where it comes into play.
Happy New Year!
I'd throw the display routine into its own thread so that you don't have to think about it in your main loop. The code below should demonstrate this concept. Set "testing" to False to see if it works with your hardware.
#!/usr/bin/python
import time
import threading
import Queue
import random
# Set this to False to read the temperature from a real sensor and display it on a 7-digit display.
testing = True
def temperature_read(q):
# Read the temperature at one second intervals.
while True:
if testing:
r = '-' * 69 + '%02d' % (random.randrange(100)) + 'blahblah' * 4
else:
r = open('/sys/bus/w1/devices/28-041670f43bff/w1_slave', 'r').read()
print r
# The temperature is represented as two digits in a long string.
# Push the digits into the queue as a tuple of integers (one per digit).
q.put((int(r[69]), int(r[70])))
# Wait for next reading.
# (Will w1_slave block until the next reading? If so, this could be eliminated.)
time.sleep(1.0)
def temperature_display(q):
# Display the temperature.
# Temperature is two digits, stored separately (high/low) for more efficient handling.
temperature_h = temperature_l = 0
while True:
# Is there a new temperature reading waiting for us?
if not q.empty():
temperature = q.get()
# If it's None, we're done.
if temperature is None:
break
# Load the two digits (high and low) representing the temperature.
(temperature_h, temperature_l) = temperature
if testing:
print 'displayH', temperature_h
time.sleep(0.05)
print 'displayL', temperature_l
time.sleep(0.05)
else:
GPIO.output(31,0)
GPIO.output(temperature_h, 1) # temp is a dictionary used to know which segments to light up to show numbers
time.sleep(0.0005)
GPIO.output(31,1)
GPIO.output(37,0)
GPIO.output(temperature_l, 1)
time.sleep(0.0005)
GPIO.output(37,1)
# Clean up here. Turn off all pins?
# Make a queue to communicate with the display thread.
temperature_queue = Queue.Queue()
# Run the display in a separate thread.
temperature_display_thread = threading.Thread(target=temperature_display, args=(temperature_queue,))
temperature_display_thread.start()
# Run the reader.
try:
temperature_read(temperature_queue)
except:
# An uncaught exception happened. (It could be a keyboard interrupt.)
None
# Tell the display thread to stop.
temperature_queue.put(None)
# Wait for the thread to end.
temperature_display_thread.join()
To support another reading (transmission), I just put it in the read loop rather than adding another thread for it. I changed the queue so that you could easily move it to another thread but I suspect you'll add more inputs so this is probably a reasonable way to do it unless the read frequency of one needs to be much different. (Even then, you could do things with counters in the loop.)
#!/usr/bin/python
import time
import threading
import Queue
import random
# Set this to False to read the temperature from a real sensor and display it on a 7-digit display.
testing = True
def observe(q):
while True:
# Make a temperature reading.
if testing:
r = '-' * 69 + '%02d' % (random.randrange(100)) + 'blahblah' * 4
else:
r = open('/sys/bus/w1/devices/28-041670f43bff/w1_slave', 'r').read()
print 'temperature ->', r
# The temperature is represented as two digits in a long string.
# Push the digits into the queue as a tuple of integers (one per digit).
q.put(('temperature', int(r[69]), int(r[70])))
# Make a transmission reading.
if testing:
r = random.randrange(1,6)
else:
r = 0 # Put your transmission reading code here.
print 'transmission ->', r
q.put(('transmission', r))
# Wait for next reading.
# (Will w1_slave block until the next reading? If so, this could be eliminated.)
time.sleep(1.0)
def display(q):
# Display the temperature.
# Temperature is two digits, stored separately (high/low) for more efficient handling.
temperature_h = temperature_l = transmission = 0
while True:
# Is there a new temperature reading waiting for us?
if not q.empty():
reading = q.get()
# If it's None, we're done.
if reading is None:
break
elif reading[0] == 'temperature':
# Load the two digits (high and low) representing the temperature.
(x, temperature_h, temperature_l) = reading
elif reading[0] == 'transmission':
(x, transmission) = reading
if testing:
print 'displayH', temperature_h
time.sleep(0.05)
print 'displayL', temperature_l
time.sleep(0.05)
print 'transmission', transmission
time.sleep(0.05)
else:
GPIO.output(31,0)
GPIO.output(temperature_h, 1) # temp is a dictionary used to know which segments to light up to show numbers
time.sleep(0.0005)
GPIO.output(31,1)
GPIO.output(37,0)
GPIO.output(temperature_l, 1)
time.sleep(0.0005)
GPIO.output(37,1)
# Clean up here. Turn off all pins?
# Make a queue to communicate with the display thread.
readings_queue = Queue.Queue()
# Run the display in a separate thread.
display_thread = threading.Thread(target=display, args=(readings_queue,))
display_thread.start()
# Observe the inputs.
try:
observe(readings_queue)
except:
# An uncaught exception happened. (It could be a keyboard interrupt.)
None
# Tell the display thread to stop.
readings_queue.put(None)
# Wait for the thread to end.
display_thread.join()
Here's a version which only reads the temperature every tenth time but reads the transmission every time. I think you'll see how to easily tweak this to meet your needs.
I would make separate threads for each reader but it would complicate the thread management quite a bit.
#!/usr/bin/python
import time
import threading
import Queue
import random
# Set this to False to read the temperature from a real sensor and display it on a 7-digit display.
testing = True
def observe(q):
count = 0
while True:
# Only read the temperature every tenth time.
if (count % 10 == 0):
# Make a temperature reading.
if testing:
r = '-' * 69 + '%02d' % (random.randrange(100)) + 'blahblah' * 4
else:
r = open('/sys/bus/w1/devices/28-041670f43bff/w1_slave', 'r').read()
print 'temperature ->', r
# The temperature is represented as two digits in a long string.
# Push the digits into the queue as a tuple of integers (one per digit).
q.put(('temperature', int(r[69]), int(r[70])))
# Make a transmission reading.
if testing:
r = random.randrange(1,6)
else:
r = 0 # Put your transmission reading code here.
print 'transmission ->', r
q.put(('transmission', r))
# Wait for next reading.
if testing:
time.sleep(0.5)
else:
time.sleep(0.1)
count += 1
def display(q):
# Display the temperature.
# Temperature is two digits, stored separately (high/low) for more efficient handling.
temperature_h = temperature_l = transmission = 0
while True:
# Is there a new temperature reading waiting for us?
if not q.empty():
reading = q.get()
# If it's None, we're done.
if reading is None:
break
elif reading[0] == 'temperature':
# Load the two digits (high and low) representing the temperature.
(x, temperature_h, temperature_l) = reading
elif reading[0] == 'transmission':
(x, transmission) = reading
if testing:
print 'displayH', temperature_h
time.sleep(0.05)
print 'displayL', temperature_l
time.sleep(0.05)
print 'transmission', transmission
time.sleep(0.05)
else:
GPIO.output(31,0)
GPIO.output(temperature_h, 1) # temp is a dictionary used to know which segments to light up to show numbers
time.sleep(0.0005)
GPIO.output(31,1)
GPIO.output(37,0)
GPIO.output(temperature_l, 1)
time.sleep(0.0005)
GPIO.output(37,1)
# Clean up here. Turn off all pins?
# Make a queue to communicate with the display thread.
readings_queue = Queue.Queue()
# Run the display in a separate thread.
display_thread = threading.Thread(target=display, args=(readings_queue,))
display_thread.start()
# Observe the inputs.
try:
observe(readings_queue)
except:
# An uncaught exception happened. (It could be a keyboard interrupt.)
None
# Tell the display thread to stop.
readings_queue.put(None)
# Wait for the thread to end.
display_thread.join()

Categories

Resources