Controlling Servo with PCA9685 and Raspberry Pi - python

I am trying to control 2 servos from my pca9685 which is connected to my raspberry pi. I have written code that works with key inputs like I want, but I am only able to use one key input, and then I don’t get a response after the first key input. Any idea on how to fix the issue?
import time
import adafruit_servokit import ServoKit
kit = ServoKit(channels=8)
key = input()
angle = 0
while angle <= 100:
if key == "a":
kit.servo[0].angle = 100
time.sleep(1)
elif key = "aa":
kit.servo[0].angle = 0
time.sleep(1)

I guess the problem is with the key assignment 'a' and 'aa', if you can change the 'aa' with some other key input i guess it will work, because when using the input() stream it processes the stream character by character so "aa" will be an equivalent of 'a' + 'a', hence you are facing this issue

The issue was that my key = input() was outside of the while loop. By having it outside of the loop, it was only being called once.
import time
import adafruit_servokit import ServoKit
kit = ServoKit(channels=8)
angle = 0
while angle <= 100:
key = input()
if key == "a":
kit.servo[0].angle = 100
time.sleep(1)
elif key = "aa":
kit.servo[0].angle = 0
time.sleep(1)

Related

(Python) How to listen to keyboard while doing other things in a while loop

Here is my code.
import keyboard
while num <= total_num_list-1:
show_num = num + 1
duration_in_min = 1:30
t = 1
while t <= duration:
if keyboard.is_pressed("n"):
break
time.sleep(1)
time_in_min = time.strftime('%M:%S', time.gmtime(t))
print(f"{time_in_min}/{duration_in_min}")
t += 1
num += 1
This code is working for now, but it needs me to hold the "n" key to break the loop because of the delay. What I want is it to have a immediate respond to my key press. What modules can I use or what should I do to make it work?
I am a beginner, sorry if there is any mistakes.

PYTHON: How to make a program to stop if some seconds have passed?

