Renamed to program to Doom_Controller.

Dependencies:   DebounceIn USBDevice mbed

Fork of BNO055_reader by Ben, Simon, Inez IDD

main.cpp

Committer:
simonscott
Date:
2015-09-20
Revision:
2:e585627bae99
Parent:
1:38cd433ff221

File content as of revision 2:e585627bae99:

/**
 * A "gun" game controller for playing first-person shooters like Doom II.
 * Written by Ben, Inez and Simon for IDD 2015.
 */

#include "mbed.h"
#include "USBMouseKeyboard.h"
#include "DebounceIn.h"

//LEDs to indicate clibration status
DigitalOut redLED(LED_RED);
DigitalOut greenLED(LED_GREEN);

//pushbuttons inputs
DebounceIn trigger(D3);
DebounceIn move(D4);
DebounceIn door(D5);
DebounceIn centerBut(D12);

// Motor output
DigitalOut vibrate(D2);
Timeout vibrateTimeout;
 
//USBMouseKeyboard
USBMouseKeyboard key_mouse(ABS_MOUSE);
HID_REPORT kbHIDReport;;

// Communication busse
Serial pc(USBTX,USBRX);
I2C i2c(D7, D6);

// Constant declarations for the accelerometer chip
const int bno055_addr = 0x28 << 1;

const int BNO055_ID_ADDR                                          = 0x00;
const int BNO055_EULER_H_LSB_ADDR                                 = 0x1A;
const int BNO055_GRAVITY_DATA_X_LSB_ADDR                          = 0x2E;
const int BNO055_TEMP_ADDR                                        = 0x34;
const int BNO055_OPR_MODE_ADDR                                    = 0x3D;
const int BNO055_CALIB_STAT_ADDR                                  = 0x35;
const int BNO055_SYS_STAT_ADDR                                    = 0x39;
const int BNO055_SYS_ERR_ADDR                                     = 0x3A;
const int BNO055_AXIS_MAP_CONFIG_ADDR                             = 0x41;
const int BNO055_SYS_TRIGGER_ADDR                                 = 0x3F;
const int BNO055_ACC_DATA_X_ADDR                                  = 0x08;

typedef struct CalibStatus_t
{
    int mag;
    int acc;
    int gyr;
    int sys;
} CalibStatus;

typedef struct Euler_t
{
    float heading;
    float pitch;
    float roll;
} Euler;

// The "zero" offset positions
short int headingOffset;
short int pitchOffset;
short int rollOffset;


/**
 * Function to write to a single 8-bit register
 */
void writeReg(int regAddr, char value)
{
    char wbuf[2];
    wbuf[0] = regAddr;
    wbuf[1] = value;
    i2c.write(bno055_addr, wbuf, 2, false);  
}

/**
 * Function to read from a single 8-bit register
 */
char readReg(int regAddr)
{
    char rwbuf = regAddr;
    i2c.write(bno055_addr, &rwbuf, 1, false);
    i2c.read(bno055_addr, &rwbuf, 1, false);
    return rwbuf;
}

/**
 * Returns the calibration status of each component
 */
CalibStatus readCalibrationStatus()
{
    CalibStatus status;
    int regVal = readReg(BNO055_CALIB_STAT_ADDR);
        
    status.mag = regVal & 0x03;
    status.acc = (regVal >> 2) & 0x03;
    status.gyr = (regVal >> 4) & 0x03;
    status.sys = (regVal >> 6) & 0x03;
    
    return status;
}


/**
 * Returns true if all the devices are calibrated
 */
bool calibrated()
{
    CalibStatus status = readCalibrationStatus();
    
    if(status.mag == 3 && status.acc == 3 && status.gyr == 3)
        return true;
    else
        return false;
}
  
 
/**
 * Checks that there are no errors on the accelerometer
 */
bool bno055Healthy()
{
    int sys_error = readReg(BNO055_SYS_ERR_ADDR);
    wait(0.001);
    int sys_stat = readReg(BNO055_SYS_STAT_ADDR);
    wait(0.001);
    
    if(sys_error == 0 && sys_stat == 5)
        return true;
    else {
        pc.printf("SYS_ERR: %d SYS_STAT: %d\r\n", sys_error, sys_stat);
        return false;
    }
}
    

/**
 * Configure and initialize the BNO055
 */
