grab() after device.leds() turns off led - python

I can read the NUML_LED state using evdev. But if I grab() the device after reading the leds when I exit the program the led is off even though the state is still on as far as the system is concerned.
import evdev
device = evdev.InputDevice('/dev/input/event3')
try:
print(device.leds(verbose=True))
print("Using device:\n" + str(device))
device.grab()
except (KeyboardInterrupt, SystemExit):
pass
If I just do one of grab() OR device.leds() without the other they (as expected) don't turn off. But invoking both messes with the leds.
Edit:
I just realised that if LED_NUML and LED_CAPSL are on when I exit the leds are off but when I press either num_lock or caps_lock the state of the other button gets fixed also.
IE:
- LED_NUML and LED_CAPSL are on
- run script
- LED_NUML and LED_CAPSL are off
- press num_lock turns num_lock off (LED_NUML is already off so it stays off)
- LED_CAPSL turns on

At this time it is not possible. There is an issue in the kernel. As ssieb says on the python evdev issues page about this issue.
When a device is released, all handlers attached to the device get restarted. My guess is that there is some handler that is resetting the LEDs.

Related

Break loop using hotkey

There are hundreds of similar questions but none of them appear to be a solution in my case.
My code is shaped in this way
def iterative_func():
# do things
while True:
iterative_func()
Then I would like it to stop when I press a hotkey let's say 'ctrl+k'.
I tried pynput but the listener is not applicable since it waits for an Input and the rest of the script (iterative_func()) won't run, in my case the script should continuously run until I press some hotkey.
Also the solution
while True:
try:
iterative_func()
except KeyboardInterrupt:
break
doesn't work for me (I don't know why, but maybe it's because I'm running VSCode), anyway it's not code I want to implement because the script will be deployed as a .exe file.
PS.
I cannot import Key and Controller from pynput, it prompts an error and I have no clue on how to fix this so also solutions using these should be avoided.
I tried pynput but the listener is not applicable since it waits for
an Input and the rest of the script (iterative_func()) won't run
I can shed some light on how to overcome this problem, which made you optout pynput. See the below approach:
from pynput import keyboard
running = True # flag on loop runs
def stop_run(): # function to stop the program
global running
running = False
# register a hotkey, and call stop_run() when it is pressed
with keyboard.GlobalHotKeys({'<ctrl>+k': stop_run}) as h:
while running:
print("Running ... ")
This will run your code and wait for hotkey to stop the loop by a flag.

Interrupts in Python

