Dependencies:   BLE_API mbed nRF51822

Fork of SensorModulePIR by ModoSmart

main.cpp

Committer:
MisterGiet
Date:
2017-05-17
Revision:
8:a0e1222bd768
Parent:
7:19fe55e82dd9

File content as of revision 8:a0e1222bd768:

#include "mbed.h"
#include "defineGPIOs.h"
#include "BLE.h"
//#include "HealthThermometerService.h"
#include "ble/services/HealthThermometerService.h"  // added
#include "PresenceDetectionService.h"
#include "HumidityMeasureService.h"
#include "ble_gatt.h"
#include "Si7020.h"
#include "UARTService.h"
#include <string.h>
#include "DiscoveredCharacteristic.h"
#include "DiscoveredService.h"


// OFFSET TEMPERATURE CONFIGURATIONS:
#define STANDARD -1  // Squared radar sensor module: -1ºC
#define ZERO 0  // no offset
#define OFFSET STANDARD

#define NEED_SERIAL_CONSOLE_OUTPUT 1
#if NEED_SERIAL_CONSOLE_OUTPUT
Serial pc(PIN_TX, PIN_RX);
#define SERIAL_DEBUG(...) { printf(__VA_ARGS__); }  //Defaults to stdio without having to wirte pcUart explicitly
#else
#define SERIAL_DEBUG(...) /* nothing */
#endif

#define NEED_BLE_OUTPUT 0 // Set this if you need debug messages on the console;
#if NEED_BLE_OUTPUT
#define DEBUG(STR) { if (uart) uart->write(STR, strlen(STR)); }
#else
#define DEBUG(...) /* nothing */
#endif /* #if NEED_CONSOLE_OUTPUT */

#define CHARACTERISTIC_PRES_UUID 0xA001
#define CHARACTERISTIC_TEMP_UUID 0x2A1C
#define CHARACTERISTIC_HUM_UUID 0xA005

#define SERVICE_PRES_UUID 0xA000
#define SERVICE_TEMP_UUID 0x1809
#define SERVICE_HUM_UUID 0xA004

I2C i2c(PIN_SDA, PIN_SCL);
Si7020 tempsensor(&i2c);

InterruptIn     motion_pin(PIN_PRESENCE_RIGHT);
DigitalIn       pinR(PIN_PRESENCE_RIGHT, PullNone);
DigitalOut      ledB(PIN_BLED_PCB, 1);
DigitalOut      ledR(PIN_RLED_PCB, 1);

//bool presenceState = false;
int presenceCounter = 0;
bool installMode = 1;   // to turn on leds and fast update of BLE services
bool battNotify = 1;
static volatile bool  triggerPresencePolling = false;
static volatile bool  triggerSensorPolling  = false;


/* BLE VARS CONFIGURATION */

BLE ble;

bool foundPresenceCharacteristic = false;
bool foundTemperatureCharacteristic = false;
bool foundHumidityCharacteristic = false;

DiscoveredCharacteristic presenceCharacteristic;
DiscoveredCharacteristic temperatureCharacteristic;
DiscoveredCharacteristic humidityCharacteristic;

PresenceDetectionService *presenceService = NULL;
static HealthThermometerService *thermometerServicePtr;
HumidityMeasureService *humidityService = NULL;
UARTService *uart;

int     neighborPresence = 0;
float   neighborTemperature = 0;
int     neighborHumidity = 0;

uint8_t     PEER_DEV_NAME[4] = {0x53,0x4D,0x3F,0x3F};   // = SM??
static const char       DEVICE_NAME[]         = "SM01";
static const uint16_t   uuid16_list[]         = {GattService::UUID_HEALTH_THERMOMETER_SERVICE};
static char             fwversion[31]         = "SON-2017";
static uint8_t          batteryLevel          = 100;
static int8_t           TxPower               = +4;
const static uint8_t    ManufData[] = {0x01,0x02,0x03,0x04,0x05};   // Set up to 26B of advertising data to use for the Manufacturer data.