So I'm making a little speed game. A random letter is going to be generated by a function, right after that, I want the program to wait some seconds. If nothing is pressed, you will lose and your record will be displayed If you press the right key, another random letter is going to be displayed. I used the time function and simulated a cronometer that lasts in a range (0,2). This is what I have so far. It works, the thing is, it displays the first letter, if you press it wrong you lose (good) but even if you press it right, the cronometer obviously keeps running, so it gets to 2 and you lose. I want it to stop and reset after I hit the key, but I have no idea of how to do it. Im new in programming so I'm sorry if you don't get something.
import string
import random
import msvcrt
import time
def generarletra():
string.ascii_lowercase
letra = random.choice(string.ascii_lowercase)
return letra
def getchar():
s = ''
return msvcrt.getch().decode('utf-8')
print("\nWelcome to Key Pop It!")
opcion = int(input("\n Press 1 to play OR\n Press 2 for instructions"))
if(opcion == 1):
acum=0
while True:
letra2 = generarletra()
print(letra2)
key = getchar()
for s in range (0,2):
print("Segundos ", s)
time.sleep(2)
acum = acum + 1
if((key is not letra2) or (s == 2)):
print("su record fue de, ", acum)
break
elif(opcion == 2):
print("\n\nWelcome to key pop it!\nThe game is simple, the machine is going to generate a
random\nletter and you have to press it on your keyboard, if you take too\nlong or press the wrong
letter, you will lose.")
else:
print("Invalid option!")
PD: You need to run it with a console simulation in your IDE or directly from the console. msvcrt library won't work inside an IDE for some reason.
msvcrt.getch() is blocking, so you don't actually measure the time it took the user to press the key. The for loop starts after the user already pressed it.
also, time.sleep() is blocking, so the user will have to wait the sleep time even if he already pressed the key.
To solve the first problem you can use msvcrt.kbhit() to check if the user pressed on some key, and call msvcrt.getch() only if he did. This way msvcrt.getch() will return immediately after you call it.
To solve the second problem you can just use time.time() to get the start time of the round, and compare it to current time inside a loop. You can print how much time passed inside the loop also.
Here is the final code (with some extra naming and formatting changes):
import string
import random
import msvcrt
import time
MAX_TIME = 2
def get_random_char():
return random.choice(string.ascii_lowercase)
def get_user_char():
return msvcrt.getch().decode('utf-8')
print("\nWelcome to Key Pop It!")
option = input("\n Press 1 to play OR\n Press 2 for instructions\n")
if option == "1":
score=0
while True:
char = get_random_char()
print("\n" + char)
start_time = time.time()
while not msvcrt.kbhit():
seconds_passed = time.time() - start_time
print("seconds passed: {0:.1f}".format(seconds_passed), end="\r")
if seconds_passed >= MAX_TIME:
key = None
break
else:
key = get_user_char()
if key != char:
break
score = score + 1
print("\nsu record fue de, ", score)
elif option == "2":
print("""
Welcome to key pop it!
The game is simple, the machine is going to generate a random
letter and you have to press it on your keyboard, if you take too
long or press the wrong letter, you will lose.""")
else:
print("Invalid option!")
Timestamp-solution:
from time import time, sleep
start = time() # start time measuring by creating timestamp
def time_passed(start, duration):
"""tests if an amount of time has passed
Args:
start(float): timestamp of time()
duration(int): seconds that need to pass
Returns:
bool: are 'duration' seconds over since 'start'
"""
return start + duration <= time()
# Use as condition for while
while not time_passed(start, 5):
sleep(1)
# ... or if statements or <younameit>
if time_passed(start, 5):
print("Do something if 5 seconds are over")

Is it possible to override key press functionality in python?

I'm writing a stenography like script, such that, when you press specific combination of keys on your keyboard such as "ASDF" it would translate to a word and type out the word instead of your key presses. However I cannot find a way to override key presses such that programs such as Notepad will ignore me typing "ASDF".
My only current solution to this has been to have my script backspace the characters that were typed, then paste the word that was intended to be typed.
Here is my script thus far:
import ctypes
def main():
GetAsyncKeyState = ctypes.windll.user32.GetAsyncKeyState
key_states = [0] * 26
pressed_keys = [0] * 26
chord = ''
while True:
for k in range(65,91):
key_states[k-65] = GetAsyncKeyState(k)
if GetAsyncKeyState(k): pressed_keys[k-65] = 1
if sum(key_states) == 0:
for k in range(26):
if pressed_keys[k] == 1:
chord += chr(k+65)
if chord != '': write(chord)
chord = ''
pressed_keys = [0] * 26
dictionary = open('dictionary.txt','r').read()
entries = dictionary.split('\n')
chords = [[0,0]]*len(entries)
for e in range(len(entries)):
s = entries[e].split('-')
chords[e] = [s[0],s[1]]
def write(chord):
found = 0
for c in chords:
if chord==c[0]:
found = 1
_type(c[1])
if not found:
_type(chord)
def _type(word):
'''
This is where the code would go...
'''
main()
The expected results would be that none of the characters I'm typing would show up until the script pastes the final word. But instead, my characters that I'm pressing to form the word show up first.
My plan would be to override the keys to do nothing.
I.E. when I pressed "e" nothing would occur.

Increase just by one when a key is pressed

I want to increase the variable "shot_pressed" just by one when the key "s" is pressed no matter how long I pressed. But the result is that the variable keeps on increasing. The longer I pressed, the bigger the value of the variable. Below is a part of my code.
import keyboard
shot_pressed = 0
if keyboard.is_pressed('s'):
shot_pressed += 1
First of all looks like you use https://pypi.python.org/pypi/keyboard
Second, I assume your code is not like you wrote above but like
import keyboard
shot_pressed = 0
while True:
if keyboard.is_pressed('s'):
shot_pressed += 1
print("shot_pressed %d times"%shot_pressed)
If yes, here is the core of the problem: is_pressed will be always True, while key is pressed. So if condition will be True and while will repeat it many times.
There are two ways of dealing with that.
1) Use the same method, but check if this is the first is_pressed moment, so inroduce was_pressed variable:
import keyboard
shot_pressed = 0
was_pressed = False
while True:
if keyboard.is_pressed('s'):
if not was_pressed:
shot_pressed += 1
print("shot_pressed %d times"%shot_pressed)
was_pressed = True
else:
was_pressed = False
2) Better use the library. You can set a hook, so on key pressed your function will be called (only once for one press). So the code will look like this:
import keyboard
shot_pressed = 0
def on_press_reaction(event):
global shot_pressed
if event.name == 's':
shot_pressed += 1
print("shot_pressed %d times"%shot_pressed)
keyboard.on_press(on_press_reaction)
while True:
pass
I do not know that keyboard module but the problem with your code is that the program takes input once. Your program should wait next input from keyboard. Try to use while loop to take inputs from user.
import keyboard
import time
shot_pressed = 0
try:
while True:
if keyboard.is_pressed("S"):
shot_pressed += 1
time.sleep(0.1)
print(sh)
except Exception as er:
pass
Or can use read key
try:
shot_pressed = 0
while True:
key.read_key()
if key.is_pressed("s"):
sh += 1
print(shot_pressed)
except Exception as er:
pass
I haven't used that module, but you probably want the same thing that you would want in javascript. keyboard.KEY_DOWN instead of is_pressed.
https://github.com/boppreh/keyboard#keyboard.KEY_DOWN
You probably need to handle things asynchronously as well.

