Geo beacon for VF.

Dependencies:   MMA8452 aconno_bsp adc52832_common

main.cpp

Committer:
dbartolovic
Date:
2018-09-25
Branch:
Lizzy_hardware
Revision:
42:bffc939efdf3
Parent:
41:7d8ce2d8d167

File content as of revision 42:bffc939efdf3:

/*
 *
 *  Made by Jurica Resetar @ aconno
 *  aconno.de
 *  All rights reserved.
 *
 */

#include "mbed.h"
#include "ble/BLE.h"
#include "GapAdvertisingData.h"
#include "acd52832_bsp.h"
#include "mma8452.h"
#include "AckService.h"
#include "nrf52_uart.h"
#include "nrf52_digital.h"
#include "acn_nrf52_pwm.h"

#define DEBUG               (0)
#define DEBUG_ACC           (0)
#define DEBUG_PRINT_UART    (0)
#define DEBUG_MAC           (0)
#define DEBUG_CONNECTION    (0)
#define DEBUG_WAKEUP_BUZZER (0)

#define ACN_FREIGHT         (1)

#define USE_ACC             (0)

#define SLEEP_TIME_S              (4.00)           /* Sleep time (in s)          */
#define ADV_TIMER_TIME_S          (1.00)           /* Advertising time (in s)    */
#define FREE_TIME_S               (0.1)            /* Time between end of a scanning and sleep mode */
#define AWAKE_TIME_S              (ADV_TIMER_TIME_S+FREE_TIME_S)      /* Was 0.15 */
#define SHORT_SLEEP_TIME_S        (0.5)            /* Shorter sleep time (s) */
#define SHORT_SLEEP_TIME_PERIOD_S (10)             /* Time after a last scanned advertisment. In the period, sleep time is SHORT_SLEEP_TIME */
#define BUZZER_FREQUENCY_Hz       (4000)

#define BUZZ_TIME_S         (1)     /* Buzz time in s */
#define ADV_INTERVAL        (100)   /* Advertising interval (in ms) */
#define UUID_SIZE_B         (16)

/* Static constants for the accelerometer */
#define WHO_AM_I            0x0D           /* Type 'read' : This should return the device id of 0x2A */
#define OUT_Z_MSB           0x05           /* Type 'read' : z axis - 8 most significatn bit of a 12 bit sample */
#define ACC_POWER           (p11)
#define I2C_DATA            (p20)
#define I2C_CLK             (p17)
#define INT2_PIN            (p15)
#define BUZZER              (p18)

#if ACN_FREIGHT
#define LED_BLINK_HALF_T    (0.5)
#endif
#define BUZZER_ON_TIME_S    (0.250)
#define BUZZER_OFF_TIME_S   (0.050)

/* LEDs */
#define RED_LED             (p31)
#define GREEN_LED           (p2)
#define BLUE_LED            (p3)

/* I2C power */
#define I2C_POWER           (p5)

/* UART pins */
#define UART_TX             (p25)
#define UART_RX             (p26)

#if DEBUG_PRINT_UART
    #include "nrf52_uart.h"
    NRF52_UART uart(UART_TX, UART_RX, Baud9600);
    char buffer[255];
    #define SEND(...) {uint8_t len = sprintf(buffer, __VA_ARGS__); uart.send(buffer, len);}
#else
    #define SEND(...)
#endif


#if ACN_FREIGHT

/* Aconno ID universal to all projects */
#define ACONNO_ID               (0x69)
/* Internal unique product identification. When doing new projects, just
 * increment old project ID by one. */
#define PRODUCT_ID              (0x03)
/* Version of the firmware. Start from zero and go upwards. */
#define VERSION                 (0x02)

/* Manufacturer that will sell the product. 0x0059 for general use case. */
#define MANUFACTURER_ID         (0x0059)

struct __attribute__((packed, aligned(1))) AdvertisingFormat
{
    uint16_t manufacturerID;
    uint8_t aconnoID;
    uint8_t productID;
    uint8_t version;
};

#endif


uint8_t sleepFlag = true;
uint8_t UUID[UUID_SIZE_B] = {0xE1, 0x61, 0x35, 0xBA, 0xC0, 0xEC, 0x47, 0x2A, 0x98, 0x00, 0xAF, 0x18, 0x43, 0xFF, 0x05, 0x00};
uint8_t startBuzz[2] = {0xBA, 0xBE};
uint8_t stopBuzz[2] = {0xDE, 0xAD};
uint8_t myMacAddress[6] = {};   
uint8_t buzzer_flag = 0;

