Attribute error : RPi.GPIO.PWM has no attribute 'ChangeDutyCycle' - python

So i'm making a little robot using a raspberry pi B+ , everything is going fairly nicely, but I get this error message that I don't seem to be able to solve ..
Attribute error : RPi.GPIO.PWM has no attribute 'ChangeDutyCycle'
Here's two things you might need to know : :D
- The code worked perfectly before I put it as a class, ChangeDutyCycle DOES exist
- If I put my mouse in an 'unallowed' i.e. which gives ChangeDutyCycle a value less than 0 or more than 100, the error changes and becomes 'ChangeDutyCycle can't accept value less than 0 or more than 100' (So first you tell me there is no such function, and then tell me it can't have some values? :roll: )
So I'm going slightly crazy now.
Note : I'm a complete beginner in python, and honestly, it seems to me like a bad version of java, but the RPi GPIO seems to be optimised for this language so I'm trying it out, so if you see any other mistake or bad things, do let me know :D
The code now :
My 'main', where I take input from a pygame window (position of mouse)
#!/usr/bin/python
# -*- coding: utf-8 -*-
import pygame
import RPi.GPIO as GPIO
from pygame.locals import *
from control import control
print('Path :', pygame.__file__)
def main():
pygame.init()
screen = pygame.display.set_mode((200,200))
ctrl = control()
bg = pygame.Surface(screen.get_size())
bg = bg.convert()
bg.fill((250,250,250))
font = pygame.font.Font(None, 36)
text = font.render("Hello", 1, (10,10,10,))
textpos = text.get_rect()
textpos.centerx = bg.get_rect().centerx
bg.blit(text, textpos)
screen.blit(bg, (0,0))
pygame.display.flip()
try:
while 1:
for event in pygame.event.get():
bg.fill((250, 250, 250))
if event.type == QUIT:
return
pos = pygame.mouse.get_pos()
x = pos[0]-100
y = -(pos[1]-200)-100
text = font.render(str(x)+' '+str(y), 1, (10,10,10,))
ctrl.updateEngine(x,y)
bg.blit(text,textpos)
screen.blit(bg, (0,0))
pygame.display.flip()
except KeyboardInterrupt:
return
finally:
ctrl.cleanup()
if __name__ == '__main__': main()
And my class with functions to control motors , control.py :
import RPi.GPIO as GPIO
from time import sleep
import sys
import Tkinter as tk
class control:
def __init__(self):
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(3, GPIO.OUT)# set GPIO 25 as output for white led
GPIO.setup(18, GPIO.OUT)# set GPIO 24 as output for red led
GPIO.setup(2, GPIO.OUT)
GPIO.setup(17, GPIO.OUT)
self.Rb = GPIO.PWM(2, 100)
self.Lf = GPIO.PWM(18, 100)
self.Lb = GPIO.PWM(17, 100)
self.Rf = GPIO.PWM(3, 100)
self.Rb.start(0)
self.Rf.start(0)
self.Lb.start(0)
self.Lf.start(0)
def cleanup(self):
self.Rb.stop()
self.Rf.stop()
self.Lb.stop()
self.Lf.stop()
def updateEngine(self, x, y):
self.clear()
if y>15 and (x>15 or x<-15) :
self.Rf.ChangeDutyCucle(y-15-x)
self.Lf.ChangeDutyCycle(y-15+x)
elif y<-15 and (x>15 or x<-15) :
self.Rb.ChangeDutyCycle(-15-y-x)
self.Lb.ChangeDutyCycle(-15-y+x)
def clear(self):
self.Rf.ChangeDutyCycle(0)
self.Rb.ChangeDutyCycle(0)
self.Lf.ChangeDutyCycle(0)
self.Lb.ChangeDutyCycle(0)
So the error happens in control.py, at the updateEngine method.
Also you'll note I imported an amazing number of 3 times the same package (RPi.GPIO) cause I'm not sure where to import it ! :)
Any help would be graciously accepted :)

I tracked down my issue to a typo: My updateEngine function had a call to self.Rf.ChangeDutyCucle, when it should have been self.Rf.ChangeDutyCycle.

Related

Bring a pygame window to front

