Use getch in while (1) python - python

I would like to use char=getch.getch() in a loop (while(1)).
The problem when I use this like that it block my loop:
import getch
while(1):
char=getch.getch()
a=read_data()
if (char=='a'): c=....
if (char=='b'): c=....
If I don't put anything, my loop is blocked... How can I solve this for getting event from my keyboard?
EDIT : At the top is an example of what I want to do but if you are interested, my real script is here. I am actually working on an analyser spectrum and I want to scan by pressing some keys:
from pylab import *
from rtlsdr import *
from bluetooth import *
import sys
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import getch
sdr=RtlSdr()
#configure device
sdr.center_freq=double(sys.argv[1]) # centrale frequency
sdr.gain=double(sys.argv[2]) #gain
sdr.sample_rate=double(sys.argv[3]) # sample rate
#configure PSD
NFFT=int(sys.argv[4]) # nb points
# Bluetooth connexion
server_sock=BluetoothSocket( RFCOMM )
server_sock.bind(("",PORT_ANY))
server_sock.listen(1)
port = server_sock.getsockname()[1]
uuid="94f39d29-7d6d-437d-973b-fba39e49d4ee"
client_sock, client_info=server_sock.accept()
while (1):
samples=sdr.read_samples(256*1024)
result=psd(samples,NFFT,Fs=sdr.sample_rate/1e6,Fc=sdr.center_freq*1e6/1e6)
tab_freq=(result[1]/1e6)
value_freq=str(tab_freq)[1:-1]
value_list=[format(float(v), ".5f") for v in value_freq.split()]
value_freq2= "\n".join(value_list)
tab_pxx=result[0]
value_pxx=str(tab_pxx)[1:-1]
value_list2=[format(float(v), ".7f") for v in value_pxx.split()]
value_pxx2= "\n".join(value_list2)
client_sock.send(value_freq2+'\n'+'\n'.join(value_pxx2.split()))
char=getch.getch()
if (char=='a'):
sdr.center_freq=sdr.center_freq+0.1e6
print 'center_freq+'
if (char=='z'):
sdr.center_freq=sdr.center_freq-0.1e6
print 'center_freq-'

If you are on Windows, you can use msvcrt.kbhit() to see if there's a keypress waiting without blocking:
import msvcrt
import time
while True:
time.sleep(1)
if msvcrt.kbhit():
# Only if there's a keypress waiting do we get it with getch()
print "Key hit! ({})".format(msvcrt.getch())
else:
# Do something else here
print "Nothing..."
On Linux, it's more complicated because there is no kbhit() equivalent.

Related

How to implement synchronization in multi threading with python and ROS Services?