enum RadioState{
    OFF,
    ADVERTISING,
    SCANNING,
};

enum RadioState radioState = OFF;

void GoToSleep();
void StartAdvertising();
void startScanning();
void WakeMeUp();

Ticker WakeSleepT;
Ticker sleepChanger;
Ticker toggleBuzzer;
#if ACN_FREIGHT
    Ticker toggleLed;
#endif
NRF52_PWM buzzer(NRF_PWM2);

#if USE_ACC
    DigitalOut accPower(ACC_POWER);
    DigitalOut i2cPower(I2C_POWER);
    InterruptIn accPulse(INT2_PIN);
    Acc_MMA8452 acc(I2C_DATA, I2C_CLK, MMA8452_ADDRESS);

#endif
BLE &ble = BLE::Instance();
ACKService<4> *ackServicePtr;

#if DEBUG || DEBUG_MAC || DEBUG_CONNECTION
    NRF52_DigitalOut advLED(RED_LED);         // Red
    NRF52_DigitalOut scanLED(BLUE_LED);        // Blue
    NRF52_DigitalOut connectedLED(GREEN_LED);   // Green
#endif

#if DEBUG_ACC
    NRF52_DigitalOut int_led(RED_LED);
    NRF52_DigitalOut act_led(RED_LED);
#endif

#if ACN_FREIGHT
    NRF52_DigitalOut signal_led(RED_LED);
#endif

#if ACN_FREIGHT
void ledToggle(){
    signal_led.toggle();
}
#endif

void buzzerToggle(){
    static uint8_t initState = 1;
    if(initState){
        // initial state is off
        buzzer.enable(BUZZER_FREQUENCY_Hz);
        buzzer.enableChannel(0, BUZZER);
        buzzer.setDuty(0,0.5f);    
        initState = 0;
        toggleBuzzer.detach();
        toggleBuzzer.attach(buzzerToggle, BUZZER_OFF_TIME_S);
    }
    else{
        buzzer.enable(0);
        buzzer.setDuty(0, 0);
        buzzer.disable();    
        initState = 1;
        toggleBuzzer.detach();
        toggleBuzzer.attach(buzzerToggle, BUZZER_ON_TIME_S);
    }
}

void buzzerStart(){
    buzzer.enable(BUZZER_FREQUENCY_Hz);
    buzzer.enableChannel(0, BUZZER);
    buzzer.setDuty(0,0.5f);
}
void buzzerStop(){
    buzzer.enable(0);
    buzzer.setDuty(0, 0);
    buzzer.disable();
}

void onConnectionCallback(const Gap::ConnectionCallbackParams_t *params){   
    #if DEBUG_CONNECTION
        scanLED = !scanLED;       // Blue
        wait_ms(100);
        scanLED = !scanLED;       // Blue
        wait_ms(100);
        scanLED = !scanLED;       // Blue
        wait_ms(100);
        scanLED = !scanLED;       // Blue
        wait_ms(100);
        scanLED = !scanLED;       // Blue
        wait_ms(100);
        scanLED = !scanLED;       // Blue
        wait_ms(100);
        scanLED = 1;              // Blue
    #endif
    WakeSleepT.detach();
    sleepFlag = false;
}


/* Restart Advertising on disconnection*/
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params){
    buzzerStop();
    #if DEBUG_CONNECTION
        advLED = !advLED;       // RED
        wait_ms(100);
        advLED = !advLED;
        wait_ms(100);
        advLED = !advLED;
        wait_ms(100);
        advLED = !advLED;
        wait_ms(100);
        advLED = 1;
        wait_ms(100);
        advLED = 1;
    #endif
    WakeSleepT.attach(WakeMeUp, FREE_TIME_S);
    sleepFlag = true;
    
}