Python method for reading keypress?

I'm new to Python, and I just made a game and a menu in Python.
Question is, that using (raw_)input() requires me to press enter after every keypress, I'd like to make it so that pressing down-arrow will instantly select the next menu item, or move down in the game. At the moment, it requires me to like type "down" and then hit enter. I also did quite a lot of research, but I would prefer not to download huge modules (e.g. pygame) just to achieve a single keyDown() method. So are there any easier ways, which I just couldn't find?
Edit:
Just found out that msvcrt.getch() would do the trick. It's not keyDown(), but it works. However, I'm not sure how to use it either, it seems quite weird, any help here? This is what I got at the moment:
from msvcrt import getch
while True:
key = getch()
print(key)
However, it keeps giving me all these nonsense bytes, for example, down-arrow is this:
b'\xe0'
b'P'
And I have no idea how to use them, I've tried to compare with chr() and even use ord() but can't really do any comparisons. What I'm trying to do is basically this:
from msvcrt import getch
while True:
key = getch()
if key == escape:
break
elif key == downarrow:
movedown()
elif key == 'a':
...
And so on... Any help?
Figured it out by testing all the stuff by myself.
Couldn't find any topics about it tho, so I'll just leave the solution here. This might not be the only or even the best solution, but it works for my purposes (within getch's limits) and is better than nothing.
Note: proper keyDown() which would recognize all the keys and actual key presses, is still valued.
Solution: using ord()-function to first turn the getch() into an integer (I guess they're virtual key codes, but not too sure) works fine, and then comparing the result to the actual number representing the wanted key. Also, if I needed to, I could add an extra chr() around the number returned so that it would convert it to a character. However, I'm using mostly down arrow, esc, etc. so converting those to a character would be stupid. Here's the final code:
from msvcrt import getch
while True:
key = ord(getch())
if key == 27: #ESC
break
elif key == 13: #Enter
select()
elif key == 224: #Special keys (arrows, f keys, ins, del, etc.)
key = ord(getch())
if key == 80: #Down arrow
moveDown()
elif key == 72: #Up arrow
moveUp()
Also if someone else needs to, you can easily find out the keycodes from google, or by using python and just pressing the key:
from msvcrt import getch
while True:
print(ord(getch()))
See the MSDN getch docs. Specifically:
The _getch and_getwch functions read a single character from the console without echoing the character. None of these functions can be used to read CTRL+C. When reading a function key or an arrow key, each function must be called twice; the first call returns 0 or 0xE0, and the second call returns the actual key code.
The Python function returns a character. you can use ord() to get an integer value you can test, for example keycode = ord(msvcrt.getch()).
So if you read an 0x00 or 0xE0, read it a second time to get the key code for an arrow or function key. From experimentation, 0x00 precedes F1-F10 (0x3B-0x44) and 0xE0 precedes arrow keys and Ins/Del/Home/End/PageUp/PageDown.
I really did not want to post this as a comment because I would need to comment all answers and the original question.
All of the answers seem to rely on MSVCRT Microsoft Visual C Runtime.
If you would like to avoid that dependency :
In case you want cross platform support, using the library here:
https://pypi.org/project/getkey/#files
https://github.com/kcsaff/getkey
Can allow for a more elegant solution.
Code example:
from getkey import getkey, keys
key = getkey()
if key == keys.UP:
... # Handle the UP key
elif key == keys.DOWN:
... # Handle the DOWN key
elif key == 'a':
... # Handle the `a` key
elif key == 'Y':
... # Handle `shift-y`
else:
# Handle other text characters
buffer += key
print(buffer)
from msvcrt import getch
pos = [0, 0]
def fright():
global pos
pos[0] += 1
def fleft():
global pos
pos[0] -= 1
def fup():
global pos
pos[1] += 1
def fdown():
global pos
pos[1] -= 1
while True:
print'Distance from zero: ', pos
key = ord(getch())
if key == 27: #ESC
break
elif key == 13: #Enter
print('selected')
elif key == 32: #Space
print('jump')
elif key == 224: #Special keys (arrows, f keys, ins, del, etc.)
key = ord(getch())
if key == 80: #Down arrow
print('down')
fdown
elif key == 72: #Up arrow
print('up')
fup()
elif key == 75: #Left arrow
print('left')
fleft()
elif key == 77: #Right arrow
print('right')
fright()
I was also trying to achieve this. From above codes, what I understood was that you can call getch() function multiple times in order to get both bytes getting from the function. So the ord() function is not necessary if you are just looking to use with byte objects.
while True :
if m.kbhit() :
k = m.getch()
if b'\r' == k :
break
elif k == b'\x08'or k == b'\x1b':
# b'\x08' => BACKSPACE
# b'\x1b' => ESC
pass
elif k == b'\xe0' or k == b'\x00':
k = m.getch()
if k in [b'H',b'M',b'K',b'P',b'S',b'\x08']:
# b'H' => UP ARROW
# b'M' => RIGHT ARROW
# b'K' => LEFT ARROW
# b'P' => DOWN ARROW
# b'S' => DELETE
pass
else:
print(k.decode(),end='')
else:
print(k.decode(),end='')
This code will work print any key until enter key is pressed in CMD or IDE
(I was using VS CODE)
You can customize inside the if for specific keys if needed
It's really late now but I made a quick script which works for Windows, Mac and Linux, simply by using each command line:
import os, platform
def close():
if platform.system() == "Windows":
print("Press any key to exit . . .")
os.system("pause>nul")
exit()
elif platform.system() == "Linux":
os.system("read -n1 -r -p \"Press any key to exit . . .\" key")
exit()
elif platform.system() == "Darwin":
print("Press any key to exit . . .")
os.system("read -n 1 -s -p \"\"")
exit()
else:
exit()
It uses only inbuilt functions, and should work for all three (although I've only tested Windows and Linux...).
I suggest keyboard module by BoppreH. This example shows how to detect keypresses outside of python console in a non blocking way. When you press the f key it calls a function that prints some text . To end the execution of the script press the q key. For more info and special key codes click here
import keyboard
def my_function():
print("button pressed")
def my_quit():
global mybool
mybool = False
keyboard.add_hotkey('f', my_function)
keyboard.add_hotkey('q', my_quit)
mybool = True
while mybool:
keyboard.read_key()
If you want to pause the execution of the script intsead of add_hotkey() use keyboard.wait('esc')

Categories