from os import environ
environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame # import after disabling prompt
screen = pygame.display.set_mode((800, 800))
screen.fill((50, 50, 50)) # Dark gray color
pygame.display.update()
Yes, I did my research already, and couldn't find anything helpful: hence this question.
Every time I run the program the pygame window opens below other windows. I want it to behave in 2 ways based on code: Pin the window on top and spawn on top but no pin.
Here is the simplest solution I found:
(It also requires tkinter to get system screen metrics)
from os import environ
environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame # import after disabling environ prompt
from win32gui import SetWindowPos
import tkinter as tk
root = tk.Tk() # create only one instance for Tk()
root.withdraw() # keep the root window from appearing
screen_w, screen_h = root.winfo_screenwidth(), root.winfo_screenheight()
win_w = 250
win_h = 300
x = round((screen_w - win_w) / 2)
y = round((screen_h - win_h) / 2 * 0.8) # 80 % of the actual height
# pygame screen parameter for further use in code
screen = pygame.display.set_mode((win_w, win_h))
# Set window position center-screen and on top of other windows
# Here 2nd parameter (-1) is essential for putting window on top
SetWindowPos(pygame.display.get_wm_info()['window'], -1, x, y, 0, 0, 1)
# regular pygame loop
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
done = True

Python Module Issue: TypeError: 'module' object is not callable

I've been attempting to develop a text adventure type game in Python (and PyGame), and thus needed a module to repeatedly blit text to the screen. After searching through a few, I downloaded KTextSurfaceWriter and installed it. Then I tried to follow the demo in the text provided here (http://www.pygame.org/project-KTextSurfaceWriter-1001-.html)
My code:
from ktextsurfacewriter import KTextSurfaceWriter
import pygame
from pygame.locals import *
import pygame.font
pygame.font.init()
screen = pygame.display.set_mode((640,480), 0, 32)
surface = pygame.surface ( (400, 400), flags = SRCALPHA, depth = 32)
surface.fill( (255,255,255,255) )
def blitSurface():
screen.blit(surface, (50,50) )
pygame.display.update()
blitSurface()
def waitForUserAction():
while True:
for event in pygame.event.get():
if event.type == QUIT:
import sys
sys.exit()
if event.type == KEYDOWN:
return
waitForUserAction()
However, this throws back the module error at line 9. I'm fairly new to Python and most of the solutions I saw for this issue involved using the 'from [module] import' code that I already have at the beginning.
You are calling the pygame.surface module:
surface = pygame.surface ( (400, 400), flags = SRCALPHA, depth = 32)
Either use pygame.surface.Surface() or use pygame.Surface() (note the capital S); these are both the same class but pygame.surface is the module in which it is defined.
I have seen your code and soure code.
surface = pygame.surface ( (400, 400), flags = SRCALPHA, depth = 32)
In your code, "surface" is lowercase, which is a python module, so python interpreter tells you the err msg.
surface = pygame.Surface( (400,400), flags=SRCALPHA, depth=32 )
In source code, "Surface" is capital, which may be a class.

Non blocking serial readline when using PyGame