I wrote a simple code to test interrupts in Python:
import RPi.GPIO as GPIO
brakepin = 32
brake_value = 0
GPIO.setmode(GPIO.BCM)
GPIO.setup(brakepin, GPIO.IN, pull_up_down = GPIO.PUD_UP) #Brake
def brake_isr(channel):
global brake_value
print ("Entered interrupt")
brake_value = GPIO.input(brakepin)
GPIO.add_event_detect(brakepin, GPIO.RISING, callback = brake_isr)
while True:
print("Brake_value: %d" % (brake_value))
time.sleep(0.025)
I am using interrupts to capture when the brakes are pressed on a scooter and when they are not. Ofcourse, 1 indicates the brakes are pressed and 0 indicates they are not. I have three questions:
In the line:
GPIO.setup(brakepin, GPIO.IN, pull_up_down = GPIO.PUD_UP) what should I put the value of pull_up_down? UP or DOWN. Actually both are working the same way, there is no difference. The brake's default value is 0 (when not pressed) because it is connected to ground.
In the line: GPIO.add_event_detect(brakepin, GPIO.RISING, callback = brake_isr) should I put GPIO.RISING or FALLING? Since I want to detect brake events (i.e. when a brake is being pressed and when it is not) I think I should use RISING only.
One very weird thing is happening:
When I press the brake I get output as:
Brake_value: 1 -> This is as expected. Great!
When I let go off the brake it still remains 1, i.e.:
Brake_value: 1 -> This should be 0
Why is the brake value not coming back to 0 when I let go of the brake
Edit:
I modified my interrupt code block to this:
def brake_isr(channel):
print ("Entered interrupt")
global brake_value
while GPIO.input(brakepin) == 1:
brake_value = GPIO.input(brakepin)
brake_value = 0
Well this solved the problem that I was facing with Question 3. However another weird thing is happening with the output (which was happening even before):
Brake_value: 0
.
.
.
When I press the brakes:
Entered interrupt
Entered interrupt
Entered interrupt
Brake_value: 1
Brake_value: 1
.
.
.
When I let go of the brakes:
Entered interrupt
Entered interrupt
Brake_value: 0
Brake_value: 0
.
.
.
Why is the code entering the interrupt block several times when I press the brakes (as can be seen with the multiple print statement) and why is it entering the interrupt when I let go off the brakes? This is happening for time.sleep(1) or time.sleep(0.025), deosnt matter what the delay is.
It looks like you have an interrupt set to detect rising edges, so when the value changes from 0 to 1, but you have nothing setup to check falling edges which would be when you release the brakes. So it looks to me like you have an interrupt triggered when you push the brakes, set Brake_value = 1... but then you never setup any way to swap it back to 0 so it just stays as a 1 in your stored variable. if you were to measure the physical voltage at the GPIO pin it would change as you pressed the brake, causing the rising edge which triggers your interrupt, however within that interrupt you're just setting Brake_value = GPIOvalue which during the interrupt will always be 1.
Basically the problem is that you're trying to track the status of your GPIO pin, but instead of just reading it's value and printing that, you are setting an internal variable to be equal to the value of the GPIO pin... except you set it high on your interrupt event and then never change it back.
There are a few ways to fix this, you could set another interrupt event to occur on a falling edge for the same pin, which would make it trigger when you make a change from 1 to 0 (when you release the brakes). Additionally you could get rid of brake_value and just replace everywhere you access it with an actual read to the GPIO pin. You could also poll the GPIO pin after the 0 -> 1 interrupt occurs, essentially just having a
while(GPIO == 1) in your interrupt code block which would exit as soon as the pin is set back to 0. There are probably a few other ways to do this but these are what immediately come to mind.
The above essentially answers your questions 3, question 1 is probably a datasheet question, the microcontrollers i've used needed specific values for the pullups to make the GPIO pins do different things, but I think the pi is much more forgiving and if you're not having any issues with either way you set it I wouldn't worry about it... and for question 2 rising edge will trigger an interrupt when the value at that pin goes from 0 to 1, while falling edge triggers the event when going from 1 to 0, so if you have it normally grounded, and it goes high when you pull the brakes, what you want is a rising edge. However if you normally have it tied to vcc and it goes to ground when you pull the brakes, what you want is a falling edge.
As suggested by #Ryan, this is what I did:
def brake_isr(channel):
print ("Entered interrupt")
global brake_value
while GPIO.input(brakepin) == 1:
brake_value = GPIO.input(brakepin)
brake_value = 0
This solves question 3, but it will be great if someone can answer the edit I posted in my question
Concerning your new issue, you might want to look into "switch debounce". This is something that can happen when you push a button and the simple answer is that cheap buttons/switches can result in multiple REALLY fast 0->1->0->1 changes for each press/release... which is why you are getting print outs when you release the button as well. Your sleeps are only pausing the print out of the current brake value, while the interrupt itself triggers it's own print out which is why the sleep duration has no effect on this message. The software fix is to have your interrupt only trigger a certain amount of time after a rising edge is detected if and only if there is no following falling edge (basically if we go from 0->1, make sure it stays at 1 for .5 seconds or something before you actually trigger the event), and then do the same thing for releasing the brake(when we go 1->0, wait 0.5 seconds to make sure it stays at 0). THAT SAID, a software solution isn't what you really want here. Switch Debouncing can be done very easily with two nand gates and a few resistors. https://electrosome.com/switch-debouncing/ has a better explanation with a few really good pictures and if you have the physical components it's the way to go, but if you don't have the actual parts you can cobble together a software solution. The software solution to this problem is sub ideal because it will introduce lag between pushing the brakes and it registering with the arduino, which in some cases you don't really care about but generally for brakes you want them to be very responsive.

Powershell script to keep computer from going to idle

