football_project_wo_output

Dependencies:   mbed

Fork of football_project by MZJ

main.cpp

Committer:
andriym
Date:
2016-06-16
Revision:
95:3cc3ae2e5e7f
Parent:
94:831a7fc2d69a
Child:
97:2894fbc2d4f8

File content as of revision 95:3cc3ae2e5e7f:

#include <RFM69.h>
#include <list>
#include <DebounceIn.h>
#ifdef BLE_ENABLE
  #include "ble/BLE.h"
  #include "ble/services/DFUService.h"
#endif 

using namespace std;

Timer tmr;
//Output out;

#ifdef BLE_ENABLE
BLEDevice  ble;

// Optional: Device Name, add for human read-ability 
//const static char     DEVICE_NAME[] = "PassiveCoach";

// You have up to 26 bytes of advertising data to use.
//const static uint8_t AdvData[] = {0x01,0x02,0x03,0x04,0x05};   /* Example of hex data */
//const static uint8_t AdvData[] = {"Keep the Space!"};         /* Example of character data */
#endif // BLE_ENABLE

///////////////////////////////////  PINS   ///////////////////////////////////

DebounceIn buttonTeam(BUT_TEAM);
DebounceIn buttonSpace(BUT_SPACE);
DebounceIn buttonVolMore(BUT_VOL_MORE);
DebounceIn buttonVolLess(BUT_VOL_LESS);

DigitalOut ledTeamA(LED_TEAM_A);
DigitalOut ledTeamB(LED_TEAM_B);
DigitalOut ledSpace5(LED_SPACE5);
DigitalOut ledSpace10(LED_SPACE10);
DigitalOut ledSpace15(LED_SPACE15);
DigitalOut ledSpace20(LED_SPACE20);
DigitalOut ledBuzzer(LED_BUZZER_ON);

AnalogIn ain(ANALOG_IN);    // used for randomizing  
#ifdef NORDIC
DigitalOut buzzer(BUZZER);
#ifndef HARD_V2
DigitalOut buzzLow(BUZZ_LOW);
#endif
DigitalOut buzzMed(BUZZ_MED);
DigitalOut buzzHigh(BUZZ_HIGH);
#else
PwmOut speaker(BUZZER);     // passive buzzer
#endif

///////////////////////////////////  RADIO  ///////////////////////////////////
#ifdef ENABLE_PIN
static RFM69 radio(RFM_MOSI, RFM_MISO, RFM_SCK, RFM_SS, RFM_IRQ, RFM_ISM_EN);
#else
static RFM69 radio(RFM_MOSI, RFM_MISO, RFM_SCK, RFM_SS, RFM_IRQ);
#endif

static bool promiscuousMode = true; // set 'true' to sniff all packets on the same network

/////////////////////////////////// FUNCTIONS ///////////////////////////////////

void beep(float period, float time, uint8_t vol) { 
#ifdef ENABLE_SOUND
    if(!vol) return;
#ifdef NORDIC
    if(vol>=3) {
#ifndef HARD_V2
        buzzLow = LOW_ON; 
#endif 
        buzzMed = MED_OFF; buzzHigh = HIGH_OFF;
    } else if(vol>=2) {
#ifndef HARD_V2
        buzzLow = LOW_OFF; 
#endif 
        buzzMed = MED_ON; buzzHigh = HIGH_OFF;
    } else {
#ifndef HARD_V2
        buzzLow = LOW_OFF; 
#endif 
        buzzMed = MED_OFF; buzzHigh = HIGH_ON;
    }
    buzzer = 0;     // P-MOSFET
    wait(time);
    buzzer = 1;
#else
    speaker.period(period);
    if(vol>=3) 
        speaker = 0.5; //50% duty cycle - max volume
    else if(vol>=2) 
        speaker = 0.33;
    else
        speaker = 0.2;
    wait(time);
    speaker=0.0; // turn off audio
#endif
#endif
}

void binary_sound(int z) {
    // Beeps numbers according to their binary form
    // (used for debugging in display-less and serial-less environments)
#ifdef ENABLE_SOUND
#ifndef NORDIC
    speaker.period(0.004);
    while(z) {
        speaker = 0.5;
        if(z&1) wait(0.5);
        else wait(0.25);
        speaker = 0.0;
        wait(1.0);
        z >>= 1;
    }
    beep(NOTE_A4, 1.0, 2);
#endif
#endif
}

void generate_name(char rand_name[], uint8_t size) {
    // Generate random name on the 62 character alphabet
    memset(rand_name, 0x00, size);
    char alph[63] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    uint32_t seed = ((ain.read_u16()+3)/17)*(radio.readTemperature(-1)+37)*((ain.read_u16()+5)/13);
    srand(seed);
    for(int i=0;i<size-1;i++) {
        rand_name[i] = alph[abs(rand()) % 62];
    }
}

