2018-10-22: This is a temporary repository to fix issue mbed OS issue 8344. I'm reverting to an earlier mbed revision that isn't messed up. Expect mbed to fix the linker issue in the next release and I'll remove this repository. I havne't tested the code at this revision - sorry!

Dependencies:   BLE_API mbed mbedtls nRF51822

main.cpp

Committer:
electronichamsters
Date:
2017-07-10
Revision:
8:46c5e0bfab05
Parent:
7:0a8bbb6dea16
Child:
9:1ea51b2048a8

File content as of revision 8:46c5e0bfab05:

/*

 Eric Tsai
 todo:  hash mac to create unqiue identifier for "my" sensors.


 */

//required to call the ecb functions
extern "C"
{
   #include "nrf_ecb.h"
}
 
#include "mbed.h"
#include "toolchain.h"
#include "ble/BLE.h"
#include "TMP_nrf51/TMP_nrf51.h"

//comment out when done with debug uart, else eats batteries
#define MyDebugEnb 1

//Pin "P0.4" on nRF51822 = mbed "p4".
//InterruptIn is pulled-up.  GND the pin to activate.

// waveshare board ******
//InterruptIn button1(p10);
//InterruptIn button2(p11);

// purple board ******
InterruptIn button1(p23);
InterruptIn button2(p24);


//Serial device(p9, p11);  // tx, rx, purple board and Rigado
#if MyDebugEnb
// if you see ~1mA consumption during sleep, that's because uart is enabled.
Serial device(p9, p11);  //nRF51822 uart :  TX=p9.  RX=p11
#endif

//InterruptIn button(p4);     //Pin P0.4 on  = mbed "p3"; 


static Ticker tic_adv;   //stop adv
static Ticker tic_debounce; //debounce I/O
static Ticker tic_periodic; //regular updates
//static TMP_nrf51  tempSensor;
static bool flag_update_io = false;
static bool flag_periodic_call = false;
static bool flag_detach_adv_tic = false;
static bool flag_set_debounce_tic = false;  //not used

//static DigitalOut alivenessLED(LED1, 1);
float mySensor = 2.0f;


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

/* You have up to 26 bytes of advertising data to use. */
/*
Advertisement

DECvolt:3.11111111,mag:1
AdvData[0-2] = Look for DEC on observer
AdvData[3] = beginning of data
*/
//full with nullls
static uint8_t AdvData[] = {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0};   /* Example of hex data */
char buffer[10]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //battery analog reading
uint8_t magnet_near=0;



static uint8_t key[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4};
//26 bytes adv data
static uint8_t encrypted[26] = {0x0,0x0,0x0,0x1,0x1,0x1,0x2,0x2,0x2,0x3,0x3,0x3,0x4,0x4,0x4,0x5,0x5,0x5,0x6,0x6,0x6,0x7,0x7,0x7,0x8,0x8};   /* Example of hex data */
//static uint8_t key_buf[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4};
static uint8_t key_buf[16] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x1, 0x2};
static uint8_t src_buf[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4};
static uint8_t des_buf[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4};

//const static uint8_t AdvData[] = {"ChangeThisData"};         /* Example of character data */



/* **** NOT USED **** */
//16byte UUID loading happens here
//Look at <GapAdvertisingData.h> for rest of definition
struct ApplicationData_t {
    //Byte 0:  AppID High Byte
    //Byte 1:  AppID Low Byte
    //Byte 2:  sensor High Word
    //Byte 3:
    //Byte 4:
    //Byte 5:  sensor Low Byte
    
    
    //app ID is 16 bit, (0xFEFE)
    uint16_t    applicationSpecificId; /* An ID used to identify temperature value in the manufacture specific AD data field */
    
    //float = 32-bit.  
    //tsai: change this to uint32_t!!!
    TMP_nrf51::TempSensorValue_t tmpSensorValue;        /* this is a float (32-bit), user data */
} PACKED;



void debounce_Callback(void)
{
    tic_debounce.detach();
    flag_set_debounce_tic = false;      //not used
    flag_update_io = true;  //start advertising
    /* Note that the buttonPressedCallback() executes in interrupt context, so it is safer to access
     * BLE device API from the main thread. */
    //buttonState = PRESSED;
}