// Firmware
GattCharacteristic  fwChars(GattCharacteristic::UUID_FIRMWARE_REVISION_STRING_CHAR, (uint8_t *)fwversion, sizeof(fwversion), sizeof(fwversion),GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
GattCharacteristic *firmwareChars[] = {&fwChars };
GattService         firmwareService(GattService::UUID_DEVICE_INFORMATION_SERVICE, firmwareChars, sizeof(firmwareChars) / sizeof(GattCharacteristic *));

//Battery
GattCharacteristic  batteryPercentage(GattCharacteristic::UUID_BATTERY_LEVEL_CHAR, (uint8_t *)batteryLevel, sizeof(batteryLevel), sizeof(batteryLevel), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
GattCharacteristic *batteryChars[] = {&batteryPercentage};
GattService         batteryService(GattService::UUID_BATTERY_SERVICE, batteryChars, sizeof(batteryChars) / sizeof(GattCharacteristic *));

//Power
GattCharacteristic  TxPowerChar(GattCharacteristic::UUID_TX_POWER_LEVEL_CHAR, (uint8_t*)&TxPower, sizeof(TxPower), sizeof(TxPower), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
GattCharacteristic *charTable[] = {&TxPowerChar};
GattService         TxPowerService(GattService::UUID_TX_POWER_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));

/* BATTERY MEASUREMENT */
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 (((NRF_ADC->BUSY & ADC_BUSY_BUSY_Msk) >> ADC_BUSY_BUSY_Pos) == ADC_BUSY_BUSY_Busy) {};
    return (uint16_t)NRF_ADC->RESULT; // 10 bit
}

float getBatteryVolt (void)
{
    char Vpower[10];

    float Vadc = (float)my_analogin_read_u16();
    float Vref = 1.2; // Internal Vref
    float Vcc = 3 * 4 * Vref * Vadc / 1024.0;

    sprintf (Vpower, "Vcc=%.2fV, ", Vcc);
    DEBUG(Vpower);
    return Vcc;
}

int getBatteryPercent ()
{
    char batt_mess[10];
    float Vbat_min = 2; //2;
    float Vbat_max = 3;
    int battLevel = (int) ( ((getBatteryVolt()-Vbat_min) / (Vbat_max-Vbat_min)) *100);

    sprintf (batt_mess, "Vbatt=%i, ", battLevel);
    DEBUG(batt_mess);

    // To avoid values bigger than 100%
    if (battLevel > 100) {
        battLevel = 100;
    }

    // To avoid overflow due to a value lower than Vbat_min
    if (battLevel >= 250) {
        battLevel = 0;
    }

    return battLevel;
}

void blinkLed (bool enable, int color)
{
    if (enable) {
        if (color == 1) {
            ledB=0;
            wait(0.2);
            ledB=1;
            wait(0.2);
        }
        if (color == 2) {
            ledR=0;
            wait(0.2);
            ledR=1;
            wait(0.2);
        }
    } else {
        // do nothing if enable=0
    }
}

uint8_t updatePresenceCounter (int counts)
{
    uint8_t uint8_counter;

    if (counts>=255) {
        uint8_counter = 255;

    } else {
        uint8_counter = uint8_t(counts);
    }

    return uint8_counter;
}
float getTemperature(void)
{
    float temp;

    if(tempsensor.getTemperature(&temp) != 0) {
        DEBUG("Error getting temperature");
        temp = -1;
    }

    // Debugging:
    SERIAL_DEBUG("\nTemperature = %.2f", temp);
    char message[50];
    sprintf (message, "T=%.2fC\n", temp);
    DEBUG(message);

    // Adding offset:
    temp = temp + OFFSET;

    return temp;
}

float getHumidity(void)
{
    float hum;
    if(tempsensor.getHumidity(&hum) != 0) {
        SERIAL_DEBUG("Error getting humidity");
        hum = -1;
    }

    // Debugging:
    SERIAL_DEBUG("\nHumidity = %f", hum);
    char message[50];
    sprintf (message, "RH=%d\n", (int)hum);
    DEBUG(message);

    return hum;
}

/**
 * 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 */
}