void onDataWrittenCallback(const GattWriteCallbackParams *params) {
    if(params->handle == ackServicePtr->getACKCharacteristicHandle()){
        // Something is written into AckCharacteristic
        if(params->data[0] == startBuzz[0]){
            if(params->data[1] == startBuzz[1]){
                #if DEBUG_CONNECTION
                    connectedLED = !connectedLED;       // BLUE
                    wait_ms(100);
                    connectedLED = !connectedLED;
                    wait_ms(100);
                    connectedLED = !connectedLED;
                    wait_ms(100);
                    connectedLED = !connectedLED;
                    wait_ms(100);
                    connectedLED = !connectedLED;
                    wait_ms(100);
                    connectedLED = 1;
                    wait_ms(100);
                #endif    
                //buzzerStart();
                toggleBuzzer.attach(buzzerToggle, BUZZER_ON_TIME_S);
                #if ACN_FREIGHT
                    toggleLed.attach(ledToggle, LED_BLINK_HALF_T);
                #endif
                return;
            }
        }
        else if(params->data[0] == stopBuzz[0]){
            if(params->data[1] == stopBuzz[1]){
                toggleBuzzer.detach();
                buzzerStop();
                #if ACN_FREIGHT
                    toggleLed.detach();
                    signal_led = 1;
                #endif
                WakeSleepT.detach();
                WakeSleepT.attach(WakeMeUp, FREE_TIME_S);
                ble.disconnect(Gap::LOCAL_HOST_TERMINATED_CONNECTION);
            }
        }
    }
    else{
        // Execute this for wrong data written into characteristic
        return;
    }
}

/**
 * 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;
    }
    
    uint8_t init_values[4] = {0,0,0,0};
    /* Get my MAC address */
    BLEProtocol::AddressType_t temp_address_type;
    ble.gap().getAddress(&temp_address_type, myMacAddress);
    ackServicePtr = new ACKService<4>(ble, init_values);
    ackServicePtr->updateMacAddress(myMacAddress);    // Update MAC address
    
    ble.gap().onDisconnection(disconnectionCallback);
    ble.gap().onConnection(onConnectionCallback);         
    ble.gattServer().onDataWritten(onDataWrittenCallback);
    
#if ACN_FREIGHT
    AdvertisingFormat msd;
    
    msd.manufacturerID = MANUFACTURER_ID;
    msd.aconnoID = ACONNO_ID;
    msd.productID = PRODUCT_ID;
    msd.version = VERSION;
    
    ble.gap().accumulateAdvertisingPayload(
        GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA,
        (uint8_t*)&msd, sizeof(msd));
#endif
    
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, (uint8_t*)UUID, sizeof(UUID));
    ble.gap().setAdvertisingInterval(ADV_INTERVAL);  // --> Has to be at least 100ms!
}



void startAdvertising(){    
    ble.gap().startAdvertising();
    #if DEBUG
        advLED = 0;
        scanLED = 1;
    #endif
    WakeSleepT.detach();
    WakeSleepT.attach(WakeMeUp, ADV_TIMER_TIME_S);    // Call the wakeMeUp function
}

void WakeMeUp(){
    sleepFlag = false;
    switch(radioState){
        case OFF:{
                radioState = ADVERTISING;
                startAdvertising();
                break;
            }
        case ADVERTISING:{
                radioState = OFF;
                WakeSleepT.detach();
                WakeSleepT.attach(GoToSleep, FREE_TIME_S);
                break;
            }
        default: return;
    }
}

void GoToSleep(){
    WakeSleepT.detach();
    WakeSleepT.attach(WakeMeUp, SLEEP_TIME_S);
    ble.gap().stopAdvertising();
    sleepFlag = true;
    #if DEBUG
        advLED = 1;
        scanLED = 1;
    #endif
}

#if USE_ACC
    void pulse_handler(){
    #if DEBUG_WAKEUP_BUZZER
        buzzerStart();
        wait_ms(50);
        buzzerStop();
    #endif
    #if DEBUG_ACC
        int_led = !int_led;
    #endif
    }
#endif

int main(){
    #if DEBUG || DEBUG_MAC
        advLED = 1;
        scanLED = 1;
        connectedLED = 1;
    #endif
    
    #if USE_ACC
        accPower = 1;
        i2cPower = 1;
    #endif 

    #if ACN_FREIGHT
        signal_led = 1;
    #endif
    
    ble.init(bleInitComplete);
    /* 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 */ }
    __enable_irq();
            
    buzzerStart();
    wait_ms(500);
    buzzerStop();
        
    WakeSleepT.attach(GoToSleep, AWAKE_TIME_S);
    
    while(true){   
        ble.waitForEvent();
    }
}