Im trying to implement multi threading (parallel processing) with python and using mutex threading. I have first process that check the Pressure Value and the modem update(in the code implemented with odom_callback and callback_modem functions), and second process that calls ROS SERVICES ( in the code implemented with ros_serice_server server and imu_client client functions). Here is the implementation code in python
#!/usr/bin/env python3
from __future__ import print_function
import rospy
import numpy as np
from os import system
import time
import threading
import Microcontroller_Manager_Serial as Serial
import IMU_Functions as IMU
import Motors_Functions as Motor
import Pressure_Functions as Pressure
from geometry_msgs.msg import Vector3
import Modem_Functions as Modem
import threading
import time
import serial
import serial.tools.list_ports
from time import sleep
from std_msgs.msg import Float32
from std_msgs.msg import String
from demo_teleop.srv import ImuValue,ImuValueResponse
P0 = 1.01325 #Default Pressure
mutex = threading.Lock()
Communication_Mode_ = 0
pub_pressure = rospy.Publisher('depth',Vector3,queue_size=1)
pub_modem = rospy.Publisher('modem_data',Float32,queue_size=1)
def handle_ros_services(req):
mutex.acquire(blocking=True)
print("Server Read Data:")
global T0
data_received = Pressure.Pressure_Get_Final_Values(1,1)
#print("Server Read Data:")
T0 = (np.int16((data_received[6]<<24) | (data_received[7]<<16) | (data_received[8]<<8) | (data_received[9])))/10000
T=T0
temperature = T
current_x_orientation_s = temperature
print("Returning Service Temperature Data", current_x_orientation_s)
return ImuValueResponse(current_x_orientation_s, True)
mutex.release()
def ros_serice_server():
s = rospy.Service('imu_value', ImuValue, handle_ros_services)
print("Ready to get_value")
def odom_callback():
# reentrang processing
mutex.acquire(blocking=True)
# work serial port here, e.g. send msg to serial port
global P0
data_received = Pressure.Pressure_Get_Final_Values(1,1)
#P1 = (np.int16((data_received_pressure[6]<<24) | (data_received_pressure[7]<<16) | (data_received_pressure[8]<<8) | (data_received_pressure[9])))/10000
P1 = (np.int16((data_received[6]<<24) | (data_received[7]<<16) | (data_received[8]<<8) | (data_received[9])))/10000
#P0 = (np.int16((data_received_pressure[6]<<24) | (data_received_pressure[7]<<16) | (data_received_pressure[8]<<8) | (data_received_pressure[9])))/10000
P0 = (np.int16((data_received[6]<<24) | (data_received[7]<<16) | (data_received[8]<<8) | (data_received[9])))/10000
P = P0 # Relative Measured Pressure
feedback =Vector3()
feedback.x = 0 #Angular Velocity
feedback.y = 0
feedback.z = P/9.81 #Depth
pressure = feedback.z
print("Pressure : ", pressure)
pub_pressure.publish(feedback)
# reentrant processing
mutex.release()
def callback_modem(event):
# reentrant processing
mutex.acquire(blocking=True)
# work serial port here, e.g. check for incoming data
event = Serial.Serial_Port_Receive_Data(20,0.2)
if (event == 1): # Received data from acoustic modem
modem_data= event
pub_modem.publish(modem_data)
print("received ")
else:
print("not received...... ")
mutex.release()
if __name__ == '__main__':
# initialize serial port here
Serial.Serial_Port_Standard()
rospy.init_node('imu_value')
ros_serice_server()
rospy.Timer(rospy.Duration(1), callback_modem)
while not rospy.is_shutdown():
try:
odom_callback()
except:
print('pass')
And the client node
#!/usr/bin/env python3
from __future__ import print_function
import rospy
import sys
import numpy as np
from os import system
import threading
import Microcontroller_Manager_Serial as Serial
import IMU_Functions as IMU
import Motors_Functions as Motor
import Pressure_Functions as Pressure
from geometry_msgs.msg import Vector3
import Modem_Functions as Modem
import time
import serial
import serial.tools.list_ports
from time import sleep
from std_msgs.msg import Float32
from std_msgs.msg import String
from demo_teleop.srv import *
mutex = threading.Lock()
Communication_Mode_ = 0
pub_modem = rospy.Publisher('modem_data',Float32,queue_size=1)
def imu_client():
mutex.acquire(blocking=True)
rospy.wait_for_service('imu_value')
imu_value = rospy.ServiceProxy('imu_value', ImuValue)
print("Request call send")
resp1 = imu_value(0.05)
return resp1.current_x_orientation_s
mutex.release()
if __name__ == "__main__":
rospy.init_node('client_node_f')
while not rospy.is_shutdown():
try:
print("entering client")
value = imu_client()
print(value)
time.sleep(1)
except:
print('pass')
So the output is following. The output of the first process with the ROS Services Server is
Pressure : 0.10602446483180428
Server Read Data:
Returning Service Temperature Data 1.0401
And then after calling the client I got
entering client
Request call send
1.0401
entering client
The problem is that after calling the ROS SERVICE client node the process stop so doesn't continue with the first process (Pressure value and modem update) . The ROS SERVICES process should be call only on demand and should HALT the first process (Pressure and modem) and then is should resume with the work. So, do I need to implement SEMAPHORES for the ROS SERVICES call ? If yes how it should be in the code. So I do need kind of synchronization , right?Please any help?
Your problem is:
def handle_ros_services(req):
mutex.acquire(blocking=True)
...
return ImuValueResponse(current_x_orientation_s, True)
mutex.release()
Because of the return statement, the release is never executed.
You need at the end:
value = ImValueResponse(...)
mutex.release()
return value
Even better would be to use your mutex as part of a with statement:
with mutex:
do anything you want, knowing that the lock will be released
at the end, even if you return or throw an exception.

Detect key pressed in a loop