bool initBNO055()
{
    unsigned char regVal;
    i2c.frequency(400000);
    bool startupPass = true;
    
    // Do some basic power-up tests
    regVal = readReg(BNO055_ID_ADDR);
    if(regVal == 0xA0)
        pc.printf("BNO055 successfully detected!\r\n");
    else {
        pc.printf("ERROR: no BNO055 detected\r\n");
        startupPass = false;
    }
        
    regVal = readReg(BNO055_TEMP_ADDR);
    pc.printf("Chip temperature is: %d C\r\n", regVal);
    
    if(regVal == 0)
        startupPass = false;
 
    // Change mode to CONFIG
    writeReg(BNO055_OPR_MODE_ADDR, 0x00);
    wait(0.2);
    
    regVal = readReg(BNO055_OPR_MODE_ADDR);
    pc.printf("Change to mode: %d\r\n", regVal);
    wait(0.1);
    
    // Remap axes
    writeReg(BNO055_AXIS_MAP_CONFIG_ADDR, 0x06);    // b00_00_01_10
    wait(0.1);    

    // Set to external crystal
    writeReg(BNO055_SYS_TRIGGER_ADDR, 0x80);
    wait(0.2);    

    // Change mode to NDOF
    writeReg(BNO055_OPR_MODE_ADDR, 0x0C);
    wait(0.2);
 
    regVal = readReg(BNO055_OPR_MODE_ADDR);
    pc.printf("Change to mode: %d\r\n", regVal);
    wait(0.1);
    
    return startupPass;
}

/**
 * Sets the current accelerometer position as the zero position.
 */
void setZeroPosition()
{
    char buf[16];
    
    // Read the current euler angles and set them as the zero position
    buf[0] = BNO055_EULER_H_LSB_ADDR;
    i2c.write(bno055_addr, buf, 1, false);
    i2c.read(bno055_addr, buf, 6, false);
        
    headingOffset = buf[0] + (buf[1] << 8);
    rollOffset = buf[2] + (buf[3] << 8);
    pitchOffset = buf[4] + (buf[5] << 8);
}


/**
 * Sets the current accelerometer position as the zero position.
 */
void setZeroHeading()
{
    char buf[16];
    
    // Read the current euler angles and set them as the zero position
    buf[0] = BNO055_EULER_H_LSB_ADDR;
    i2c.write(bno055_addr, buf, 1, false);
    i2c.read(bno055_addr, buf, 6, false);
        
    headingOffset = buf[0] + (buf[1] << 8);
}


/**
 * Reads the Euler angles, zeroed out
 */
Euler getEulerAngles()
{
    char buf[16];
    Euler e;
    
    // Read in the Euler angles
    buf[0] = BNO055_EULER_H_LSB_ADDR;
    i2c.write(bno055_addr, buf, 1, false);
    i2c.read(bno055_addr, buf, 6, false);
    
    short int euler_head = buf[0] + (buf[1] << 8);
    short int euler_roll = buf[2] + (buf[3] << 8);
    short int euler_pitch = buf[4] + (buf[5] << 8);
    
    e.heading = ((int)euler_head - (int)headingOffset) / 16.0;
    e.roll = ((int)euler_roll - (int)rollOffset) / 16.0;
    e.pitch = ((int)euler_pitch - (int)pitchOffset) / 16.0;
    
    if(e.pitch > 90 || e.pitch < -90)
        e.pitch = 0;
    
    // Occassionally the heading goes into 180..360 degrees, need to wrap around
    if(e.heading > 180)
        e.heading = e.heading - 360;;
    
    return e;
}


/***** Functions to vibrate the motor (non-blocking call) *****/
void vibrateOff()
{
    vibrate = 0;
}

void vibrateMotor()
{
    vibrate = 1;
    vibrateTimeout.attach(&vibrateOff, 0.1);
}


/**
 * The main function
 */