void spaceLEDs(int level) {
    if(level<=0) {
        ledSpace5 = 1; ledSpace10 = 0; ledSpace15 = 0; ledSpace20 = 0;
    } else if(level<=1) {
        ledSpace5 = 0; ledSpace10 = 1; ledSpace15 = 0; ledSpace20 = 0;
    } else if(level<=2) {
        ledSpace5 = 0; ledSpace10 = 0; ledSpace15 = 1; ledSpace20 = 0;
    } else {
        ledSpace5 = 0; ledSpace10 = 0; ledSpace15 = 0; ledSpace20 = 1;
    }
}

#ifdef BLE_ENABLE
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
    //pc.printf("Disconnected \r\n");
    //pc.printf("Restart advertising \r\n");
    ble.startAdvertising();
}

// Optional: Restart advertising when peer disconnects 
/*
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
    BLE::Instance().gap().startAdvertising();
}
*/
// This function is called when the ble initialization process has failed
void onBleInitError(BLE &ble, ble_error_t error)
{
    // Avoid compiler warnings 
    (void) ble;
    (void) error;
    
    // Initialization error handling should go here
}    

// Callback triggered when the ble initialization process has finished
/* 
void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
    BLE&        ble   = params->ble;
    ble_error_t error = params->error;

    if (error != BLE_ERROR_NONE) {
        // In case of error, forward the error handling to onBleInitError
        onBleInitError(ble, error);
        return;
    }

    // Ensure that it is the default instance of BLE
    if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
        return;
    }
    
    // Set device name characteristic data
    ble.gap().setDeviceName((const uint8_t *) DEVICE_NAME);

    // Optional: add callback for disconnection
    ble.gap().onDisconnection(disconnectionCallback);

    // Sacrifice 3B of 31B to Advertising Flags
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE );
    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);

    // Sacrifice 2B of 31B to AdvType overhead, rest goes to AdvData array you define
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, AdvData, sizeof(AdvData));

    // Optional: Add name to device
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));

    // Set advertising interval. Longer interval == longer battery life
    ble.gap().setAdvertisingInterval(100); // 100ms

    // Start advertising
    ble.gap().startAdvertising();
}
*/
#endif // BLE_ENABLE

/////////////////////////////////// CLASSES ///////////////////////////////////

struct Contact {
    int16_t rssi;
    uint32_t time_ms;
};

class Player {
  public:
    char name[NAME_LEN+1];
    int8_t team;
    Player(const char rand_name[]) {
        memset(name, 0x00, NAME_LEN+1);
        memcpy(name, rand_name, NAME_LEN);
    }
    void update(int8_t _team, int rssi, uint32_t time=NULL) {
        // Remember this contact
        team = _team;
        Contact c;
        c.rssi = rssi;
        c.time_ms = (time!=NULL) ? time : tmr.read_ms();
        contacts.push_front(c);
        
        // Cleanup
        while(contacts.size() > MAX_RECORDS) contacts.pop_back();
        while(!contacts.empty() && c.time_ms - contacts.back().time_ms > MAX_HISTORY_MS) contacts.pop_back();
    }
    int16_t get_distance(int &cnt) {
        // Find max RSSI
        uint32_t cur_time = tmr.read_ms();
        int16_t max_rssi=-128;
        cnt=0;
        for (list<Contact>::iterator it=contacts.begin(); it != contacts.end(); ++it) {
            if(cur_time - it->time_ms > SIGNALS_VALID_MS) break;
            if(it->rssi > max_rssi) max_rssi = it->rssi;
            cnt++;
        }
        return max_rssi;
    }
    inline bool operator==(const char rhs[]){ return memcmp(name, rhs, NAME_LEN)==0; }
    inline bool operator==(const Player& rhs){ return *this==rhs.name; }
    //inline bool operator!=(const char rhs[]){ return !(*this == rhs); }
  private:
    list<Contact> contacts;
};

class Players {
  public:
    list<Player> players;
    
    void update(const char name[], int8_t team, int16_t rssi, uint32_t time_ms=NULL) {
        list<Player>::iterator it;
        for (it=players.begin(); it != players.end(); ++it) {
            if(*it==name) break;
        }
        if(it!=players.end()) {
            it->update(team, rssi, time_ms);
        } else {
            Player p(name);
            p.update(team, rssi, time_ms);
            players.push_front(p);
        }
    }
    