I'm trying to keep my computer from going into idle mode. So I'm trying to write a script that will wiggle my mouse. This is a slightly customized version of a powershell script I found.
param($cycles = 60)
Add-Type -AssemblyName System.Windows.Forms
$screen = [System.Windows.Forms.SystemInformation]::VirtualScreen
for ($i = 0; $i -lt $cycles; $i++) {
Start-Sleep -Seconds 3
[Windows.Forms.Cursor]::Position = "$($screen.Width),$($screen.Height)"
Start-Sleep -Seconds 3
[Windows.Forms.Cursor]::Position = "$($screen.Left),$($screen.Top)"
}
While this does wiggle the mouse it doesn't keep the screen from turning off. So I wrote this: (in python)
import ctypes, time, datetime
mouse_event = ctypes.windll.user32.mouse_event
MOUSEEVENTF_MOVE = 0x0001
print("press ctrl-c to end mouse shaker")
try:
while True:
mouse_event(MOUSEEVENTF_MOVE,25,0,0,0)
time.sleep(1)
mouse_event(MOUSEEVENTF_MOVE,0,25,0,0)
time.sleep(1)
mouse_event(MOUSEEVENTF_MOVE,-25,0,0,0)
time.sleep(1)
mouse_event(MOUSEEVENTF_MOVE,0,-25,0,0)
time.sleep(1)
except KeyboardInterrupt:
pass
This python code will keep my screen from going to sleep and prevents the machine from becoming idle. I believe the issue is because in the powershell scrip I never send the os the "MOUSEEVENTF_MOVE = 0x0001" string. According to the Microsoft website the variable MOUSEEVENTF_MOVE is a flag to signify that the mouse has moved.
Additionally I found this SO question (How i can send mouse click in powershell) that seems to be doing this exact thing but is also clicking, which I don't want. I've tried just commenting out those lines but the "SendImput" I think is expecting some input causing it to fail.
So here is my question. How do I pass the MOUSEEVENTF_MOVE variable in the powershell code? I'm thinking it should have the same effect as my python code.
P.S. This is my first time working with powershell.

What is the easiest way to detect key presses in python 3 on a linux machine?