int main() {
    
    // Declare variables
    uint16_t x_center = (X_MAX_ABS - X_MIN_ABS)/2;
    uint16_t y_center = (Y_MAX_ABS - Y_MIN_ABS)/2;
    uint16_t x_screen = 0;
    uint16_t y_screen = 0;
    bool startupPassed;
    Euler e;
    bool down;
    CalibStatus calStat;

    // Record old state of buttons
    bool triggerPressed = false;
    bool movePressed = false;

    // USB HID report to send move up/down
    kbHIDReport.length = 4;
    kbHIDReport.data[0] = 1;        // USB ID
    kbHIDReport.data[1] = 0;        // modifier key
    kbHIDReport.data[2] = 0;        // don't know

    // Initialize
    pc.baud(115200);
    trigger.mode(PullUp);
    move.mode(PullUp);
    door.mode(PullUp);
    centerBut.mode(PullUp);
    wait(0.8);
    startupPassed = initBNO055();   // Note: set LED to RED if this fails
    redLED = 0;
    
    // Wait until calibration passes
    while(!calibrated()){
        wait(0.1);
        calStat = readCalibrationStatus();
        printf("MAG: %d ACC: %d GYR: %d SYS: %d\r\n", calStat.mag, calStat.acc, calStat.gyr, calStat.sys);      
        wait(0.5); 
    }
    redLED = 1;
    greenLED = 0;   
     
    pc.printf("Board fully calibrated!\r\n");
    
    // Wait until user hits the trigger. Then zero out the readings
    while(trigger.read() == 1) {
        wait(0.01);
    }
    setZeroPosition();

    // Read orientation values
    while(true)
    {
        // Make sure that there are no errors
        if(!bno055Healthy())
        {
            //wait(0.1);
            //calStat = readCalibrationStatus();
            //pc.printf("Heading: %7.2f \tRoll: %7.2f \tPitch: %7.2f Down: %d \tMAG: %d ACC: %d GYR: %d SYS: %d\r\n", e.heading, e.roll, e.pitch,down, calStat.mag, calStat.acc, calStat.gyr, calStat.sys); 
            pc.printf("ERROR: BNO055 has an error/status problem!!!\r\n");
        }
        
        // Read in the Euler angles
        e = getEulerAngles();
        wait(0.001);
 
        // Read in the calibration status
        calStat = readCalibrationStatus();
        wait(0.001);
        
        // LED red if not calibrated else green
        if(!calibrated())
        {
            redLED = 0;
            greenLED = 1;
        }
        else
        {
            redLED = 1;
            greenLED = 0;
        }
        wait(0.001);
        
        // If trigger state changed
        if (!trigger.read() != triggerPressed)
        {
            // If trigger pressed
            if(!trigger.read())
            {
                kbHIDReport.data[3] = 0x07;     // D key press
                vibrateMotor();
                triggerPressed = true;
            }
            
            else {
                triggerPressed = false;
                kbHIDReport.data[3] = 0x00;     // UP arrow
            }
            key_mouse.sendNB(&kbHIDReport);
        }

        // If the door open button was pressed
        if (!door.read()){
            int counter = 0;
            while(counter<50){
                key_mouse.keyCode(' ');
                counter++;
                wait(0.001);
            }
            wait(0.2);
        }
        
        // If the re-center button was pressed
        if(!centerBut.read())
        {
            wait(0.01);
            setZeroPosition();
            wait(0.1);
        }

        // If move button state changed
        if (!move.read() != movePressed)
        {
            // If move pressed
            if(!move.read()) {
                kbHIDReport.data[3] = 0x52;     // UP arrow
                movePressed = true;
            }
            
            else {
                kbHIDReport.data[3] = 0x00;     // no press
                movePressed = false;
            }
            
            key_mouse.sendNB(&kbHIDReport);
        }

        // Limit how much we can move the mouse
        // No motion beyond 90 degrees, deadband between -2 and +2 degrees, and parabolic beyond 20 degrees.
        float heading_limited;
        if(e.heading > 90)
            heading_limited = 90;
        else if(e.heading < -90)
            heading_limited = -90;
        else if(e.heading < 2 && e.heading > -2)
            heading_limited = 0;
        else if(e.heading > -20 && e.heading < 20)
            heading_limited = 16 * e.heading;
        else
            heading_limited = 0.8 * e.heading * abs(e.heading);

        //moving the mouse now
        x_screen = x_center + heading_limited;
        y_screen = y_center;
        
        printf("Heading: %7.2f \tRoll: %7.2f \tPitch: %7.2f Down: %d \tMAG: %d ACC: %d GYR: %d SYS: %d\r\n", e.heading, e.roll, e.pitch,
            down, calStat.mag, calStat.acc, calStat.gyr, calStat.sys);
            
        key_mouse.move(x_screen, y_screen);
        wait(0.02);
    }
}