    void showAll(int8_t team, uint8_t level, uint8_t volume) {
        // Output the current state to the user:
        //   - show the current information table and/or
        //   - beep if the user is too close to another one
        int i=0, nContacts, signal, maxTeamSignal = -128;
        list<Player>::iterator it;
        //out.clear();
        for (it=players.begin(); it != players.end(); ++it) {
            signal = it->get_distance(nContacts);
            if(signal>-128) {
                /*
                out.printf("%d ", ++i);
                out.printf((team==it->team) ? "+" : "-");       // teammate or opponent?
                out.printf("%s ", it->name);
                out.printf("%d/%d", -signal, nContacts);
                out.printf((-signal<SPACE[level]) ? "!" : " ");
                out.printf("\r\n");
                */
            }
            if(team==it->team && signal>maxTeamSignal) {
                maxTeamSignal = signal;
            }
        } 
        //if(!i) {
        //    out.printf("Nobody around\r\n");
        //    beep(NOTE_A5,0.5,volume);
        //}
        if(-maxTeamSignal<SPACE[level]) {
            beep(NOTE_A4, 0.33, volume);
        }
    }
};

/////////////////////////////////// MAIN CODE ///////////////////////////////////

int main()
{    
#ifdef BLE_ENABLE
    // BLE initialization
    ble.init();
    ble.onDisconnection(disconnectionCallback);
#endif // BLE_ENABLE

    char tx_buff[100] = {0};
    char rx_buff[100] = {0};
    char rand_name[NAME_LEN+1] = {0};
    char other_name[NAME_LEN+1] = {0};
    int8_t myTeam = 1, otherTeam;
    uint8_t level = 1, volume = 1;      // SPACE10, VOL_LOW
    int bTeamNew, bTeamOld, bSpaceOld, bSpaceNew, bVMNew, bVLNew, bVMOld, bVLOld;
    
    // Blink all the LEDs at startup
    spaceLEDs(0); wait(0.2); spaceLEDs(1); wait(0.2); spaceLEDs(2); wait(0.2);  spaceLEDs(3); wait(0.2); ledSpace20 = 0;
#ifdef TEST_LED_SOUND
    // Test LEDs and sound simultaneously to save time
    buzzMed = MED_OFF; buzzHigh = HIGH_OFF; 
    ledBuzzer = 1; buzzer=0; wait(0.2); buzzer=1; ledBuzzer = 0;
    buzzMed = MED_ON;
    ledTeamA = 1; buzzer=0; wait(0.2); buzzer=1; ledTeamA = 0;  
    buzzMed = MED_OFF; buzzHigh = HIGH_ON; 
    ledTeamB = 1; buzzer=0; wait(0.2); buzzer=1; ledTeamB = 0; 
    buzzHigh = HIGH_OFF;
#else
    ledBuzzer = 1; wait(0.2); ledBuzzer = 0;
    ledTeamA = 1; wait(0.2); ledTeamA = 0;  
    ledTeamB = 1; wait(0.2); ledTeamB = 0; 
    
    // Beep all the levels on startup
    beep(NOTE_A5, 0.2, 1);
    beep(NOTE_A5, 0.2, 2);
    beep(NOTE_A5, 0.2, 3);
#endif
    
    // ...and go to a informative LEDs
    spaceLEDs(level);
    ledBuzzer = volume ? 1 : 0;
    if(myTeam & 1) ledTeamA = 1;
    else ledTeamB = 1;

    // Buttons initialization
    buttonSpace.mode(PullDown);
    buttonVolMore.mode(PullDown);
    buttonVolLess.mode(PullDown);
#ifdef NORDIC
    buttonTeam.mode(PullDown);
    bTeamOld = 0;
#else
    buttonTeam.mode(PullUp);
    bTeamOld = 1;
#endif
    bSpaceOld = 0;
    bVMOld = 0;
    bVLOld = 0;
    
#ifdef BLE_ENABLE
   // setup advertising 
    ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
    ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,
                                    (const uint8_t *)"KeepTheSpace", sizeof("KeepTheSpace") - 1);
    //ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
    //                                (const uint8_t *)uart_base_uuid_rev, sizeof(uart_base_uuid));
    
    // 100ms; in multiples of 0.625ms. 
    ble.setAdvertisingInterval(160);

    //ble.addService(uartService);
    
    DFUService dfu(ble);
    
    ble.startAdvertising(); 
    //pc.printf("Advertising Start \r\n");    

    /*
    // GAP version
    BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
    ble.init(bleInitComplete);  // Initialize BLE baselayer
    DFUService dfu( ble );
    */
#endif // BLE_ENABLE
    
    // Pick node number
    char this_node = int(ain.read()*255+17)*int(ain.read()*255+11); // random node value
    //out.printf("Node: %d\r\n", this_node);

    // Initialize the radio    
    radio.initialize(FREQUENCY, this_node, NETWORKID);
#ifdef HIGH_POWER
    radio.setHighPower(true);