I am trying to detect the key pressed by a user in order to reset a variable for my program, a basic application to detect a barcode, which is running inside a While true
I have tried using keyboard library like in the first solution suggested here
import keyboard # using module keyboard
while True:
if keyboard.is_pressed('q'): # if key 'q' is pressed
print('You Pressed A Key!')
...
but I m getting an error You must be root to use this library on Linux. I am not sure if that it means is not possible to use this approach in my case because I m ruining the program on Raspberry Pi OS.
I also tried the second approach ( from the same link) using pynput.keyboard but as the author says, it does not perform well when using inside a loop. the application waits for a key to be pressed.
Edit : More Info
from pyzbar import pyzbar
import datetime
import imutils
import time
from imutils.video import VideoStream
import pika
import json
from pynput.keyboard import Key, Listener
connection = pika.BlockingConnection(
pika.ConnectionParameters(host="----",
virtual_host="----",
credentials=pika.credentials.PlainCredentials(
"---", "---")
))
channel = connection.channel()
channel.queue_declare(queue='barcode_queue')
print("[INFO] Connection Started ...")
vs = VideoStream(usePiCamera=True).start()
print("[INFO] starting video stream...")
time.sleep(2.0)
userWebsocket = None
while True:
# this is what I want to achive
# key = Get the key pressed
# if key == 'q' then userWebsocket = None
frame = vs.read()
frame = imutils.resize(frame, width=400)
barcodes = pyzbar.decode(frame)
for barcode in barcodes:
barcodeData = barcode.data.decode("utf-8")
barcodeType = barcode.type
if barcodeType == 'QRCODE':
decodedBarcode = json.loads(barcodeData)
print(decodedBarcode)
print(decodedBarcode['name'])
userWebsocket = decodedBarcode['id']
else:
print(datetime.datetime.now(), barcodeData, barcodeType)
if (userWebsocket is None):
print("please login before")
else:
sentObj = {"socket": userWebsocket, "barcode": barcodeData}
jsonSentObj = json.dumps(sentObj)
channel.basic_publish(exchange='', routing_key='barcode_queue', body=jsonSentObj)
time.sleep(5)
vs.stop()
What I want to achieve is to "sign-out" the user when he presses a certain key.

Run Script as parallel Subprocess