void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params)
{
    //ble.gap().getAdvertisingPayload();  // superfluo?
    SERIAL_DEBUG("\n*****************************");
    SERIAL_DEBUG("\n>>Start advertisementCallback");

    // Some nice debugging:
    SERIAL_DEBUG("\nLooking for devices in the neighbourhood....");
    SERIAL_DEBUG("\nThe peer's BLE address: ");
    for(uint8_t index=0; index<=5; index++) {
        SERIAL_DEBUG("%02x ", params->peerAddr[5-index]);  // for such a reason the address is reverse-ordered
    }
    SERIAL_DEBUG("\nThe peer's RSSI value: %i", params->rssi);
    SERIAL_DEBUG("\nThe peer's is ScanResponse: %i", params->isScanResponse);
    SERIAL_DEBUG("\nThe peer's Advertisement type: %i", params->type);
    SERIAL_DEBUG("\nThe peer's advertisement Payload: ");
    for(uint8_t index=0; index<=30; index++) {
        SERIAL_DEBUG("%02x ", params->advertisingData[index]);
    }
    SERIAL_DEBUG("\nThe peer's DEVICE NAME: ");
    for(uint8_t index=23; index<=26; index++) {
        SERIAL_DEBUG("%c", params->advertisingData[index]);
    }
    SERIAL_DEBUG("(");
    for(uint8_t index=23; index<=26; index++) {
        SERIAL_DEBUG("%02x:", params->advertisingData[index]);
    }
    SERIAL_DEBUG(") ");

    /* If the central device hasn't found yet the peripheral:*/
    //if (!deviceFound) {

        // If the peers peripheral does not correpond to the desired PEER_DEV_NAME: exit
        if ( (params->advertisingData[23] != PEER_DEV_NAME[0]) || (params->advertisingData[24] != PEER_DEV_NAME[1])
                || (params->advertisingData[25] != PEER_DEV_NAME[2]) || (params->advertisingData[26] != PEER_DEV_NAME[3]) ) {

            SERIAL_DEBUG("\n>PEER DEVICE NOT FOUND\n");
            return;
            
        } else {
            BLE::Instance().gap().connect(params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL);
            //deviceFound = true;
            SERIAL_DEBUG("\n CONNECTED!\n");
            blinkLed(installMode,1);
            blinkLed(installMode,1);
        }
    //}
    SERIAL_DEBUG("\n> End advertisementCallback\n");
}

void serviceDiscoveryCallback(const DiscoveredService *service)
{
    SERIAL_DEBUG("\n>>> SERVICE DISCOVERY \n");
    if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) {
        SERIAL_DEBUG("  S UUID-%x attrs[%u %u]\r\n", service->getUUID().getShortUUID(), service->getStartHandle(), service->getEndHandle());
    } else {
        printf("  S UUID-");
        const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID();
        for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) {
            SERIAL_DEBUG("%02x", longUUIDBytes[i]);
        }
        SERIAL_DEBUG(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle());
    }
    SERIAL_DEBUG("<<< End Service discovery Callback\n\n");
}

void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP)
{
    SERIAL_DEBUG("  >> CHARACTERISTIC DISCOVERY \n");
    SERIAL_DEBUG("  UUID %x\n", characteristicP->getUUID().getShortUUID());
    SERIAL_DEBUG("      C UUID-%02x valueAttr[%02x] props[%02x] DEVICE_NAME[%02x]\n", characteristicP->getUUID(), characteristicP->getValueHandle(),
                 characteristicP->getProperties().broadcast());

    if (characteristicP->getUUID().getShortUUID() == CHARACTERISTIC_PRES_UUID) {
        SERIAL_DEBUG("-->Found presence characteristic! ** \n");
        presenceCharacteristic      = *characteristicP;
        foundPresenceCharacteristic = true;
    }

    if (characteristicP->getUUID().getShortUUID() == CHARACTERISTIC_TEMP_UUID) {
        SERIAL_DEBUG("-->Found temperature characteristic! ** \n");
        temperatureCharacteristic      = *characteristicP;
        foundTemperatureCharacteristic = true;
    }

    if (characteristicP->getUUID().getShortUUID() == CHARACTERISTIC_HUM_UUID) {
        SERIAL_DEBUG("-->Found humidity characteristic! ** \n");
        humidityCharacteristic      = *characteristicP;
        foundHumidityCharacteristic = true;
    }
    SERIAL_DEBUG("  << End Characteristic discovery Callback\r\n");
}