//----- button rising ---
void buttonPressedCallback(void)
{


    //flag_update_io = true;
    
    tic_debounce.attach(debounce_Callback, 1); //ok to attach multiple times, first one wins
    

    //buttonState = PRESSED;
    
    /*
    if (flag_set_debounce_tic == false)
    {
        flag_set_debounce_tic = true;
        
    }
    */
}

//----- button falling ---
void buttonReleasedCallback(void)
{
    

    //flag_update_io = true;
    
    tic_debounce.attach(debounce_Callback, 1);
    
    
  
}


void stop_adv_Callback(void)
{
    //stops advertising after X seconds
    /* Note that the Callback() executes in interrupt context, so it is safer to do
     * heavy-weight sensor polling from the main thread (where we should be able to block safely, if needed). */
    
    //tic_adv.detach();
    
    flag_detach_adv_tic = true;
    //ble.gap().stopAdvertising();
    

}





void periodic_Callback(void)
{
    flag_update_io = true;
    flag_periodic_call = true;
}

void setupApplicationData(ApplicationData_t &appData)
{
    // two byte ID:  0xFEFE
    static const uint16_t APP_SPECIFIC_ID_TEST = 0xFEFE;        //2 byte application ID

    appData.applicationSpecificId = APP_SPECIFIC_ID_TEST;
    //appData.tmpSensorValue        = tempSensor.get();
    appData.tmpSensorValue        = mySensor;
}



/**
 * This function is called when the ble initialization process has failled
 */
void onBleInitError(BLE &ble, ble_error_t 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);

    /* Setup advertising payload */
    //set modes "no EDR", "discoverable" for beacon type advertisements
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    
    //set device profile in ADV/GATT
    //ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER);
    
    //set advertising data (ADV data)
    //ApplicationData_t appData;
    //setupApplicationData(appData);
    //in /BLE_API/ble/GapAdvertisingData.h:  sets payload (uuid)
    //ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, (uint8_t *)&appData, sizeof(ApplicationData_t));

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

    /* Setup advertising parameters:  not connectable */
    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
    ble.gap().setAdvertisingInterval(300);  //one advertisment every 300ms.  Self tickers, so you don't have to worry.



    //don't start advertising on init.  Only advertise on pin interrupt.
    //ble.gap().startAdvertising();
}


//https://developer.mbed.org/users/MarceloSalazar/notebook/measuring-battery-voltage-with-nordic-nrf51x/
void my_analogin_init(void)
{
    NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Enabled;
    NRF_ADC->CONFIG = (ADC_CONFIG_RES_10bit << ADC_CONFIG_RES_Pos) |
                      (ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) |
                      (ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) |
                      (ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos) |
                      (ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos);
}

uint16_t my_analogin_read_u16(void)
{
    NRF_ADC->CONFIG     &= ~ADC_CONFIG_PSEL_Msk;
    NRF_ADC->CONFIG     |= ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos;
    NRF_ADC->TASKS_START = 1;
    
    
    //while loop doesn't actually loop until reading comlete, use a wait.
    while (((NRF_ADC->BUSY & ADC_BUSY_BUSY_Msk) >> ADC_BUSY_BUSY_Pos) == ADC_BUSY_BUSY_Busy) {};
    wait_ms(2);

    //save off RESULT before disabling.
    //uint16_t myresult = (uint16_t)NRF_ADC->RESULT;
    
    //disable ADC to lower bat consumption
    //NRF_ADC->TASKS_STOP = 1;
    //NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Disabled;    //disable to shutdown ADC & lower bat consumption
    
    return (uint16_t)NRF_ADC->RESULT; // 10 bit
    //return myresult;
}

