Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
For our final project, we decided to create a game of WHACK-A-MOLE. Instead of the player hitting the mole, this time the player is the mole, and the game tries to hit you. There are two colors that the camera is able to detect: green and purple. The green circle triggers the game into attack mode. The purple circle triggers defense mode. The protect mode takes priority over the attack mode so if both are present, the game will favor protection.
The components used in the design of the project were a camera, two servo motors, a lever arm, a wooden base, and maker beam in addition to the Orange Pi and MSP430. The Orange Pi is powered using the HP Power Supply and the motors are powered using the +5V Power Supply.
The Orange Pi communicates with the MSP430 using I2C protocol to send a number of green or purple pixels as well as a voltage value corresponding to the position of the centroid of those pixels. This angle is the angle that the larger servo turns to when the number of pixels is beyond a certain threshold. At the same time, if the number of pixels is beyond a certain threshold, the hammer arm will actuate to either repeatedly strike the hole that the mole came through if it is green or hover over the hole if it is purple.
Overall, the design of the project was successful and we were able to operate with a decent amount of speed. We overcame several challenges such as identifying multiple colors and blocking the moles with the lever arm when it is lowered.
Orange Pi code
C/C++#include <stdio.h>
#include <opencv/cv.h>
#include <opencv2/videoio/videoio_c.h>
#include <math.h>
#include <highgui.h>
#include <sys/select.h>
#include <termios.h>
#include <stropts.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <time.h>
#define BILLION 1000000000L
#define I2C
#define ADDR 0x25
#ifdef I2C
#include "i2c.h"
#endif
// Simple structure to hold details about the framebuffer device after it has
// been opened.
typedef struct {
// Pointer to the pixels. Try to not write off the end of it.
uint32_t * buffer;
// The file descriptor for the device.
int fd;
// Number of bytes in the buffer. To work out how many elements are in the buffer, divide this by 4 (i.e. sizeof(uint32_t))
size_t screen_byte_size;
// Structs providing further information about the buffer configuration. See https://www.kernel.org/doc/Documentation/fb/api.txt
struct fb_fix_screeninfo fix_info;
struct fb_var_screeninfo var_info;
} screen_t;
// Because you can't have too many variants of NULL
static const screen_t NULL_SCREEN = {0};
// Indicates if the passed screen_t struct is valid.
#define valid_screen(s) ((s).buffer != NULL)
#define NUMFRAME_ROWS 800
#define NUMFRAME_COLS 480
#define FRAMEOFFSET_ROWS 150
#define FRAMEOFFSET_COLS 30
#define NUM_ROWS 120
#define NUM_COLS 160
#define PI 3.1415926
screen_t open_fb(void);
void close_fb(screen_t *screen);
int my_min(int a, int b, int c);
int my_max(int a, int b, int c);
void rgb2hsv(int red,int green,int blue,int *hue,int *sat,int *value);
int _kbhit(void);
int main(void) {
#ifdef I2C
int bus;
// add any other I2C variables here
#endif
//int bus;
unsigned char tx[8]= {0, 0, 0, 0, 0, 0, 0, 0};
unsigned char rx[8]= {0, 0, 0, 0, 0, 0, 0, 0};
long tx_long1 = 0;
long tx_long2 = 0;
long rx_long1 = 0;
long rx_long2 = 0;
uint64_t diff;
struct timespec current_time,previous_time;
int key=0;
CvCapture* capture;
IplImage *frame, *frame_hsv;
capture= cvCaptureFromCAM(-1);
cvSetCaptureProperty(capture,CV_CAP_PROP_FRAME_WIDTH, NUM_COLS); // Number of columns
cvSetCaptureProperty(capture,CV_CAP_PROP_FRAME_HEIGHT, NUM_ROWS); // Number of rows
if(!capture) printf("No camera detected \n");
frame=cvQueryFrame(capture);
frame_hsv=cvCreateImage(cvGetSize(frame),8,3);
#ifdef I2C
bus = i2c_start_bus(1);
#endif
screen_t screen = open_fb();
if (!valid_screen(screen)) {
printf("ERROR: Failed to open screen\n");
return 1;
}
float period=0,Hz=0;
unsigned char *data_hsv, *data_bgr, *current_pixel;
int r=0;
int c=0;
int nr;
int nc;
int celements;
long numpix = 1;
long rowsum = 0;
long colsum = 0;
long numpix2 = 1;
long rowsum2 = 0;
long colsum2 = 0;
double x_cen = 0;
double y_cen = 0;
double theta = 0;
double dummy_tan=1;
double x_cen2 = 0;
double y_cen2 = 0;
double theta2 = 0;
double dummy_tan2=1;
double test = 4;
nr=frame_hsv->height; //number of rows in image should be 120
nc=frame_hsv->width; //number of collumns
celements = nc*3; // b,g,r elements in each column
int vh=255, vl=176, sh=228, sl=150, hh=81, hl=58;
int vh2=255, vl2=73, sh2=124, sl2=53, hh2=251, hl2=205;
int h=0, s=0, v=0;
int blue=0, green=0, red=0;
clock_gettime(CLOCK_MONOTONIC, &previous_time);
while(! _kbhit()) { //27 is the code corresponding to escape key
frame=cvQueryFrame(capture);
if(!frame) break;
// -----loop through image pixels-----
data_bgr=(unsigned char *)(frame->imageData);
numpix = 0;
rowsum = 0;
colsum = 0;
numpix2 = 0;
rowsum2 = 0;
colsum2 = 0;
for(r=0; r<nr; r++) {
for(c=0; c<nc;c++) {
blue = (int)data_bgr[r*celements+c*3];
green = (int)data_bgr[r*celements+c*3+1];
red = (int)data_bgr[r*celements+c*3+2];
rgb2hsv(red,green,blue,&h,&s,&v);
//--------------process each thresholded pixel here------------------
//Testing Green Pixels
if( (h>=hl && h <= hh) && (s>=sl && s<=sh) && (v>=vl && v<=vh) ) {
green = 0;
blue = 255;
red = 0;
numpix++;
rowsum+=r;
colsum+=c;
}
//Testing purple pixels
if( (h>=hl2 && h <= hh2) && (s>=sl2 && s<=sh2) && (v>=vl2 && v<=vh2) ) {
green = 0;
blue = 0;
red = 255;
numpix2++;
rowsum2+=r;
colsum2+=c;
}
screen.buffer[(r+FRAMEOFFSET_ROWS)*NUMFRAME_COLS + (FRAMEOFFSET_COLS + c)] = (int)((red<<16) | (green<<8) | blue);
}
} //--------------end pixel processing---------------------
//Green
if (numpix!=0){
x_cen = (int) (colsum/numpix);
y_cen = (int) (rowsum/numpix);
if (x_cen==86){
x_cen = 87;
}
dummy_tan = y_cen/(x_cen-86);
theta =180/PI*atan2(y_cen,x_cen-86);
}
//Purple
if (numpix2!=0){
x_cen2 = (int) (colsum2/numpix2);
y_cen2 = (int) (rowsum2/numpix2);
if (x_cen2==86){
x_cen2 = 87;
}
dummy_tan2 = y_cen2/(x_cen2-86);
theta2 =180/PI*atan2(y_cen2,x_cen2-86);
}
if (x_cen<2||x_cen>nc-2||y_cen<2||y_cen>nr-2){
} else {
int x_cen;
int y_cen;
screen.buffer[(y_cen+FRAMEOFFSET_ROWS)*NUMFRAME_COLS + (FRAMEOFFSET_COLS + x_cen)] = 0;
screen.buffer[(y_cen+1+FRAMEOFFSET_ROWS)*NUMFRAME_COLS + (FRAMEOFFSET_COLS + x_cen)] = 0;
screen.buffer[(y_cen-1+FRAMEOFFSET_ROWS)*NUMFRAME_COLS + (FRAMEOFFSET_COLS + x_cen)] = 0;
screen.buffer[(y_cen+FRAMEOFFSET_ROWS)*NUMFRAME_COLS + (FRAMEOFFSET_COLS + 1 + x_cen)] = 0;
screen.buffer[(y_cen+FRAMEOFFSET_ROWS)*NUMFRAME_COLS + (FRAMEOFFSET_COLS - 1 + x_cen)] = 0;
}
if(numpix >20){
tx_long1 = 18.5*theta+1200;
}
if(numpix2<10){
tx_long2 = numpix;
}
if(numpix2>=20){
tx_long1 = 18.5*theta2+1200;
tx_long2 = numpix+2000;
}
tx[0]=tx_long1>>24;
tx[1]=tx_long1>>16;
tx[2]=tx_long1>>8;
tx[3]=tx_long1;
tx[4]=tx_long2>>24;
tx[5]=tx_long2>>16;
tx[6]=tx_long2>>8;
tx[7]=tx_long2;
i2c_write_bytes(bus, ADDR, tx, 8);
i2c_read_bytes(bus, ADDR,rx, 8);
rx_long1 = (((long)rx[0]<<24)+((long)rx[1]<<16)+((long)rx[2]<<8)+rx[3]);
rx_long2 = (((long)rx[4]<<24)+((long)rx[5]<<16)+((long)rx[6]<<8)+rx[7]);
clock_gettime(CLOCK_MONOTONIC, ¤t_time);
diff = BILLION * (current_time.tv_sec - previous_time.tv_sec) + current_time.tv_nsec - previous_time.tv_nsec;
period = diff * 1e-9;
if (period > 0.0) {
Hz = 1.0/period;
} else {
Hz = 0.0;
}
printf("Hz = %.4f %.4lf %.4lf %.4lf %ld\n",Hz,x_cen2,y_cen2,theta2, numpix2);
previous_time = current_time;
} // end while
cvReleaseCapture(&capture);
#ifdef I2C
i2c_close_bus(bus);
#endif
printf("All done, bye bye.\n");
close_fb(&screen);
return 0;
}
int my_min(int a, int b, int c) {
int min;
min = a;
if (b < min) {
min = b;
}
if (c < min) {
min = c;
}
return (min);
}
int my_max(int a, int b, int c) {
int max;
max = a;
if (b > max) {
max = b;
}
if (c > max) {
max = c;
}
return (max);
}
void rgb2hsv(int red,int green,int blue,int *hue,int *sat,int *value) {
int min, delta;
min = my_min( red, green, blue );
*value = my_max( red, green, blue );
delta = *value - min;
if( *value != 0 ) {
*sat = (delta*255) / *value; // s
if (delta != 0) {
if( red == *value )
*hue = 60*( green - blue ) / delta; // between yellow & magenta
else if( green == *value )
*hue = 120 + 60*( blue - red ) / delta; // between cyan & yellow
else
*hue = 240 + 60*( red - green ) / delta; // between magenta & cyan
if( *hue < 0 )
*hue += 360;
} else {
*hue = 0;
*sat = 0;
}
} else {
// r = g = b = 0
// s = 0, v is undefined
*sat = 0;
*hue = 0;
}
*hue = (*hue*255)/360;
}
/**
* Opens the first frame buffer device and returns a screen_t struct
* for accessing it. If the framebuffer isn't in the expected format
* (32 bits per pixel), NULL_SCREEN will be returned.
*/
screen_t open_fb(void) {
const char * const SCREEN_DEVICE = "/dev/fb0";
int screen_fd = open(SCREEN_DEVICE, O_RDWR);
if (screen_fd == -1) {
printf("ERROR: Failed to open %s\n", SCREEN_DEVICE);
return NULL_SCREEN;
}
struct fb_var_screeninfo var_info = {0};
if (ioctl(screen_fd, FBIOGET_VSCREENINFO, &var_info) == -1) {
printf("ERROR: Failed to get variable screen info\n");
return NULL_SCREEN;
}
struct fb_fix_screeninfo fix_info = {0};
if (ioctl(screen_fd, FBIOGET_FSCREENINFO, &fix_info) == -1) {
printf("ERROR: Failed to get fixed screen info\n");
return NULL_SCREEN;
}
if (var_info.bits_per_pixel != 32) {
printf("ERROR: Only support 32 bits per pixel. Detected bits per pixel: %d\n", var_info.bits_per_pixel);
return NULL_SCREEN;
}
const size_t screen_byte_size = var_info.xres * var_info.yres * var_info.bits_per_pixel / 8;
uint32_t * const buffer = (uint32_t *)mmap(NULL, screen_byte_size, PROT_READ | PROT_WRITE, MAP_SHARED, screen_fd, 0 /* offset */);
screen_t screen = {
.buffer = buffer,
.fd = screen_fd,
.screen_byte_size = screen_byte_size,
.fix_info = fix_info,
.var_info = var_info
};
return screen;
}
/**
* Closes the framebuffer when you are finished with it. Don't try
* to access things in the struct after calling this or else a
* kitten will die.
*/
void close_fb(screen_t *screen) {
munmap(screen->buffer, screen->screen_byte_size);
close(screen->fd);
*screen = NULL_SCREEN;
}
int _kbhit() {
static const int STDIN = 0;
static int initialized = 0;
if (! initialized) {
// Use termios to turn off line buffering
struct termios term;
tcgetattr(STDIN, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN, TCSANOW, &term);
setbuf(stdin, NULL);
initialized = 1;
}
int bytesWaiting;
ioctl(STDIN, FIONREAD, &bytesWaiting);
return bytesWaiting;
}
MSP430 Code
C/C++/******************************************************************************
MSP430F2272 Project Creator 4.0
ME 461 - S. R. Platt
Fall 2010
Updated for CCSv4.2 Rick Rekoske 8/10/2011
Written by: Steve Keres
College of Engineering Control Systems Lab
University of Illinois at Urbana-Champaign
*******************************************************************************/
#include "msp430x22x2.h"
#include "UART.h"
char newprint = 0;
unsigned long timecnt = 0;
int idx=0;
unsigned char RXData[8] = {0, 0, 0, 0, 0, 0, 0, 0} ;
unsigned char TXData[8] = {0, 0, 0, 0, 0, 0, 0, 0};
long rxlong1 = 0;
long rxlong2 = 0;
long txlong1 = 23461265;
long txlong2 = 345622;
int traj = 1;
char status = 0;
long numpix = 0;
void main(void) {
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
if (CALBC1_16MHZ ==0xFF || CALDCO_16MHZ == 0xFF) while(1);
DCOCTL = CALDCO_16MHZ; // Set uC to run at approximately 16 Mhz
BCSCTL1 = CALBC1_16MHZ;
//P1IN Port 1 register used to read Port 1 pins setup as Inputs
P1SEL &= ~0xFF; // Set all Port 1 pins to Port I/O function
P1REN &= ~0xFF; // Disable internal resistor for all Port 1 pins
P1DIR |= 0x3; // Set Port 1 Pin 0 and Pin 1 (P1.0,P1.1) as an output. Leaves Port1 pin 2 through 7 unchanged
P1OUT &= ~0xFF; // Initially set all Port 1 pins set as Outputs to zero
P1IFG &= ~0xFF; // Clear Port 1 interrupt flags
P1IES &= ~0xFF; // If port interrupts are enabled a High to Low transition on a Port pin will cause an interrupt
P1IE &= ~0xFF; // Disable all port interrupts
UCB0CTL1 = UCSWRST+UCSSEL_2; //put uscIBO into reset and set clock source to SMCLK
UCB0CTL0 = UCSYNC + UCMODE_3; //set USCIBO to synchronous mode and I2C mode
UCB0I2COA = 0x0025; //set the USCIBO's "Own Address" to 0x25. THis is the address your linux program needs to communicate with
UCB0CTL1 &= ~UCSWRST; //pulls the USCIBO out of reset
IE2 |= UCB0RXIE; // enables only the UCB0RXIE interrupt. the I2C recieve interrupt
IFG2 &= ~0xFF;
P3SEL |= 0x06;//set P3.1 and P3.2 as USCIBO pins
//Add your code here to initialize USCIB0 as an I2C slave
// Timer A Config
TACCTL0 = CCIE; // Enable Timer A interrupt
TACCR0 = 16000; // period = 1ms
TACTL = TASSEL_2 + MC_1; // source SMCLK, up mode
// Timer B Config
TBCTL = ID_3 + TASSEL_2 + MC_1; //Divide by 8
TBCCTL1 = OUTMOD_7;
TBCCTL2 = OUTMOD_7;
TBCCR0 = 40000; // period = 20ms
TBCCR1 = 1200;
TBCCR2 = 4700;
//Port 4
P4SEL = 0x06;
P4DIR = 0x06;
/* Uncomment this block when using the wireless modems on the robots
// Wireless modem control pins
P2SEL &= ~0xC0; // Digital I/O
P2REN &= ~0xC0; // Disable internal resistor for P2.6 and P2.7
P2OUT |= 0x80; // CMD/Data pin high for data
P2DIR |= 0x80; // Output direction for CMD/data pin (P2.7)
P2DIR &= ~0x40; // Input direction for CTS pin (P2.6)
P1IFG &= ~0xC0; // Clear Port P2.6 and P2.7 interrupt flags
P1IES &= ~0xC0; // If port interrupts are enabled a High to Low transition on a Port pin will cause an interrupt
P1IE &= ~0xC0; // Disable P2.6 and P2.7 interrupts
*/
Init_UART(115200,1); // Initialize UART for 9600 baud serial communication
_BIS_SR(GIE); // Enable global interrupt
while(1) {
if(newmsg) {
//my_scanf(rxbuff,&var1,&var2,&var3,&var4);
newmsg = 0;
}
if (newprint) {
//P1OUT ^= 0x1; // Blink LED
//UART_printf("R: %ld %ld T: %ld %ld \n\n\r",rxlong1, rxlong2, txlong1, txlong2);
UART_printf("TBCCR1 %ld \n\r",numpix);
// UART_send(1,(float)timecnt/500);
newprint = 0;
}
}
}
// Timer A0 interrupt service routine
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
timecnt++; // Keep track of time for main while loop.
if ((timecnt%500) == 0) {
newprint = 1; // flag main while loop that .5 seconds have gone by.
if (status){
status = 0;
} else{
status = 1;
}
}
}
/*
// ADC 10 ISR - Called when a sequence of conversions (A7-A0) have completed
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void) {
}
*/
// USCI Transmit ISR - Called when TXBUF is empty (ready to accept another character)
#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCI0TX_ISR(void) {
if((IFG2&UCA0TXIFG) && (IE2&UCA0TXIE)) { // USCI_A0 requested TX interrupt
if(printf_flag) {
if (currentindex == txcount) {
senddone = 1;
printf_flag = 0;
IFG2 &= ~UCA0TXIFG;
} else {
UCA0TXBUF = printbuff[currentindex];
currentindex++;
}
} else if(UART_flag) {
if(!donesending) {
UCA0TXBUF = txbuff[txindex];
if(txbuff[txindex] == 255) {
donesending = 1;
txindex = 0;
} else {
txindex++;
}
}
}
IFG2 &= ~UCA0TXIFG;
}
if((IFG2&UCB0RXIFG) && (IE2&UCB0RXIE)) { // USCI_B0 RX interrupt occurs here for I2C
// put your RX code here.
RXData[idx] = UCB0RXBUF;
TXData[idx] = RXData[idx];
idx++;
if(idx > 7){
idx = 0;
TXData[0] = txlong1>>24;
TXData[1] = txlong1>>16;
TXData[2] = txlong1>>8;
TXData[3] = txlong1;
TXData[4] = txlong2>>24;
TXData[5] = txlong2>>16;
TXData[6] = txlong2>>8;
TXData[7] = txlong2;
rxlong1 = (((long)RXData[0]<<24)+((long)RXData[1]<<16)+((long)RXData[2]<<8)+((long)RXData[3]));
TBCCR1 = (int) rxlong1;
rxlong2 = (((long)RXData[4]<<24)+((long)RXData[5]<<16)+((long)RXData[6]<<8)+((long)RXData[7]));
numpix = rxlong2;
if(numpix>20&&numpix<1000){
if(status){
TBCCR2 = 2350;
} else {
TBCCR2 = 4700;
}
}
if(numpix>1000){
TBCCR2 = 2450;
}
if(numpix<10){
TBCCR2 = 4700;
}
P1OUT ^= 0x10;
newprint = 1;
IE2 &= ~UCB0RXIE;
IE2 |= UCB0TXIE;
}
} else if ((IFG2&UCB0TXIFG) && (IE2&UCB0TXIE)) { // USCI_B0 TX interrupt
// put your TX code here.
UCB0TXBUF = TXData[idx];
idx++;
if(idx > 7){
idx = 0;
P1OUT ^= 0x20;
newprint = 1;
IE2 &= ~UCB0TXIE;
IE2 |= UCB0RXIE;
}
}
}
// USCI Receive ISR - Called when shift register has been transferred to RXBUF
// Indicates completion of TX/RX operation
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void) {
if((IFG2&UCA0RXIFG) && (IE2&UCA0RXIE)) { // USCI_A0 requested RX interrupt (UCA0RXBUF is full)
if(!started) { // Haven't started a message yet
if(UCA0RXBUF == 253) {
started = 1;
newmsg = 0;
}
} else { // In process of receiving a message
if((UCA0RXBUF != 255) && (msgindex < (MAX_NUM_FLOATS*5))) {
rxbuff[msgindex] = UCA0RXBUF;
msgindex++;
} else { // Stop char received or too much data received
if(UCA0RXBUF == 255) { // Message completed
newmsg = 1;
rxbuff[msgindex] = 255; // "Null"-terminate the array
}
started = 0;
msgindex = 0;
}
}
IFG2 &= ~UCA0RXIFG;
}
if((UCB0I2CIE&UCNACKIE) && (UCB0STAT&UCNACKIFG)) { // I2C NACK interrupt
UCB0STAT &= ~UCNACKIFG;
}
if((UCB0I2CIE&UCSTPIE) && (UCB0STAT&UCSTPIFG)) { // I2C Stop interrupt
UCB0STAT &= ~UCSTPIFG;
}
if((UCB0I2CIE&UCSTTIE) && (UCB0STAT&UCSTTIFG)) { // I2C Start interrupt
UCB0STAT &= ~UCSTTIFG;
}
if((UCB0I2CIE&UCALIE) && (UCB0STAT&UCALIFG)) { // I2C Arbitration Lost interrupt
UCB0STAT &= ~UCALIFG;
}
}
Comments