June 05, 2012
by ead3281
|
I just completed a project that uses an ultrasonic ping sensor. The distance from the sensor is used to position a paddle in a pong game. Pygame is used to make the pong interface. I used the code from a pygame tutorial example called chimp.py(see- www.pygame.org/docs/). It was great fun building the project and its a blast to play pong by running back and forth across the room. The code is shown below. Sorry if it is a little lengthy, I tried to insert comments to explain what was going on
Here's the C code for the microcontroller
// ping.c
// for NerdKits with ATmega168
//
//05/29/2012
//This code was written to operate a parallax ping sensor
//It implements a timer and external interrupts via pin PCO
//The ping sensor has only one data line so PCO must be operated
//both in read and write mode
//It ouputs the objects distance via the uart
#define F_CPU 14318180//Note I'm using a different crystal than supplied
//with nerdkit
#include <avr/pgmspace.h>//Needed for PSTR function
#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include "../libnerdkits/uart.h"
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
uint16_t counter=0;
uint16_t int_counter=0;//intervals between pings are 25ms (.01ms*2500)
double time;
ISR(TIMER1_COMPA_vect)//this is the interrupt handler, gets called by the timer
{
if (counter==0){
PCICR &= ~(1<<PCIE1);//Enables interrupts on PC0-PC7 see pg. 69
PCMSK1 &= ~(1<<PCINT8);//pin PC0 triggers interrupt
DDRC |= (1<<PC0);
PORTC |=(1<<PC0);
NOP;
//5 us pulse tells ping to output a sound pulse
delay_us(5);
DDRC &=~(1<<PC0);
PCICR |= (1<<PCIE1);//Enables interrupts on PC0-PC7 see pg. 69
PCMSK1 |= (1<<PCINT8);//pin PC0 triggers interrupt
}
counter++;
if (counter==2500){
counter=0;
}
}
ISR(PCINT1_vect){
if(counter>80){
//Ping sensor outputs a pulse whose length corresponds to distance of object
//The leading edge of the pulse occurs at .79 ms thats where the
//79.0(below) and 80 (above) come from
// The rest of the math is due to the speed of sound
//being 340 m/s. The .00001 refers to .01 ms which are the increments
//of the timer. The factor of two is due to the sound has to go to
//the sensed object and return to the sensor
float distance=(counter-79.0)*340*.00001/2;
printf_P(PSTR("%.2fM\r\n"),distance);
}
}
int main() {
// start up the serial port
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar,
_FDEV_SETUP_RW);
stdin=stdout=&uart_stream;
//I am going to add some code to try and generate a timestamp
TCCR1B |=(1<<WGM12);//Configure timer 1 for CTC mode
TIMSK1 |=(1<<OCIE1A);//Enable CTC interrupt
sei();//Enable global interrupts
OCR1A=143;//Set CTC compare value for .01 msec time steps
TCCR1B |=(1<<CS10);//Start timer at F oscillator
//Documentation for above can be found in ATMega guide
//TCCR1B pg 132; OCR1A pg 133;TIMSK1 pg 13
// loop keeps looking forever
while(1) {
}
return 0;
}
And here's the code for the pong game:
#!/usr/bin/env python
"""
Below is the comments from the original chimp.py
This simple example is used for the line-by-line tutorial
that comes with pygame. It is based on a 'popular' web banner.
Note there are comments here, but for the full explanation,
follow along in the tutorial.
Below are my comments
I modified the code to make a pong game. The paddle is controlled
via an ultrasonic ping parallax sensor. I never changed the pong ball
from the original Chimp class, so Chimp refers to the ball
"""
#Import Modules
import serial
import os, pygame
from pygame.locals import *
from pygame.compat import geterror
if not pygame.font: print ('Warning, fonts disabled')
if not pygame.mixer: print ('Warning, sound disabled')
main_dir = os.path.split(os.path.abspath(__file__))[0]
data_dir = os.path.join(main_dir, 'data')
#constants
PLAYER_SPEED = 10
global going
#functions to create our resources
def load_image(name, colorkey=None):
fullname = os.path.join(data_dir, name)
try:
image = pygame.image.load(fullname)
except pygame.error:
print ('Cannot load image:', fullname)
raise SystemExit(str(geterror()))
return image, image.get_rect()
class Paddle(pygame.sprite.Sprite):
"""moves a paddle across the screen.."""
def __init__(self):
pygame.sprite.Sprite.__init__(self) #call Sprite intializer
self.image, self.rect = load_image('paddle_v.bmp', -1)
global screen
screen = pygame.display.get_surface()
self.area = screen.get_rect()
self.rect.topleft = 5, 250
def move(self, direction):
self.rect = self.rect.move(0,-direction*PLAYER_SPEED).\
clamp(screen.get_rect())
class Chimp(pygame.sprite.Sprite):
"""This was the original code to move the monkey across the screen.
I am using it to move the pong ball acrosss the screen"""
def __init__(self):
self.PLAYER_SPEED = 10
self.B_SPEED=5
self.HITS=0
pygame.sprite.Sprite.__init__(self) #call Sprite intializer
self.image, self.rect = load_image('blip.bmp', -1)
screen = pygame.display.get_surface()
self.area = screen.get_rect()
self.rect.topleft = 787, 488
self.moveX=-self.B_SPEED
self.moveY=-self.B_SPEED
def collide(self):
self.moveX=self.B_SPEED
newpos = self.rect.move((self.moveX, self.moveY))
self.HITS=self.HITS+1
if (self.HITS==5):
self.B_SPEED=self.B_SPEED+1
self.HITS=0
def update(self):
test=self._walk()
def _walk(self):
state=0
"move the monkey across the screen, and turn at the ends"
newpos = self.rect.move((self.moveX,self.moveY))
if self.rect.left < self.area.left-25:
self.moveX=-self.B_SPEED
self.moveY=-self.B_SPEED
self.rect.topleft=787,200
self.B_SPEED=5
self.HITS=0
newpos = self.rect.move((self.moveX, self.moveY))
if self.rect.right > self.area.right:
self.moveX=-self.B_SPEED
newpos = self.rect.move((self.moveX, self.moveY))
if self.rect.top < self.area.top:
self.moveY = self.B_SPEED
newpos = self.rect.move((self.moveX, self.moveY))
if self.rect.bottom>self.area.bottom:
self.moveY = -self.B_SPEED
newpos = self.rect.move((self.moveX, self.moveY))
self.rect = newpos
return state
def main():
"""this function is called when the program starts.
it initializes everything it needs, then runs in
a loop until the function returns."""
#Initialize Everything
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption('Back to the Seventies')
pygame.mouse.set_visible(0)
#Create The Backgound
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((250, 250, 250))
#Put Text On The Background, Centered
if pygame.font:
font = pygame.font.Font(None, 20)
text = font.render("And we're off", 1, (10, 10, 10))
textpos = text.get_rect(centerx=background.get_width()/2)
background.blit(text, textpos)
#Display The Background
screen.blit(background, (0, 0))
pygame.display.flip()
#Prepare Game Objects
clock = pygame.time.Clock()
chimp = Chimp()
paddle=Paddle()
allsprites = pygame.sprite.RenderPlain( chimp,paddle)
#Main Loop
going = True
while going:
clock.tick(60)
#Handle Input Events
pygame.event.pump()
keystate = pygame.key.get_pressed()
if keystate[K_ESCAPE] or pygame.event.peek(QUIT):
going=False
direction = keystate[K_UP] - keystate[K_DOWN]
paddle.move(direction)
allsprites.update()
paddlerect=[paddle.rect]
x=chimp.rect.collidelist(paddlerect)
if x==0:
#print "we have contact"
chimp.collide()
#Draw Everything
screen.blit(background, (0, 0))
allsprites.draw(screen)
pygame.display.flip()
pygame.quit()
#Game Over
#this calls the 'main' function when this script is executed
if __name__ == '__main__':
main()
|
June 05, 2012
by ead3281
|
Ooops, I forgot I was modifying the pygame code so that the ball would speed up every five paddle hits and I had taken out the code that reads in the serial input. Here's what the main loop in the bottom of the pygame code should be to read the serial input and move the paddle
#Main Loop
going = True
s = serial.Serial("/dev/ttyUSB0", 115200)
while going:
x=s.readline()
str_m=x.split('M')
paddle_x= int(eval(str_m[0])*100)
clock.tick(60)
#Handle Input Events
pygame.event.pump()
keystate = pygame.key.get_pressed()
if keystate[K_ESCAPE] or pygame.event.peek(QUIT):
going=False
direction = keystate[K_UP] - keystate[K_DOWN]
paddle.move(direction)
allsprites.update()
paddlerect=[paddle.rect]
x=chimp.rect.collidelist(paddlerect)
if x==0:
print "we have contact"
chimp.collide()
#Draw Everything
paddle.rect.topleft = (10,600-(paddle_x-50)*3)
screen.blit(background, (0, 0))
allsprites.draw(screen)
pygame.display.flip()
pygame.quit()
#Game Over
#this calls the 'main' function when this script is executed
if __name__ == '__main__':
main()
Sorry for the earlier mix-up |