int main(void)
{

#if MyDebugEnb
    device.baud(9600);
    device.printf("started sensor node 36 ");
    device.printf("\r\n");
#endif

    
    
    Timer myTimer;  //timed advertising
    myTimer.start();


    AdvData[0] = 0x44;          //D
    AdvData[1] = 0x45;          //E
    AdvData[2] = 0x43;          //C
    AdvData[3] = 0x22;          //"
    AdvData[4] = 0x76;          //V volt
    AdvData[5] = 0x6f;          //o
    AdvData[6] = 0x22;          //"
    AdvData[7] = 0x3a;          //:
    AdvData[8] = 0x24;          //3     #
    AdvData[9] = 0x24;          //.     #
    AdvData[10] = 0x24;         //1     #
    AdvData[11] = 0x24;         //1     #
    AdvData[12] = 0x2c;         //,     
    AdvData[13] = 0x22;         //" mag
    AdvData[14] = 0x6d;         //a
    AdvData[15] = 0x22;         //"
    AdvData[16] = 0x3a;         //:
    AdvData[17] = 0x24;         //0 or 1, 30 or 31

    button1.fall(buttonPressedCallback);
    button1.rise(buttonReleasedCallback);
    button1.mode(PullNone);
    button1.fall(NULL);
    button1.rise(NULL); 

    BLE &ble = BLE::Instance();
    ble.init(bleInitComplete);
    
    //debug uart
    //device.baud(115200);
    float bat_reading;  //hold battery voltage reading (Vbg/Vcc)
    
    my_analogin_init();//routes band-gap to analog input

    /* SpinWait for initialization to complete. This is necessary because the
     * BLE object is used in the main loop below. */
    while (ble.hasInitialized() == false) { /* spin loop */ }
    
    //every X seconds, sends period update, up to 1800 (30 minutes)
    tic_periodic.attach(periodic_Callback, 10);

    while (true) {
        
        uint16_t mySeconds =(uint16_t)(myTimer.read_ms()/1000); //problem:  mySeconds is only 2 byte
        //reading the ADV value, only goes up to 0-255;
        //reading the uart:  current time in seconds: -1782, goes negative.
        //need to be able to count 1800 seconds since that's the length of timer.
        
#if MyDebugEnb
        device.printf("current time in seconds: %d \r\n", mySeconds);
#endif
        //**** set which pin should be interrupt, set pullups ***
        
        //set both pins to pull-up, so they're not floating when we read state
        button1.mode(PullUp);
        button2.mode(PullUp);
        
        //wait_ms(300);   //contact settle
        

        //AdvData[12] is automatically CR?  why?
        
        //0x33 0x2E 0x33 0x32 0x13
        //   3    .    3    2   CR
        
        //expect either button1 or button2 is grounded, b/c using SPDT reed switch
        //the "common" pin on the reed switch should be on GND
        uint8_t button1_state = button1.read();
        uint8_t button2_state = button2.read();
        
        
        //let's just update the pins on every wake.  Insurance against const drain.
        //if state == 0, pin is grounded.  Unset interrupt and float pin
        //set the other input
        if ( (button1_state == 0) && (button2_state == 1) )
        {
            magnet_near = 1;
            //AdvData[4] = 0x11;  //dont' set ADV data directly.  Using json now, need spacing
            //button1.disable_irq() //don't know if disables IRQ on port or pin
            button1.fall(NULL);     //disable interrupt
            button1.rise(NULL);     //disable interrupt
            button1.mode(PullNone); //float pin to save battery
            
            //button2.disable_irq() //don't know if disables IRQ on port or pin
            button2.fall(buttonReleasedCallback);     //disable interrupt
            button2.rise(buttonReleasedCallback);     //disable interrupt
            button2.mode(PullUp); //float pin to save battery
        }
        else if ( (button1_state == 1) && (button2_state == 0) )       //assume other pin is open circuit
        {
            magnet_near = 0;
            //AdvData[4] = 0x22;    //dont' set ADV data directly.  Using json now, need spacing
            //button1.disable_irq() //don't know if disables IRQ on port or pin
            button1.fall(buttonReleasedCallback);     //disable interrupt
            button1.rise(buttonReleasedCallback);     //disable interrupt
            button1.mode(PullUp); //float pin to save battery
            
            //button2.disable_irq() //don't know if disables IRQ on port or pin
            button2.fall(NULL);     //disable interrupt
            button2.rise(NULL);     //disable interrupt
            button2.mode(PullNone); //float pin to save battery
        }    
        else    //odd state, shouldn't happen, suck battery and pullup both pins
        {
            magnet_near = 2;
            //AdvData[4] = 0x33;
            //button1.disable_irq() //don't know if disables IRQ on port or pin
            button1.fall(buttonReleasedCallback);     //disable interrupt
            button1.rise(buttonReleasedCallback);     //disable interrupt
            button1.mode(PullUp); //float pin to save battery
            
            //button2.disable_irq() //don't know if disables IRQ on port or pin
            button2.fall(buttonReleasedCallback);     //disable interrupt
            button2.rise(buttonReleasedCallback);     //disable interrupt
            button2.mode(PullUp); //float pin to save battery
        }         
        
        
        if (flag_update_io) {
            /* Do blocking calls or whatever hardware-specific action is
             * necessary to poll the sensor. */

            //analog reading consumes 940uA if not disabled
            bat_reading = (float)my_analogin_read_u16();    
            bat_reading = (bat_reading * 3.6) / 1024.0;
#if MyDebugEnb
            device.printf("bat reading: %f \r\n", bat_reading);
#endif

            //memset(&buffer[0], 0, sizeof(buffer));      //clear out buffer
            //sprintf (buffer, "%f.2", bat_reading);    //don't know what i'm doing
            //sprintf (buffer, "%.2f", bat_reading);
            //AdvData[8] = buffer[0]; //"3"=0x33
            //AdvData[9] = buffer[1]; //"."=0x2E
            //AdvData[10] = buffer[2];//"3"=0x33
            //AdvData[11] = buffer[3];//"2"=0x32
            
            //disable ADC
            NRF_ADC->TASKS_STOP = 1;
            NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Disabled;    //disable to shutdown ADC & lower bat consumption

            //***********************************
            //form JSON string in ADV_DATA
            //1)  volts starts at AdvData[8]
            

            memset(&AdvData[0], 0, sizeof(AdvData));
            uint8_t JSON_loc=0; //DEC"vo": <---

            AdvData[JSON_loc] = 0x44;          //D
            JSON_loc++;
            AdvData[JSON_loc] = 0x45;          //E
            JSON_loc++;
            AdvData[JSON_loc] = 0x43;          //C
            JSON_loc++;
            AdvData[JSON_loc] = mySeconds & 0xFF;           //reserved for timer
            JSON_loc++;
            AdvData[JSON_loc] = (mySeconds >> 8) & 0xFF;;           //reserved for timer
            JSON_loc++;
            AdvData[JSON_loc] = 0x22;          //"
            JSON_loc++;
            AdvData[JSON_loc] = 0x76;          //V volt
            JSON_loc++;
            AdvData[JSON_loc] = 0x6f;          //o
            JSON_loc++;
            AdvData[JSON_loc] = 0x6C;          //l
            JSON_loc++;
            AdvData[JSON_loc] = 0x74;          //t
            JSON_loc++;
            AdvData[JSON_loc] = 0x22;          //"
            JSON_loc++;
            AdvData[JSON_loc] = 0x3a;          //:
            JSON_loc++;
    
    
            
            //write battery voltage
            uint8_t total_chars;
            memset(&buffer[0], 0, sizeof(buffer));      //clear out buffer
            total_chars = sprintf (buffer, "%.2f", bat_reading);    //returns total number of characters

#if MyDebugEnb
            device.printf("char buff: %c%c%c%c \r\n", buffer[0], buffer[1], buffer[2], buffer[3]);
            device.printf("num chars: %d \r\n", total_chars);
#endif

            for (int i=0; i < total_chars; i++)
            {
                AdvData[JSON_loc] = buffer[i];
                JSON_loc++;
#if MyDebugEnb
                device.printf("JSON_loc: %d     buf[]:%c \r\n", JSON_loc, buffer[i]);
#endif
            } //JSON_loc left at location of next character
            
            //DEC"vo":3.11,"
            AdvData[JSON_loc] = 0x2c;       //,
            JSON_loc++;
            
            AdvData[JSON_loc] = 0x22;       //" start mag
            JSON_loc++;
            
            AdvData[JSON_loc] = 0x6d;       //m
            JSON_loc++;
            
            AdvData[JSON_loc] = 0x61;       //a
            JSON_loc++;
            
            AdvData[JSON_loc] = 0x67;       //g
            JSON_loc++;
            
            if (flag_periodic_call)
            {
                //AdvData[JSON_loc] = 0x2f;       // "/"
                //JSON_loc++;
                AdvData[JSON_loc] = 0x2f;       // "/"
                JSON_loc++;
                AdvData[JSON_loc] = 0x70;       // "p"
                JSON_loc++;
            }//end if period call
            
            AdvData[JSON_loc] = 0x22;       //"
            JSON_loc++;

            AdvData[JSON_loc] = 0x3a;       //:
            JSON_loc++;
            
            //prep magnet location (1 or 0) for char[]
            memset(&buffer[0], 0, sizeof(buffer));      //clear out buffer
            //magnet_near is an integer
            total_chars = sprintf (buffer, "%d", magnet_near);    //returns total number of characters
            for (int i=0; i < total_chars; i++)
            {
                AdvData[JSON_loc] = buffer[i];
                JSON_loc++;
            } //JSON_loc left at location of next character
            
            //MUST null terminate for JSON to read correctly, else get intermittent JSON parse errors at gateway
            //happens when string is shorter than last string, get trash left overs
            //not really needed after clearning AdvData at start.
            
            
            //AdvData[JSON_loc] = 0x0;    //null terminate here

            ApplicationData_t appData;
            setupApplicationData(appData);
            

            for (int i=0; i<16; i++)
            {
                src_buf[i] = AdvData[i+3];
            }

            //nrf_ecb_set_key(key_buf);
            nrf_ecb_init();
            nrf_ecb_set_key(key_buf);
            bool successful_ecb = nrf_ecb_crypt(des_buf, src_buf);
#if MyDebugEnb
            device.printf("success ecb = %d \r\n", successful_ecb);
            device.printf("src_buf: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x \r\n", src_buf[0], src_buf[1], src_buf[2], src_buf[3], src_buf[4], src_buf[5], src_buf[6], src_buf[7], src_buf[8], src_buf[9], src_buf[10], src_buf[11], src_buf[12], src_buf[13], src_buf[14], src_buf[15]);
            device.printf("des_buf: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x \r\n", des_buf[0], des_buf[1], des_buf[2], des_buf[3], des_buf[4], des_buf[5], des_buf[6], des_buf[7], des_buf[8], des_buf[9], des_buf[10], des_buf[11], des_buf[12], des_buf[13], des_buf[14], des_buf[15]);
#endif
            for (int i=0; i<16; i++)
            {
                AdvData[i+3] = des_buf[i];
            }
            
            //ble.gap().updateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, (uint8_t *) &appData, sizeof(ApplicationData_t));
            ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, AdvData, sizeof(AdvData));
            
            flag_update_io = false;
            flag_periodic_call = false;
            
            //GAP::AddressType_t *myType;
            //GAP::Address_t myAddress
            //ble_error_t getAddress(Gap::AddressType_t *typeP, Gap::Address_t address)
            //ble.gap().getAddress(myType, myAddress);
            //ble.gap().getAddress(Gap::AddressType_t *typeP, Gap::Address_t address);
            uint8_t mac[6] = {0x0,0x0,0x0,0x0,0x0,0x0};
            ble.getAddress(0,mac);  //NOTE:  last byte of MAC (as shown on phone app) is at mac[0], not mac[6];
#if MyDebugEnb
            device.printf("mac = ");
            for (int i=0; i<6; i++)
            {
                device.printf("%x:", mac[i]);
            }
            device.printf("\r\n");
#endif
            ble.gap().startAdvertising();
            tic_adv.attach(stop_adv_Callback, 2); /* trigger turn off advertisement after X seconds */

            /*
            Timer myTimer;  //timed advertising
            myTimer.start();
            uint32_t duration = myTimer.read_ms();
            
            //do this as a ticker instead of keeping processor on
            while (duration < 15000)            //advertise for 1000 ms
            {
                duration = myTimer.read_ms();   //read miliseconds
            }
            myTimer.stop();
            ble.gap().stopAdvertising();
            */
        
        }//end flag_update_io
        
        
        /*
        if (flag_set_debounce_tic == true)
        {
            tic_debounce.attach();
            //flag_set_debounce_tic = false;
            
        }
        */
        
        //if (trigger_Detach_ADV_Tick == false)
        //{  
        //}
        if (flag_detach_adv_tic == true)    //Stop Advertising
        {
            ble.gap().stopAdvertising();    //may be safer to execute BLE operations in main
            tic_adv.detach();
            flag_detach_adv_tic = false;
        }
        //device.printf("Input Voltage: %f\n\r",bat_reading);
        
        ble.waitForEvent(); //sleeps until interrupt
        

    }//end forever while
}//end main