I'm working on a heart rate monitor written in PyGame using an Arduino as an input, the idea being that the game allows you to try and control your heart rate through relaxation exercises regardless of what's on screen.
I want to be able to run things like video/mouse/keybord button capture etc. in the game itself, whilst displaying the heart rate in the top left hand corner and updating it from the arduino when it changes.
The arduino reads a heart rate monitor and then publishes a JSON string formatted as follows:
{'heart_rate': 65,'state': 'running'}
"State" can be one of 'intialising','running','failed' or 'stopped'.
Whilst I'm more than familiar with Python, I've taken code from http://www.akeric.com/blog/?p=1237 to get me started with PyGame as I've not ventured here much before.
The problem I have is that when I try and read from the serial port, it locks up the game.
I've read around threading and I think I've implemented it properly, however the following code still blocks:
"""
default.py
www.akeric.com - 2010-09-07
Default, base, initial setup for a pygame program.
In this case, black background, white circle follows mouse position, and
framerate is shown in the title-bar.
"""
#-------------------------------------------------------------------------------
# Imports & Inits
import sys
import serial
import json
import threading
import pygame
from pygame.locals import *
pygame.init()
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=0)
#-------------------------------------------------------------------------------
# Constants
VERSION = '1.0'
WIDTH = 800
HEIGHT = 600
FRAMERATE = 60
CURRENT_MESSAGE = {'heart_rate': 0}
#-------------------------------------------------------------------------------
# Screen Setup
screen = pygame.display.set_mode((WIDTH, HEIGHT))
bgCol = Color('black')
clock = pygame.time.Clock()
moduleName = __file__.split('\\')[-1]
#-------------------------------------------------------------------------------
# Define helper functions, classes, etc...
def text_objects(text, font):
textSurface = font.render(text, True, (255,255,255))
return textSurface, textSurface.get_rect()
def message_display(text,x_pos=(WIDTH/2),y_pos=(HEIGHT/2)):
largeText = pygame.font.Font('freesansbold.ttf',25)
TextSurf, TextRect = text_objects(text, largeText)
TextRect.center = (x_pos,y_pos)
screen.blit(TextSurf, TextRect)
def spam():
pos = pygame.mouse.get_pos()
pygame.draw.circle(screen, Color('white'), pos, 32)
def handle_data(data):
CURRENT_MESSAGE = json.loads(data)
message_display("HR: %s" % CURRENT_MESSAGE['heart_rate'],50,20)
def read_from_port(ser):
while True:
reading = ser.readline().decode()
if len(reading) > 0:
print "Data Recieved"
handle_data(reading)
#-------------------------------------------------------------------------------
# Main Program
def main():
print "Running Python version: %s"%sys.version
print "Running PyGame version: %s"%pygame.ver
print "Running %s version: %s"%(moduleName, VERSION)
looping = True
# Main Loop-----------------------
while looping:
# Maintain our framerate, set caption, clear background:
clock.tick(FRAMERATE)
pygame.display.set_caption("%s - FPS: %.2f" %(moduleName,clock.get_fps()) )
screen.fill(bgCol)
spam()
# Update our display:---------
pygame.display.flip()
#-------------------------------------------------------------------------------
# Execution from shell\icon:
if __name__ == "__main__":
# Make running from IDE work better:
thread = threading.Thread(target=read_from_port, args=(ser,))
thread.start()
sys.exit(main())
Can anyone help me understand where I'm going wrong here?
I know very little about Python but I would test to see if there are any characters waiting to be read and just read that number instead of using readline. I think you want to use in_waiting and read(size).
Readline will block until a carriage return is received.
So, I think something like:
def read_from_port(ser):
while True:
if in_waiting > 0:
reading = read(in_waiting)
print "Data Recieved"
handle_data(reading)
Now you will need to concatenate and parse the string at the delimiter to make sure the entire string has been received.
I fixed it! :)
#!/usr/bin/env python
import pygame
from threading import Thread
import serial
import json
from pygame.color import Color
from pygame.locals import *
CURHR = 0
ser = serial.Serial('/dev/pts/3', 9600, timeout=0)
def worker():
global CURHR
while True:
msg = ser.readline()
if len(msg) > 0:
print "Message Received: %s" % msg
current_message = json.loads(msg)
if current_message["state"] == "running":
CURHR=current_message['heart_rate']
t = Thread(target=worker)
t.daemon = True
t.start()
pygame.init()
#-------------------------------------------------------------------------------
# Constants
VERSION = '1.0'
WIDTH = 800
HEIGHT = 600
FRAMERATE = 60
#-------------------------------------------------------------------------------
# Define helper functions, classes, etc...
def text_objects(text, font):
textSurface = font.render(text, True, (255,255,255))
return textSurface, textSurface.get_rect()
def message_display(text,x_pos=(WIDTH/2),y_pos=(HEIGHT/2)):
largeText = pygame.font.Font('freesansbold.ttf',25)
TextSurf, TextRect = text_objects(text, largeText)
TextRect.center = (x_pos,y_pos)
screen.blit(TextSurf, TextRect)
def spam():
pos = pygame.mouse.get_pos()
pygame.draw.circle(screen, Color('white'), pos, 32)
screen = pygame.display.set_mode((WIDTH,HEIGHT))
clock = pygame.time.Clock()
font = pygame.font.SysFont("consolas", 25, True)
count = 0
pygame.display.set_caption("Test")
done = False
while not done:
screen.fill(Color('black'))
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
done = True
spam()
hr_string = "HR: %s" % CURHR
hr_text = font.render(hr_string, True, Color('red'))
screen.blit(hr_text, [25,10])
clock.tick(20)
pygame.display.flip()
The trick is to setup a Global and then get the thread to set the value of that global.
The global is then rendered when the screen is "blitted" (is that a thing?!) and the value is updated in realtime without affecting the movement of the cursor.