void discoveryTerminationCallback(Gap::Handle_t connectionHandle)
{
    SERIAL_DEBUG("Terminated Discovery for handle %u\r\n", connectionHandle);
    blinkLed(installMode,1);
    blinkLed(installMode,1);
    blinkLed(installMode,1);
}

void connectionCallback(const Gap::ConnectionCallbackParams_t *params)
{
    SERIAL_DEBUG(">> Connection Callback\r\n");
    if (params->role == Gap::CENTRAL) {
        SERIAL_DEBUG("Launching discovery of BLE services and characteristics...\n");
        //connectionHandle = params->handle;
        ble.gattClient().onServiceDiscoveryTermination(discoveryTerminationCallback);
        ble.gattClient().launchServiceDiscovery(params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback);
    }

    SERIAL_DEBUG("> End Connection Callback\r\n");
}

void sendRemoteValues(const GattWriteCallbackParams *response)
{
    SERIAL_DEBUG("Presence resetted \n");
}

void getRemoteValues(const GattReadCallbackParams *response)
{
    SERIAL_DEBUG("\nStart getRemote Values\n");
    if (response->handle == presenceCharacteristic.getValueHandle()) {
#if DUMP_READ_DATA
        printf("triggerToggledWrite: handle %u, offset %u, len %u\r\n", response->handle, response->offset, response->len);
        for (unsigned index = 0; index < response->len; index++) {
            printf("%c[%02x]", response->data[index], response->data[index]);
        }
        printf("\r\n");

#endif
        neighborPresence = response->data[0];
        SERIAL_DEBUG("Neighbour presence: %d", neighborPresence);
    } else {
        SERIAL_DEBUG("\nERROR while getting presence value\n");
    }

    if (response->handle == temperatureCharacteristic.getValueHandle()) {
#if DUMP_READ_DATA
        SERIAL_DEBUG("triggerToggledWrite: handle %u, offset %u, len %u\r\n", response->handle, response->offset, response->len);
        for (unsigned index = 0; index < response->len; index++) {
            printf("%c[%02x]", response->data[index], response->data[index]);
        }
        SERIAL_DEBUG("\nERROR while getting temperature value\n");
        printf("\r\n");
#endif
        neighborTemperature = float( (response->data[1]) + (response->data[2] << 8) ) /100;
        SERIAL_DEBUG("Neighbour temperature: %.2f (%x:%x:%x:%x\n)\n", float(neighborTemperature),response->data[0],response->data[1],response->data[2],response->data[3]);
        //SERIAL_DEBUG("Neighbour temperature(HEX): %x:%x:%x:%x\n", response->data[0],response->data[1],response->data[2],response->data[3]);
    }

    if (response->handle == humidityCharacteristic.getValueHandle()) {
#if DUMP_READ_DATA
        printf("triggerToggledWrite: handle %u, offset %u, len %u\r", response->handle, response->offset, response->len);
        for (unsigned index = 0; index < response->len; index++) {
            printf("%c[%02x]", response->data[index], response->data[index]);
        }
        printf("\r\n");
#endif
        neighborHumidity = response->data[0];
        SERIAL_DEBUG("Neighbour humidity: %f", neighborHumidity);
    } else {
        SERIAL_DEBUG("\nERROR while getting temperature value\n");
    }
    SERIAL_DEBUG("> End getRemote Values\r\n");
}

/* Restart Advertising on disconnection*/
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
    SERIAL_DEBUG("> Disconnected\r\n");
    BLE::Instance().gap().startAdvertising();
    ble.gap().setScanParams(500, 100);
    BLE::Instance().gap().startScan(advertisementCallback);
}

void onDataWrittenCallback(const GattWriteCallbackParams *params)
{
    int sel = params->data[0];

    if (sel == 1) {
        blinkLed(installMode,1);
        SERIAL_DEBUG("BLE. Resetting presence\n");
        presenceCounter = 0;
        triggerSensorPolling = true;       // force update of all BLE services
    }

    if (sel == 2) {
        blinkLed(1,1);
        blinkLed(1,1);
        SERIAL_DEBUG("BLE. All LEDs ON/OFF\n");
        installMode = !installMode;
    }

    if (sel == 3) {
        blinkLed(1,1);
        blinkLed(1,1);
        blinkLed(1,1);
        SERIAL_DEBUG("BLE. Rebooting sensor\n");
        wait(3);
        NVIC_SystemReset(); // SW Reset
    }
}

