void dump_sensors(uint16_t digital, uint8_t analog) {
    uint8_t pin;
    uint8_t value;
    while(1) {
        if (USART_RXBufferData_Available(&BT_data) && '*' == USART_RXBuffer_GetByte(&BT_data))
            return;
        bt_putchar(':');
        for (pin=0; pin<10; pin++) 
            if (digital & (1<<pin))
                bt_putchar(read_input('0'+pin)+48);
        for (pin=0; pin<6; pin++) 
            if (analog & (1<<pin)) {
                value = read_analog(pgm_read_byte_near(muxPosPins+pin), 0);
                put_hex_nibble(value>>4);
                put_hex_nibble(value&0xF);
            }
        _delay_ms(5);
    }
int main(void)
{
        uint8_t sensor; // Stores analog readings for transmission
        int i; // multipurpose counter
        char choice = 0; // Holds the top-level menu character
//      int red = 0;     // For the red LED intensity
//      int green = 0;   // For the green LED intensity
//      int blue = 0;    // For the blue LED intensity
        uint8_t exit = 0;    // Flag that gets set if we need to go back to idle mode.
        unsigned int temph=0;  // Temporary variable (typically stores high byte of a 16-bit int )
        unsigned int templ=0;  // Temporary variable (typically stores low byte of a 16-bit int)
        uint8_t location = 0;  // Holds the EEPROM location of a stored IR signal
//      unsigned int frequency = 0; // For PWM control - PWM frequency, also used to set buzzer frequency
//        int amplitude;
        int channel;
        unsigned int duty;      // For PWM control - PWM duty cycle

        int baud;   // Baud rate selection for auxiliary UART
        char scale;  // For auxiliary UART

        long int time_out=0; // Counter which counts to a preset level corresponding to roughly 1 minute

        // Initialize system
        
        // Turn off JTAG to save a bit of battery life
        CCP = CCP_IOREG_gc;    
        MCU.MCUCR = 1;

        init_clock();

        init_led();

        init_adc();

        init_ir();

        init_BT();

        init_dac();

        init_buzzer();

        initAccel();

        init_aux_uart(131, -3); // Set the auxiliary uart to 115200 8n1

        EEPROM_DisableMapping();

        // Enable global interrupts
        sei();

        // Do the following indefinitely
        while(1) {
            // Turn off LED
                set_led(0,0,0);

                // Reset flags (in case this isn't the first time through the loop)
                exit = 0;
                choice = 0;
                time_out = 0;
                stop_ir_timer();

                // Sing a BL song in idle mode so you can be found. Stop as soon as you get a *
                while(choice != 42) {
                        bt_putchar('B');
                        bt_putchar('L');
                        if (USART_RXBufferData_Available(&BT_data)) {
                                choice = USART_RXBuffer_GetByte(&BT_data);
                                if (choice == 128) {
                                    // Something is trying to connect directly to an iRobot
                                    set_aux_baud_rate( ROOMBA_UART_SETTINGS ); // depends on model
                                    aux_putchar( 128); // pass through to iRobot
                                    serial_bridge(); // currently never returns
                                }
                                else if (choice == 0) {
                                    // Something may be trying to connect directly to a MindFlex headset
                                    set_aux_baud_rate( 135, -2); // 57600
                                    aux_putchar( 0); // pass through to headset
                                    serial_bridge(); // currently never returns;
                                }
                                else {
                                    bt_putchar(choice);
                                }
                        }
                        if (choice != 42)
                            _delay_ms(500);
                }

                // Active part of the program - listens for commands and responds as necessary
                while(exit == 0) {
                        // Checks if we haven't heard anything for a long time, in which case we exit loop and go back to idle mode
                        time_out++;
                        // Corresponds to roughly 60 seconds
                        if(time_out > 33840000) {
                                exit = 1;
                        }

                        // Check for a command character
                        if (USART_RXBufferData_Available(&BT_data)) {
                                choice = USART_RXBuffer_GetByte(&BT_data);
                        }
                        else {
                                choice = 0;
                        }
                        // If it exists, act on command
                        if(choice != 0) {
                                // Reset the time out
                                time_out = 0;
                                // Return the command so the host knows we got it
                                bt_putchar(choice);

                                // Giant switch statement to decide what to do with the command
                                switch(choice) {
                                        // Return the currect accelerometer data - X, Y, Z, and status (contains tapped and shaken bits)
                                        case 'A':
                                                updateAccel();
                                                bt_putchar(_acc.x);
                                                bt_putchar(_acc.y);
                                                bt_putchar(_acc.z);
                                                bt_putchar(_acc.status);
                                                break;
                                        // Set the buzzer
                                        case 'B': // frequency_divider(2)
                                                if (get_arguments(2)) {
                                                    set_buzzer(GET_16BIT_ARGUMENT(0));
                                                }
                                                break;
                                        // Turn off the buzzer
                                        case 'b':
                                                turn_off_buzzer();
                                                break;
                                        // Returns the value of the light sensor
                                        case 'L':
                                                sensor = read_analog(LIGHT, 0);
                                                bt_putchar(sensor);
                                                break;
                                        // Returns the Xmegas internal temperature read - this is undocumented because the value returned is very erratic
                                        case 'T':
                                                sensor = read_internal_temperature();
                                                bt_putchar(sensor);
                                                break;
                                        // Returns the battery voltage
                                        case 'V':
                                                sensor = read_analog(BATT_VOLT, 0);
                                                bt_putchar(sensor);
                                                break;
                                        // Differential ADC mode
                                        case 'D': // active(1) numgainstages(1)
                                                if (get_arguments(2)) {
                                                    if (arguments[0] == '0') {
                                                        adcResolution = ADC_RESOLUTION_8BIT_gc;
                                                        adcConvMode = ADC_ConvMode_Unsigned;
                                                        adcGain = ADC_DRIVER_CH_GAIN_NONE;
                                                        adcInputMode = ADC_CH_INPUTMODE_SINGLEENDED_gc;
                                                        init_adc();
                                                    }
                                                    else if (arguments[0] == '1') {
                                                        adcResolution = ADC_RESOLUTION_12BIT_gc;
                                                        adcConvMode = ADC_ConvMode_Signed;

                                                        if (arguments[1] >= '1' && arguments[1] <= '6') { // number of gain stages
                                                            adcGain = (arguments[1] - '0') << ADC_CH_GAINFAC_gp;
                                                            adcInputMode = ADC_CH_INPUTMODE_DIFFWGAIN_gc;
                                                            init_adc();
                                                        }
                                                        else if (arguments[1] == '0') {
                                                            adcGain = ADC_DRIVER_CH_GAIN_NONE;
                                                            adcInputMode = ADC_CH_INPUTMODE_DIFF_gc;
                                                            init_adc();
                                                        }
                                                        else {
                                                            err();
                                                        }
                                                    }
                                                    else {
                                                        err();
                                                    }
                                                }
                                                break;
                                        // Returns the readings on all six ADC ports
                                        case 'X':
                                                for (i=0; i<6; i++) {
                                                    sensor = read_analog(pgm_read_byte_near(muxPosPins+i), 0);
                                                    bt_putchar(sensor);
                                                }
                                                break;
                                        // Returns differential measurement on pair of ADC ports
                                        // Assumes we are in differential mode
                                        case 'x':
                                                if (get_arguments(2)) {
                                                    i = read_differential(arguments[0], arguments[1]);
                                                    bt_putchar(0xFF&(i >> 8));
                                                    bt_putchar(0xFF&i);
                                                }
                                                break;

                                        case 'e': // this is a bit of testing code, which will eventually be changed
                                                  // do not rely on it
                                                if (get_arguments(2)) {
                                                  char a1 = arguments[0];
                                                  char a2 = arguments[1];
                                                  while(1) {
                                                      _delay_ms(2);
                                                      i = read_differential(a1, a2);
                                                      itoa((i<<4)>>4, (char*)arguments, 10);
                                                      for (i=0; arguments[i]; i++)
                                                          bt_putchar(arguments[i]);
                                                      bt_putchar('\r');
                                                      bt_putchar('\n');
                                                  }
                                                }
                                                break;

                                        // Sets the full-color LED
                                        case 'O': // red(1) green(1) blue(1);
                                                if (get_arguments(3)) {
                                                    set_led(arguments[0], arguments[1], arguments[2]);
                                                }
                                                break;
                                        // Switches serial between irDA and standard serial
                                        // Currently, this works over the same port numbers as
                                        // standard serial, instead of using the IR receiver
                                        // and IR LED.  This may change when I get the IR receiver
                                        // and LED working reliably.
                                        case 'J':
                                                temph = bt_getchar_timeout_echo();
                                                if(/*temph == 256 || */ temph < '0' || temph > '1') {
                                                        err();
                                                        break;
                                                }
                                                set_irda_mode(temph - '0');
                                                break;

                                        // Sets up the IR transmitter with signal characteristics
                                        case 'I':
                                                init_ir();
                                                temph = bt_getchar_timeout_echo();
                                                if(temph == 256) {
                                                        err();
                                                        break;
                                                }
                                                templ = bt_getchar_timeout_echo();
                                                if(templ == 256) {
                                                        err();
                                                        break;
                                                }
                                                // Set the frequency of the IR carrier
                                                robotData.frequency = ((temph)<<8) + templ;
                                                set_ir_carrier(robotData.frequency);
                                                templ = bt_getchar_timeout_echo();
                                                if(templ == 256) {
                                                        err();
                                                        break;
                                                }
                                                else {
                                                // Set the length of the start up pulses
                                                        robotData.startUpPulseLength = templ;
                                                }
                                                if(robotData.startUpPulseLength > 16) {
                                                        err();
                                                        break;
                                                }

                                                // Read in the start up pulse timing data
                                                for(i=0; i < robotData.startUpPulseLength; i++) {
                                                        temph = bt_getchar_timeout_echo();
                                                        if(temph == 256) {
                                                                err();
                                                                break;
                                                        }
                                                        templ = bt_getchar_timeout_echo();
                                                        if(templ == 256) {
                                                                err();
                                                                break;
                                                        }
                                                        robotData.startUpPulse[i] = ((temph)<<8) + templ;
                                                }
                                                if(temph == 256 || templ == 256) {
                                                        break;
                                                }
                                                templ = bt_getchar_timeout_echo();
                                                if(templ == 256) {
                                                        err();
                                                        break;
                                                }
                                                // Set the bit encoding to one of four pre-determined settings (see protocol instructions for more information)
                                                robotData.bitEncoding = templ;

                                                templ = bt_getchar_timeout_echo();
                                                if(templ == 256) {
                                                        err();
                                                        break;
                                                }
                                                // Set the number of bits (and bytes) contained in an IR command
                                                robotData.numBits = templ;
                                                robotData.numBytes = (robotData.numBits-1)/8 + 1;
                                                temph = bt_getchar_timeout_echo();
                                                if(temph == 256) {
                                                        err();
                                                        break;
                                                }
                                                templ = bt_getchar_timeout_echo();
                                                if(templ == 256) {
                                                        err();
                                                        break;
                                                }
                                                // Set timing data for a high bit
                                                robotData.highBitTime = ((temph)<<8) + templ;
                                                temph = bt_getchar_timeout_echo();
                                                if(temph == 256) {
                                                        err();
                                                        break;
                                                }
                                                templ = bt_getchar_timeout_echo();
                                                if(temph == 256) {
                                                        err();
                                                        break;
                                                }
                                                // Set timing data for a low bit
                                                robotData.lowBitTime = ((temph)<<8) + templ;
                                                temph = bt_getchar_timeout_echo();
                                                if(temph == 256) {
                                                        err();
                                                        break;
                                                }
                                                templ = bt_getchar_timeout_echo();
                                                if(templ == 256) {
                                                        err();
                                                        break;
                                                }
                                                // Set timing data for on or off
                                                robotData.offTime = ((temph)<<8) + templ;
                                                break;
                                        // Transmit an IR signal according to the previously determined configuration
                                        case 'i':
                                                init_ir();
                                                // Get the signal data as one or more bytes
                                                for(i = 0; i < robotData.numBytes; i++) {
                                                        templ = bt_getchar_timeout_echo();
                                                        if(templ == 256) {
                                                                err();
                                                                break;
                                                        }
                                                        robotData.irBytes[i] = templ;
                                                }
                                                if(templ == 256) {
                                                        break;
                                                }
                                                temph = bt_getchar_timeout_echo();
                                                if(temph == 256) {
                                                        err();
                                                        break;
                                                }
                                                templ = bt_getchar_timeout_echo();
                                                if(templ == 256) {
                                                        err();
                                                        break;
                                                }
                                                // Determine if the signal is repeated or not, and if so, with what frequency
                                                robotData.repeatTime = ((temph)<<8) + templ;
                                                if(robotData.repeatTime != 0) {
                                                        robotData.repeatFlag = 1;
                                                }
                                                else {
                                                        robotData.repeatFlag = 0;
                                                }
                                                // Startup timer interrupts
                                                start_ir_timer();
                                                break;
                                        // Turn off any repeating IR signal
                                        case '!':
                                                robotData.repeatFlag = 0;
                                                stop_ir_timer();
                                                break;
                                        // Capture a signal from the IR receiver
                                        case 'R':
                                                init_ir_read();
                                                while(ir_read_flag!=0);
                                                break;
                                        // Store the captured signal in an EEPROM location
                                        case 'S':
                                                location = bt_getchar_timeout_echo()-48; // Subtracting 48 converts from ASCII to numeric numbers
                                                if((location >= 0) && (location < 5) && (signal_count > 4)) {
                                                        write_data_to_eeprom(location);
                                                }
                                                else {
                                                        err();
                                                }
                                                break;
                                        // Receive a raw IR signal over bluetooth and transmit it with the IR LED
                                        case 's':
                                                if(read_data_from_serial()) {
                                                        temph = bt_getchar_timeout_echo();
                                                        if(temph == 256) {
                                                                err();
                                                                break;
                                                        }
                                                        templ = bt_getchar_timeout_echo();
                                                        if(templ == 256) {
                                                                err();
                                                                break;
                                                        }
                                                        // Set if the signal should repeat and if so, with what frequency
                                                        robotData.repeatTime = ((temph)<<8) + templ;
                                                        if(robotData.repeatTime != 0) {
                                                                robotData.repeatFlag = 1;
                                                        }
                                                        else {
                                                                robotData.repeatFlag = 0;
                                                        }
                                                        // Set frequency to 38KHz, since raw signals must have come from the receiver at some point
                                                        robotData.frequency = 0x0349;
                                                        robotData.startUpPulseLength = 0;
                                                        robotData.bitEncoding = 0x04;
                                                        start_ir_timer();
                                                }
                                                else {
                                                                err();
                                                                break;
                                                }
                                                break;
                                        // Get a stored signal from an EEPROM location and transmit it over the IR LED (and repeat as desired)
                                        case 'G':
                                                location = bt_getchar_timeout_echo()-48;
                                                if(location >= 0 && location < 5) {
                                                        temph = bt_getchar_timeout_echo();
                                                        if(temph == 256) {
                                                                err();
                                                                break;
                                                        }
                                                        templ = bt_getchar_timeout_echo();
                                                        if(templ == 256) {
                                                                err();
                                                                break;
                                                        }
                                                        robotData.repeatTime = ((temph)<<8) + templ;
                                                        if(robotData.repeatTime != 0) {
                                                                robotData.repeatFlag = 1;
                                                        }
                                                        else {
                                                                robotData.repeatFlag = 0;
                                                        }
                                                        read_data_from_eeprom(location);
                                                        robotData.frequency = 0x0349;
                                                        robotData.startUpPulseLength = 0;
                                                        robotData.bitEncoding = 0x04;
                                                        start_ir_timer();
                                                }
                                                else {
                                                        err();
                                                }
                                                break;
                                        // Get a stored signal from EEPROM and print it over bluetooth to the host
                                        case 'g':
                                                location = bt_getchar_timeout_echo()-48;
                                                if(location >= 0 && location < 5) {
                                                        print_data_from_eeprom(location);
                                                }
                                                else {
                                                        err();
                                                }
                                                break;
                                                // Output on digital I/O
                                        case '>':
                                                // Set port
                                                temph = bt_getchar_timeout_echo();
                                                if(temph == 256) {
                                                        err();
                                                        break;
                                                }
                                                // Get value
                                                templ = bt_getchar_timeout_echo();
                                                if(templ == 256) {
                                                        err();
                                                        break;
                                                }
                                                set_output(temph, (templ-48));
                                                break;
                                                // Input on digital I/O
                                        case '<':
                                                // Get port
                                                temph = bt_getchar_timeout_echo();
                                                if(temph == 256) {
                                                        err();
                                                        break;
                                                }
                                                // Get value (1 or 0)
                                                templ = read_input(temph)+48;
                                                bt_putchar(templ);
                                                break;
                                        // Configure PWM frequency
                                        case 'P':
                                                if (get_arguments(2))
                                                    pwm_frequency = GET_16BIT_ARGUMENT(0);
                                                break;
                                        // Set PWM duty cycle for a specific port
                                        case 'p':
                                                if (get_arguments(3)) {
                                                    set_pwm();
                                                    duty = GET_16BIT_ARGUMENT(1);
                                                    if (arguments[0] == '0')
                                                        set_pwm0(duty);
                                                    else if (arguments[1] == '1')
                                                        set_pwm1(duty);
                                                    // could add else err();, but original firmware doesn't do this
                                                }
                                                break;
                                        // Set DAC voltage on one of the two DAC ports
                                        case 'd':
                                                temph = bt_getchar_timeout_echo();
                                                if(temph == 256) {
                                                        err();
                                                        break;
                                                }
                                                        if(temph == '0') {
                                                                temph = bt_getchar_timeout_echo();
                                                                if(temph == 256) {
                                                                        err();
                                                                        break;
                                                                }
                                                                else {
                                                                        set_dac0(temph);
                                                                }
                                                        }
                                                        else if(temph == '1') {
                                                                temph = bt_getchar_timeout_echo();
                                                                if(temph == 256) {
                                                                        err();
                                                                        break;
                                                                }
                                                                else {
                                                                        set_dac1(temph);
                                                                }
                                                        }
                                                break;
                                                // Go back to idle mode so you can be found again. Should be sent at end of host program.
                                        case 'Q':
                                                exit = 1;
                                                break;
                                                // Human-readable uart speed setting
                                        case 'u':
                                                temph = bt_getchar_timeout_echo();
                                                if(temph == 256) {
                                                        err();
                                                        break;
                                                }
                                                templ = bt_getchar_timeout_echo();
                                                if(templ == 256) {
                                                        err();
                                                        break;
                                                }
                                                baud = ((temph)<<8) + templ;
                                                switch(baud) {
                                                    case ('1'<<8) + '2': // 1200
                                                        baud = 6663;
                                                        scale = -2;
                                                        break;
                                                    case ('4'<<8) + '8': // 4800
                                                        baud = 3325;
                                                        scale = -3;
                                                        break;
                                                    case ('9'<<8) + '6': // 9600
                                                        baud = 829;
                                                        scale = -2;
                                                        break;
                                                    case ('1'<<8) + '9': // 19200
                                                        baud = 825;
                                                        scale = -3;
                                                        break;
                                                    case ('3'<<8) + '8': // 38400
                                                        baud = 204;
                                                        scale = -2;
                                                        break;
                                                    case ('5'<<8) + '7': // 57600
                                                        baud = 135;
                                                        scale = -2;
                                                        break;
                                                    case ('1'<<8) + '1': // 115200
                                                        baud = 131;
                                                        scale = -3;
                                                        break;
                                                    default:
                                                        err();
                                                        goto BAUD_DONE;
                                                }
                                                set_aux_baud_rate(baud, scale);

                                                BAUD_DONE:
                                                break;

                                        // Configures the baud rate of the auxiliary UART
                                        case 'C': // baud(2) scale(1)
                                          // e.g., 9600 = C\x03\x3D\xFE
                                                if (! get_arguments(3))
                                                    break;

                                                bt_putchar(arguments[2]); // duplicate buggy behavior from official firmware; delete if unwanted

                                                set_aux_baud_rate( GET_16BIT_ARGUMENT(0), arguments[2]);
                                                break;
                                        // BT-serial high speed bridge mode
                                        case 'Z':
                                                serial_bridge();
                                                break;
                                        case 'w': // channel(1) type(1) duty(1) amplitude(1) frequency(3)
                                                  // w0s\x20\xFF\x02\x00\x00
                                                if (!get_arguments(7))
                                                     break;
                                                if (arguments[0] < '0' || arguments[0] > '1' || arguments[2] > WAVEFORM_SIZE) {
                                                     err();
                                                     break;
                                                }

                                                play_wave_dac(arguments[0]-'0', (char)arguments[1], arguments[2], arguments[3], get_24bit_argument(4));

                                                break;
                                        case 'W': // channel(1) frequency(3) length(1) data(length)
                                                if (!get_arguments(5))
                                                    break;
                                                if (arguments[0] < '0' || arguments[0] > '1' || arguments[4] > WAVEFORM_SIZE || arguments[4] == 0 ) {
                                                        err();
                                                        break;
                                                }
                                                channel = arguments[0] - '0';
                                                if (bt_to_buffer(waveform[channel], arguments[4])) {
                                                     disable_waveform(channel);
                                                     play_arb_wave(channel, waveform[channel], arguments[4], get_24bit_argument(1));
                                                }
                                                break;

                                        case '@':
                                                channel = bt_getchar_timeout_echo();
                                                if (channel == '0')
                                                    disable_waveform0();
                                                else if (channel == '1')
                                                    disable_waveform1();
                                                else
                                                    err();
                                                break;
                                        case 'r':
                                                i = USART_RXBufferData_AvailableCount(&AUX_data);
                                                bt_putchar((uint8_t)(i+1));
                                                while(i-- > 0) {
                                                        bt_putchar(USART_RXBuffer_GetByte(&AUX_data));
                                                }
                                                break;
                                        // Transmit a stream of characters from bluetooth to auxiliary serial
                                        case 't':
                                                temph= bt_getchar_timeout_echo();
                                                if (temph == 256) {
                                                    err();
                                                    break;
                                                }

                                                for(; temph>0; temph--) {
                                                        templ= bt_getchar_timeout_echo();

                                                        if(templ == 256) {
                                                                err();
                                                                break;
                                                        }
                                                        else {
                                                                aux_putchar( templ);
                                                        }
                                                }
                                                break;
                                        default:
                                                break;
                                }
                        }
static uint8_t get_gamecube_data(uint8_t rumble) {
    uint8_t timeout = 255;
    uint8_t curpos;
    
    for(curpos=0; curpos<8; curpos++)
        buffer8[curpos] = 0;
    
    curpos = 0;
    uint8_t curbitmap = 0x80;

    port->DIRSET = bitmap;
    
    if (! (initializedGCOnPin & (1<<gcPin)) ) {
        gamecube_zeroes(8);
        gamecube_one();
        _delay_us(400);
        initializedGCOnPin |= 1<<gcPin;
    }
    
    gamecube_zeroes(1);
    gamecube_one();
    gamecube_zeroes(12);
    gamecube_one();
    gamecube_one();

    gamecube_zeroes(7);
//    gamecube_one();
    if (rumble)
        gamecube_one();
    else
        gamecube_zeroes(1);
    gamecube_one(); // stop bit
    
    port->DIRCLR = bitmap;

    while(--timeout) {
        // wait for low
        if(!(port->IN & bitmap)) {            
            _delay_us(14./16.*2);
            if(port->IN & bitmap)
                buffer8[curpos] |= curbitmap;
            curbitmap >>= 1;
            if (curbitmap == 0) {
                curbitmap = 0x80;
                curpos++;
                // there is a stop bit coming while the data is being
                // written to Bluetooth: we just ignore it
                if (curpos >= 8) {
                    bt_putchar('G');
                    for(curpos=0;curpos<8;curpos++)
                        bt_putchar(buffer8[curpos]);
                    return 1;
                }
            }
            timeout = 96;
            // wait for high
            while(--timeout && !(port->IN & bitmap));
            if (timeout==0) {
                break;
            }
            timeout = 255;
        }
        asm("nop");
        asm("nop");
    }
static void put_hex_nibble(uint8_t nibble) {
    if (nibble<10)
        bt_putchar(nibble+'0');
    else
        bt_putchar(nibble+('A'-10));
}