i'm pretty new to python, so my knowledge is quiet basic. (i'm a system engineer)
i have a raspberry pi, an led strip and a python script to simulate a fire on the led strip :D
now i want to start the script by pressing my flic button. i found the fliclib sdk on github and installed it. my problem is now, how to handle the event correctly. i successfully can start the script, but i'd like to stop it by doublepress the flic button. but it seems like i'm stuck in the fire.py script as soon as i press the button once. can anybody help me how to set this up correctly please? :-)
Edit after suggestion:
i just edited my scripts as the following. i can see when the button is pressed once or twice with this output:
Starting Fire
Stopping Fire
but the led wont turn on, seems like, fire.py isn't opened or something like that.. when i set button=1 in fire.py itself, the fire turns on.
main.py
#!/usr/bin/env /usr/bin/python3
# -*- coding: utf-8 -*-
import flicbutton
import fire
button = 0
flicbutton.py
#!/usr/bin/env /usr/bin/python3
# -*- coding: utf-8 -*-
import fliclib
client = fliclib.FlicClient("localhost")
MyButton1 = '80:e4:da:71:83:42' #turquoise flic button
def got_button(bd_addr):
cc = fliclib.ButtonConnectionChannel(bd_addr)
cc.on_button_single_or_double_click_or_hold = some_handler
cc.on_connection_status_changed = \
lambda channel, connection_status, disconnect_reason: \
print(channel.bd_addr + " " + str(connection_status) + (" " + str(disconnect_reason) if connection_status == fliclib.ConnectionStatus.Disconnected else ""))
client.add_connection_channel(cc)
def got_info(items):
print(items)
for bd_addr in items["bd_addr_of_verified_buttons"]:
got_button(bd_addr)
def some_handler(channel, click_type, was_queued, time_diff):
if channel.bd_addr == MyButton1:
try:
if click_type == fliclib.ClickType.ButtonSingleClick:
print("Starting Fire")
button=1
if click_type == fliclib.ClickType.ButtonDoubleClick:
print("Stopping Fire")
button=2
if click_type == fliclib.ClickType.ButtonHold:
print("ButtonHold has not been assigned an action")
except Exception:
import datetime
print('An error occured: {:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now()))
client.get_info(got_info)
client.on_new_verified_button = got_button
client.handle_events()
fire.py
import RPi.GPIO as GPIO
import threading
import time
import random
import math
R = 17
G = 22
pwms = []
intensity = 1.0
def initialize_gpio():
GPIO.setmode(GPIO.BCM)
GPIO.setup([17,22], GPIO.OUT)
def red_light():
p = GPIO.PWM(R, 300)
p.start(100)
pwms.append(p)
while True:
p.ChangeDutyCycle(min(random.randint(50, 100) * math.pow(intensity + 0.1, 0.75), 100) if intensity > 0 else 0)
rand_flicker_sleep()
def green_light():
global green_dc
p = GPIO.PWM(G, 300)
p.start(0)
pwms.append(p)
while True:
p.ChangeDutyCycle(random.randint(5, 10) * math.pow(intensity, 2) if intensity > 0 else 0)
rand_flicker_sleep()
def rand_flicker_sleep():
time.sleep(random.randint(3,10) / 100.0)
def fan_the_flame(_):
global intensity
intensity = min(intensity + 0.25, 1.0)
def light_candle():
threads = [
threading.Thread(target=red_light),
threading.Thread(target=green_light),
## threading.Thread(target=burning_down)
]
for t in threads:
t.daemon = True
t.start()
for t in threads:
t.join()
def startfire():
try:
initialize_gpio()
print("\nPress ^C (control-C) to exit the program.\n")
light_candle()
except KeyboardInterrupt:
pass
finally:
for p in pwms:
p.stop()
def stopfire():
GPIO.cleanup()
#if __name__ == '__main__':
# main()
if button == 1:
startfire()
if button == 2:
stopfire()
Have a common (global variable) that both codes can read, you can put this in a standalone file that both codes can access. So script 1 updates this variable like
if(single press): variable=1
elif(double press): variable=2
then in fire.py you can poll the variable.
if(varaible==1): start/stop fire
elif(variable==2): stop/start fire
else: #throw error
I'm sure there are more efficient ways to do this, but this method should be the easiest to understand.

How to receive multiple ATCommand response packets from the local xbee for the "FN" (finding neighbour) AT command using python digi-xbee library

This is the code which works well for all AT commands except "FN":
from digi.xbee.devices import XBeeDevice
#Initialise a serial port for the local xbee
local_xbee = XBeeDevice("/dev/tty.usbserial-AH02D9Q4", 9600).
#Opens serial port for sending commands
local_xbee.open()
#Sets new timeout for sync command operation
local.set_sync_ops_timeout(10).
#Send "FN" AT command to local xbee to receive neighbour list
neighbour_xbee_list = local.get_parameter("FN")
print(neighbour_xbee_list)
local_xbee.close()
Note:
The above code returns only one neighbour whereas I have more than one nodes in the network.
I believe the point of the question is not how to use the FN command, but how to discover neighbors.
For this, you can use the start_discovery_process function described here.
http://xbplib.readthedocs.io/en/latest/user_doc/discovering_the_xbee_network.html#discovernetwork
import serial
from digi.xbee.packets.common import ATCommPacket
from digi.xbee.devices import XBeeDevice
from digi.xbee.reader import PacketListener
from digi.xbee.serial import XBeeSerialPort
from digi.xbee.util import utils
import time
local_xbee = XBeeDevice("/dev/tty.usbserial-AH02D9Q4", 9600)
local_xbee.open()
print("This is : ", local_xbee.get_node_id())
print(local_xbee._packet_listener.is_running())
parameter = "FN"
frame_id = 33
my_packet = ATCommPacket(frame_id, parameter)
#print(my_packet)
#print(my_packet.frame_id)
#print(my_packet.command)
final_send = my_packet.output()
local_xbee._serial_port.write(final_send)
print("Finding Neighbours")
while True:
print(".")
Queue = local_xbee._packet_listener.get_queue()
received_packet = Queue.get_by_id(frame_id)
if received_packet != None:
#if received_packet.status == ATCommandStatus.OK:
final = received_packet._get_api_packet_spec_data().__str__()
print(final)
time.sleep(0.5)
local_xbee.close()

Trying to control a GNUradio flow graph object

Good Day:
I am trying to write some python code to control a gnuradio block. To illustrate the issue I encountered, I have created a simple flowgraph consisting of an audio source connected to the sound card. There is a single gnuradio companion WX GUI element (a variable slider) to control the audio frequency. I tried to take the python code created by gnuradio companion and create an object within python, then create two threads. One thread starts the GNUradio object, the second thread queries the user to input a frequency, then queries the object's frequency and prints it to the terminal for confirmation.
When the code is run, the audio generator starts, the WX GUI slider is shown, and the terminal prompts the user to input a frequency. When the frequency is input via the terminal query, that number is echoed back as expected but the GNUradio block does not change its frequency. Changing the frequency via the WX GUI slider works as expected.
Obviously I'm not linking the variable to the GNUradio block correctly. The code is copied below, any assistance would be appreciated. Thank you.
-Ed
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
##################################################
# GNU Radio Python Flow Graph
# Title: Top Block
# Generated: Wed Oct 4 06:04:54 2017
##################################################
if __name__ == '__main__':
import ctypes
import sys
if sys.platform.startswith('linux'):
try:
x11 = ctypes.cdll.LoadLibrary('libX11.so')
x11.XInitThreads()
except:
print "Warning: failed to XInitThreads()"
from gnuradio import analog
from gnuradio import audio
from gnuradio import eng_notation
from gnuradio import gr
from gnuradio.eng_option import eng_option
from gnuradio.filter import firdes
from gnuradio.wxgui import forms
from grc_gnuradio import wxgui as grc_wxgui
from optparse import OptionParser
import wx
import threading
from threading import Thread
class top_block(grc_wxgui.top_block_gui):
def __init__(self):
grc_wxgui.top_block_gui.__init__(self, title="Top Block")
_icon_path = "/usr/share/icons/hicolor/32x32/apps/gnuradio-grc.png"
self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY))
##################################################
# Variables
##################################################
self.samp_rate = samp_rate = 32000
self.freq = freq = 1000
##################################################
# Blocks
##################################################
_freq_sizer = wx.BoxSizer(wx.VERTICAL)
self._freq_text_box = forms.text_box(
parent=self.GetWin(),
sizer=_freq_sizer,
value=self.freq,
callback=self.set_freq,
label="frequency",
converter=forms.float_converter(),
proportion=0,
)
self._freq_slider = forms.slider(
parent=self.GetWin(),
sizer=_freq_sizer,
value=self.freq,
callback=self.set_freq,
minimum=300,
maximum=5000,
num_steps=100,
style=wx.SL_HORIZONTAL,
cast=float,
proportion=1,
)
self.Add(_freq_sizer)
self.audio_sink_0 = audio.sink(samp_rate, "", True)
self.analog_sig_source_x_0 = analog.sig_source_f(samp_rate, analog.GR_COS_WAVE, freq, .25, 0)
##################################################
# Connections
##################################################
self.connect((self.analog_sig_source_x_0, 0), (self.audio_sink_0, 0))
def get_samp_rate(self):
return self.samp_rate
def set_samp_rate(self, samp_rate):
self.samp_rate = samp_rate
self.analog_sig_source_x_0.set_sampling_freq(self.samp_rate)
def get_freq(self):
return self.freq
def set_freq(self, freq):
self.freq = freq
self.analog_sig_source_x_0.set_frequency(self.freq)
self._freq_slider.set_value(self.freq)
self._freq_text_box.set_value(self.freq)
toneGen = top_block()
def runToneGen():
toneGen.Start(True)
toneGen.Wait()
def userInput():
while True:
freq = raw_input("Enter frequency: ")
toneGen.freq = freq
print "tone generator freq set to: ",toneGen.freq," Hz"
#initiate thread to query user for freq
uiThread = Thread(target=userInput, args=())
uiThread.start()
#initiate thread to run gnuradio block
gnuThread = Thread(target=runToneGen, args=())
gnuThread.start()
I believe I have found the answer to my own question. In the code sample above, in the 'userInput()' definition, I was trying to change the frequency of the tone generator block by directly setting the variable 'toneGen.freq'. This did not work. In order to correctly set the frequency the method 'set_freq' in the toneGen object must be used with this syntax:
toneGen.set_freq(int(freq))
this function call will correctly set the audio frequency of the generator and update the UI slider.

Categories