new text rendered over older text in pygame

I wrote an application in pygame to display some text. The text consist of a counter which is updated every second or so. I am using raspberry pi for this application. So when I use xserver then everything is displayed correctly but if I use sdl_videodriver fbcon for display then static text is displayed correctly but the counter(text) whose value changes is not displayed correctly. The new value of counter is displayed over the older value and thus after few seconds it becomes unreadable. Following is my code
class pyscope :
def __init__(self):
disp_no = os.getenv("DISPLAY")
if disp_no:
print "I'm running under X display = {0}".format(disp_no)
drivers = ['fbcon', 'directfb', 'svgalib']
found = False
for driver in drivers:
if not os.getenv('SDL_VIDEODRIVER'):
os.putenv('SDL_VIDEODRIVER', driver)
try:
pygame.display.init()
except pygame.error:
print 'Driver: {0} failed.'.format(driver)
continue
found = True
break
if not found:
raise Exception('No suitable video driver found!')
size = [1920,1080]
self.screen = pygame.display.set_mode(size,pygame.FULLSCREEN)
self.screen.fill((0,0,0))
pygame.font.init()
pygame.display.update()
def __del__(self):
"Destructor to make sure pygame shuts down, etc."
def test(self):
pygame.display.set_caption("Test")
done=False
clock=pygame.time.Clock()
font = pygame.font.SysFont("consolas", 34, True)
frame_rate = 20
count = 0
while done==False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done=True
high_score = 2270
plan = 2100
count = count + 1
font = pygame.font.SysFont("consolas", 200, True)
if count >100:
count = 12
output_string = "ACTUAL %s" %count
text = font.render(output_string,True,red)
pygame.display.flip()
self.screen.blit(text, [250,420])
output1 = "random %.2f" %(float(count)/100*100)
text = font.render(output1,True,red)
self.screen.blit(text, [250,540])
pygame.display.flip()
clock.tick(20)
pygame.display.flip()
scope = pyscope()
scope.test()
time.sleep(10)
Thus my question how can I avoid new text being rendered over older text while using sdl_videodriver?
It is not enough to update() the screen, you should also "clear" it with a color. Before any blitting/drawing, do:
self.screen.fill((0,0,0))
You currently only do this once, when initializing the application. This should be done on each frame, if you want to keep a fresh, new screen on each frame.
It sounds like pygame is not clearing the area of the text counter.
Since I don't have access to an raspberry pi I would suggest you to
make sure you clear/update the area where the counter is rendered.
Before blitting out the text, you could clear the screen area with pygame.draw.rect
pygame.draw.rect(self.screen,(0,0,0),text.get_rect())
Should work OK, as long as your background is a solid color.

USB controller will not work in Pygame