Right now I'm trying to make a small code with a raspberry pi and and a makey makey. The makey makey is a small board that acts as a usb keyboard when certain contacts are powered. My question is what is the easiest way to detect those keypresses inside a python script. I understand using the GPIO pins would be easier, but right now I'm looking for this. I have seen examples such as using using getch() from msvcrt (which from what I understand is windows only,) using pygame.key, and using getKey. Which of theses is easiest to use? Are there any that can detect a key being pressed and a key being released?
Pseudo code:
import whatever needs importing
if the "W" key is pressed:
print ("You pressed W")
elif the "S" is pressed:
print ("You pressed S")
and so on. Thanks.
This is a simple loop that will put stdin in raw mode (disabling buffering so you don't have to press enter) to get single characters. You should do something smarter (like a with statement to disable it) but you get the idea here:
import tty
import sys
import termios
orig_settings = termios.tcgetattr(sys.stdin)
tty.setcbreak(sys.stdin)
x = 0
while x != chr(27): # ESC
x=sys.stdin.read(1)[0]
print("You pressed", x)
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, orig_settings)
I think you'd have to loop to detect key releases in Python.
ETA some more explanation:
On Linux, input to your program will be line buffered. This means that the operating system will buffer up input until it has a whole line, so your program won't even see anything the user typed until the user also hits 'enter'. In other words, if your program is expecting the user to type 'w' and the user does this, 'w' will be sitting in the OS's buffer until the user hits 'enter'. At this point the entire line is delivered to your program so you will get the string "w\n" as the user's input.
You can disable this by putting the tty in raw mode. You do this with the Python function tty.setcbreak which will make a call down the tty driver in linux to tell it to stop buffering. I passed it the sys.stdin argument to tell it which stream I wanted to turn buffering off for1. So after the tty.setcbreak call, the loop above will give you output for every key the user presses.
A complication, though, is that once your program exits, the tty is still in raw mode. You'll generally find this unsatisfying since you don't get any of the power that modern terminal settings offer (like when you use control or escape sequences). For example, notice that you might have trouble exiting the program with ctrl-C. Consequently you should put the terminal back into cooked mode once you are done reading input characters. The termios.tcsetattr call simply says "put the terminal back the way I found it". It knows how to do this by first calling termios.tcgetattr at the beginning of the program which is saying "tell me all the current settings for the terminal".
Once you understand all that, you should easily be able to encapsulate the functionality in a function that suits your program.
1 stdin is the stream that input comes to you from the user. Wikipedia can tell you more about standard streams.
Using a good lightweight module curtsies you could do something like this (taken from their examples/ directory):
from curtsies import Input
def main():
with Input(keynames='curses') as input_generator:
for e in input_generator:
print(repr(e))
if __name__ == '__main__':
main()
So pressing keys on your keyboard gives you something like this:
'a'
's'
'KEY_F(1)'
'KEY_F(2)'
'KEY_F(3)'
'KEY_F(4)'
'KEY_F(5)'
'KEY_LEFT'
'KEY_DOWN'
'KEY_UP'
'KEY_RIGHT'
'KEY_NPAGE'
'\n'
curtsies is used by bpython as a low level abstraction of terminal-related stuff.
The basic problem of decoding the input is that in different terminals and terminal emulator programs like xterm or gnome-terminals physically same keys produce different keycode sequences. That's why one needs to know which terminal settings should be used to decode input. Such a module helps to abstract from those gory details.
Since your question states that you are using a Raspberry Pi and a USB HID keyboard peripheral, but does not specify whether or not you have the Pi configured to boot into text or graphical mode where you will be running your script, I would suggest using libinput which will work in either case.
You can use libinput's python bindings to read keyboard (and most other input devices) events directly from the kernel.
pip3 install python-libinput
The interface to this subsystem is exposed through the character devices which usually live in /dev/input/. They are managed by udev rules which create one or more character devices per attached input device, and are added and removed dynamically when, for example, a USB keyboard is attached or unplugged, or a Bluetooth mouse connects or disconnects.
Libinput handles for you the task of opening and reading from all attached input devices, and opening and closing devices when they are added and removed, respectively.
Using libinput from python to read key events would look like this:
import libinput
def read_key_events():
# init libinput
li = libinput.LibInput(udev=True)
li.udev_assign_seat('seat0')
# loop which reads events
for event in li.get_event():
# test the event.type to filter out only keyboard events
if event.type == libinput.constant.Event.KEYBOARD_KEY:
# get the details of the keyboard event
kbev = event.get_keyboard_event()
kcode = kbev.get_key() # constants in libinput.define.Key.KEY_xxx
kstate = kbev.get_key_state() # constants libinput.constant.KeyState.PRESSED or .RELEASED
# your key handling will look something like this...
if kstate == libinput.constant.KeyState.PRESSED:
print(f"Key {kcode} pressed")
elif kstate == libinput.constant.KeyState.RELEASED:
if kbev.get_key() == libinput.define.Key.KEY_ENTER:
print("Enter key released")
elif kcode == libinput.define.Key.KEY_SPACE:
print("Space bar released")
else:
print(f"Key {kcode} released")
One minor gotcha to be aware of is that udev is commonly configured to set permissions on the event devices it creates in /dev/input/ to allow access only from users who are members of a special supplementary 'input' group, since to allow unrestricted access to raw user key and mouse input would be a major security flaw. As such, o if running this throws an error during the libinput initialisation, you may need to add input to your user's supplementary groups by running:
sudo usermod -G input -a "${USERNAME}"

Set keyboard layout using pyudev

I wrote this script to change the usb keyboard layout automatically on plug-in
import pyudev
from subprocess import call
monitor = pyudev.Monitor.from_netlink(pyudev.Context())
monitor.filter_by('usb')
def kbd_event(action, device):
if action == 'add':
call(["setxkbmap", "carpalx"])
observer = pyudev.MonitorObserver(monitor, kbd_event)
observer.start()
setxkbmap carpalx works if I type it in bash, but it doesn't change the layout in the above code. So I did this in bash:
setxkbmap carpalx
xmodmap -pke > carpalx2
changed the above call line to call(["xmodmap", "./carpalx2"]) and now the script works. I have the following problems:
Why does xmodmap work in the code and setxkbmap doesn't, but both work in bash?
Currently, kbd_event is called for every usb event and call(["xmodmap", "./carpalx2"]) is run for every usb device I plug in. How can I further filter the events so the layout changes only when I insert a keyboard?
With my current code, the keyboard layout changes every time I plug in my mouse :)
You can filter keyboards by checking for the ID_INPUT_KEYBOARD property:
if action == 'add' and device['ID_INPUT_KEYBOARD'] == '1':
print('a keyboard was added')
Concerning the difference between calling setxkbmap directly and using the script, I'd guess that the X server needs time to initialize the keyboard, too. UDev invokes the callback as soon as the keyboard has finished udev processing, but this can easily before the X11 server configures and initializes the new keyboard.
Check the X.org logs, and any error message that setxkbmap might print.

Categories

Resources