#endif
    radio.encrypt(0);
    radio.promiscuous(promiscuousMode);
    if(FREQUENCY == RF69_868MHZ) 
        radio.setFrequency(868000000);
    else if(FREQUENCY == RF69_915MHZ) 
        radio.setFrequency(915000000);

    // Pick node name
    generate_name(rand_name, sizeof(rand_name));
    /*
    out.printf("Name: %s\r\n", rand_name);
    out.sleep(2.0);
    */
        
    tmr.start();
    
    Players players;
    
    uint32_t last_send = tmr.read_ms();
    uint32_t last_shown = tmr.read_ms();
    //uint32_t min_wait = SEND_RATE_MS - 0.5*SEND_RATE_MS*SEND_DESYNC;
    //uint32_t send_wait = min_wait;

    while (true)
    {
        // Read team button
        bTeamNew = buttonTeam;
        if(bTeamNew==PRESSED && bTeamOld==RELEASED) {
            myTeam = (myTeam==1) ? 2 : 1;
            ledTeamA = myTeam & 1;
            ledTeamB = ~myTeam & 1;
            /*
            out.clear();
            out.printf("New team: %d\r\n", myTeam);
            out.sleep(2);
            */
        }
        bTeamOld = bTeamNew;
        
        // Read space button
        bSpaceNew = buttonSpace;
        if(bSpaceNew && !bSpaceOld) {
            level = (level+1) & 0b11;   // four states
            spaceLEDs(level);
            /*
            out.clear();
            out.printf("New level: %d\r\n", level);
            out.sleep(2);
            */
        }
        bSpaceOld = bSpaceNew;

        // Read volume buttons
        bVMNew = buttonVolMore.read();
        bVLNew = buttonVolLess.read();
        if(bVMNew && !bVMOld) {
            volume++;
            if(volume>3) volume=3;
            ledBuzzer = 1;
            /*
            out.clear();
            out.printf("New volume: %d\r\n", volume);
            out.sleep(2);
            */
        }
        if(bVLNew && !bVLOld) {
            if(volume>0) volume--;
            if(!volume) ledBuzzer = 0;
            /*
            out.clear();
            out.printf("New volume: %d\r\n", volume);
            out.sleep(2);
            */
        }
        bVMOld = bVMNew;
        bVLOld = bVLNew;

        // Output
        unsigned long current_time = tmr.read_ms();
        
        if (current_time - last_shown > CYCLE_MS)
        {
            players.showAll(myTeam, level, volume);
            last_shown = current_time;
        }
        
        // Radios 
        if (current_time - last_send > SEND_RATE_MS)
        {
            // Send message
            snprintf(tx_buff, sizeof(tx_buff), "N=%s,%d", rand_name, myTeam);
            radio.send(EVERY_NODE, tx_buff, strlen(tx_buff));
            last_send = current_time;
            //send_wait = min_wait + (rand() % SEND_RATE_MS)*SEND_DESYNC;
      
            //// Some debugging info I used to display
            //out.clear();
            //out.printf("Sent: %s\r\n", tx_buff);
            //uint8_t tempC = radio.readTemperature(-1); // -1 = user cal factor, adjust for correct ambient
            //uint8_t gain = (radio.readReg(REG_LNA) & 0b111000)>>3;   // LNA Current Gain
            //uint32_t freq = radio.getFrequency();
            //out.printf("T: %d, G: %d\r\n", tempC, gain);
            //if(freq!=868000000) out.printf("Freq: %d\r\n", freq);
            
            //beep(NOTE_A3, 0.05, volume);
        }

        if (radio.receiveDone())
        {
            memset(rx_buff, 0x00, sizeof(rx_buff));
            memcpy(rx_buff, (char*)radio.DATA, radio.DATALEN > sizeof(rx_buff)-1 ? sizeof(rx_buff)-1 : radio.DATALEN);
            
            if(rx_buff[0]=='N' && rx_buff[1]=='=') {
                memcpy(other_name, rx_buff+2, NAME_LEN);
                other_name[5] = 0x00;
                if(sizeof(rx_buff)>8 && rx_buff[7]==',') {
                    otherTeam = rx_buff[8]-'0';
                } else otherTeam = 1;
                players.update(other_name, otherTeam, radio.RSSI, tmr.read_ms());

                //uint8_t gain = (radio.readReg(REG_LNA) & 0b111000)>>3;   // LNA Current Gain
                //out.clear();
                //out.printf("Other: %s\r\n", other_name);
                //out.printf("RSSI: %d, G: %d\r\n", radio.RSSI, gain);
                
                //beep(NOTE_A5, 0.5, volume);
            } else {    // received unknown signal 
                uint8_t gain = (radio.readReg(REG_LNA) & 0b111000)>>3;   // LNA Current Gain
                /*
                out.clear();
                out.printf("Got: %s\r\n", rx_buff);
                out.printf("RSSI: %d, G: %d\r\n", radio.RSSI, gain);
                out.sleep(2.0);
                */
            }
        }
        
#ifdef BLE_ENABLE
//        ble.waitForEvent();
#endif // BLE_ENABLE
    }
}