I have a USB controller that I have mapped to be the arrow keys on the keyboard, outside of gaming programs (as in, you can use it as the normal arrow keys on the keyboard). I did this using the program ControlMK. My Pygame program will not recognize the controller as the keyboard. When I try to use the Joystick modules, the program does not function properly.
Here is my code:
import pygame, sys, time, random
from pygame.locals import *
# set up pygame
try:
pygame.init()
pygame.display.init()
pygame.mixer.init(size=8, buffer=2048)
pygame.mixer.get_init
except:
print "error in init\n"
white=(255,255,255)
screen=pygame.display.set_mode((800,600), pygame.RESIZABLE)
class Loadsound:
def __init__(self, name, key, sound_file):
self.name = name
self.name=key
self.sound = pygame.mixer.Sound(sound_file)
sound_left=[]
sound_left.append(Loadsound("left","K_LEFT", "left.wav"))
sound_right=[]
sound_right.append(Loadsound("right","K_RIGHT", "right.wav"))
sound_up=[]
sound_up.append(Loadsound("up","K_UP","up.wav"))
sound_down=[]
sound_down.append(Loadsound("down","K_DOWN","down.wav"))
while True:
for i in pygame.event.get():
if i.type==QUIT:
exit()
pressed=pygame.key.get_pressed()
if pressed[K_LEFT]:
for left in sound_left:
left.sound.play()
elif pressed[K_RIGHT]:
for right in sound_right:
right.sound.play()
elif pressed[K_UP]:
for up in sound_up:
up.sound.play()
elif pressed[K_DOWN]:
for down in sound_down:
down.sound.play()
Thank you for the help!
Edit:
I have been trying to use the Joystick module. Here is where I was taking example code from:
http://www.pygame.org/wiki/Joystick_analyzer
I edited my code to include some of it, but it still does not work. Here is my edited code:
import pygame, sys, time, random
from pygame.locals import *
from pygame import *
# set up pygame
try:
pygame.init()
pygame.display.init()
pygame.mixer.init(size=8, buffer=2048)
pygame.mixer.get_init
except:
print "error in init\n"
white=(255,255,255)
screen=pygame.display.set_mode((800,600), pygame.RESIZABLE)
class Loadsound:
def __init__(self, name, key, sound_file):
self.name = name
self.name=key
self.sound = pygame.mixer.Sound(sound_file)
sound_left=[]
sound_left.append(Loadsound("left","K_LEFT", "left.wav"))
sound_right=[]
sound_right.append(Loadsound("right","K_RIGHT", "right.wav"))
sound_up=[]
sound_up.append(Loadsound("up","K_UP","up.wav"))
sound_down=[]
sound_down.append(Loadsound("down","K_DOWN","down.wav"))
def __init__(self):
pygame.joystick.init()
self.my_joystick = None
self.joystick_names = []
for i in range(0, pygame.joystick.get_count()):
self.joystick_names.append(pygame.joystick.Joystick(i).get_name())
print self.joystick_names
if (len(self.joystick_names) > 0):
self.my_joystick = pygame.joystick.Joystick(0)
self.my_joystick.init()
#max_joy = max(self.my_joystick.get_numaxes(), self.my_joystick.get_numbuttons(), self.my_joystick.get_numhats()
while True:
for i in pygame.event.get():
pygame.event.pump()
if i.type==QUIT:
exit()
#pygame.joystick.Joystick(0)
#Joystick.init()
pressed=pygame.key.get_pressed()
#pressed_j=Joystick.get_hat()
def check_hat(self, p_hat):
if (self.my_joystick):
if (p_hat,self.my_joystick.get_numhats()):
return self.my_joystick.get_hat(p_hat)
return (0, 0)
if check_hat==(-1,0):
#if pressed[K_LEFT]:
for left in sound_left:
left.sound.play()
elif pressed[K_RIGHT]:
for right in sound_right:
right.sound.play()
elif pressed[K_UP]:
for up in sound_up:
up.sound.play()
elif pressed[K_DOWN]:
for down in sound_down:
down.sound.play()
For clarity, my code DOES work when using the keyboard. However, it does not translate to the controller which is mapped to the same keys.
You'll need to use Pygame's Joystick module, or something similar, which has method's such as: Joystick.get_init(), which tells if the joystick is initialized in Pygame, and Joystick.get_axis(axis_number) which returns the Joystick's position, as a float, along a given axis axis_number. ControlMK is likely mapping the joystick to key inputs at too high of a level to interact with Pygame, though there may be some way to change that, its documentation seems limited.
Try this:
import pygame
pygame.init()
print "Joystics: ", pygame.joystick.get_count()
my_joystick = pygame.joystick.Joystick(0)
my_joystick.init()
clock = pygame.time.Clock()
while 1:
for event in pygame.event.get():
print my_joystick.get_axis(0), my_joystick.get_axis(1)
clock.tick(40)
pygame.quit ()

Categories