/**
 * Callback triggered when the ble initialization process has finished
 */
void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
    SERIAL_DEBUG("BLE. Init \n");

    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;
    }

    //ble.gap().onConnection(connectionCallback);
    ble.gap().onDisconnection(disconnectionCallback);

    // Set Transmission power:
    ble.setTxPower(TxPower);

    //ble.gap().setScanParams(500, 100);
    //ble.gap().startScan(advertisementCallback);

    //BLE server setup
    ble.gattServer().onDataWritten(onDataWrittenCallback);

    //Setup primary services
    presenceService = new PresenceDetectionService(ble, updatePresenceCounter(presenceCounter));
    thermometerServicePtr = new HealthThermometerService(ble, getTemperature(), HealthThermometerService::LOCATION_EAR);
    humidityService = new HumidityMeasureService(ble, getHumidity());
    ble.addService(batteryService);
    ble.addService(firmwareService);
    uart = new UARTService(ble);
    ble.addService(TxPowerService);


    /* setup advertising */

    /* 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, ManufData, sizeof(ManufData));
    ble.gap().accumulateAdvertisingPayloadTxPower(TxPower);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
    //ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::THERMOMETER_EAR);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER);
    //ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,(const uint8_t *)"BLE UART", sizeof("BLE UART") - 1);
    //ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)UARTServiceUUID_reversed, sizeof(UARTServiceUUID_reversed));
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));

    ble.setAdvertisingInterval(1000); /* 1000ms */
    ble.startAdvertising();
    SERIAL_DEBUG("\nBLE Init completed\n");
}

void periodicCallbackPresence(void)
{
    triggerPresencePolling = true;
    //presenceState = true;
}

void periodicCallbackSensor (void)
{
    triggerSensorPolling = true;
}

void updateBLEservices ()
{
    thermometerServicePtr->updateTemperature(getTemperature());
    humidityService->updateHumidity(getHumidity());
    presenceService->updatePresence(updatePresenceCounter (presenceCounter));
    batteryLevel = getBatteryPercent();
    ble.updateCharacteristicValue(batteryPercentage.getValueAttribute().getHandle(), &batteryLevel, sizeof(batteryLevel));
    ble.updateCharacteristicValue(TxPowerChar.getValueAttribute().getHandle(), (uint8_t*)&TxPower, 1);
}

int main(void)
{
    SERIAL_DEBUG("Start \n");
    char pres_message[20];

    blinkLed(1,1);

    motion_pin.fall(&periodicCallbackPresence);

    Ticker ticker_sensor;
    ticker_sensor.attach(periodicCallbackSensor, 300); // 5 min

    //BLE instance setup
    BLE &bleptr = BLE::Instance();
    bleptr.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) {    }
    */

    while (true) {

        /* Update presence data if presence is detected:*/
        if(triggerPresencePolling) {
            triggerPresencePolling = false;
            presenceCounter++;

            /* if in installMode, blinkleds and update BLE services continuosly */
            if (installMode) {
                updateBLEservices();    // Update BLE services
                blinkLed(1,1);      // blinkLed
            }

            // Debugging: 
            SERIAL_DEBUG("Presence counter: %i\n", presenceCounter);
            sprintf (pres_message, "Pres=%i\n", presenceCounter);
            DEBUG(pres_message);
        }

        // if no presence:
        else {
            SERIAL_DEBUG(".");
        }

        //if (triggerSensorPolling && ble.getGapState().connected) {
        /* Update BLE services only every 300sec or if connected: */
        //if (triggerSensorPolling || ble.getGapState().connected) {

        /* Update BLE services only if connected: */
        if (ble.getGapState().connected) {

            // Quanto consuma quando sta leggendo la temperatura?
            //triggerSensorPolling = false;
            updateBLEservices();

            // Red LED blinks if low battery, at every connection
            if (batteryLevel <= 10) {
                blinkLed(battNotify,2);
            }

        } else {
            ble.waitForEvent();
        }
    }
}