BTLE demo for MAXWSNENV.

Dependencies:   BLE_API BMP180 Si7020 mbed MaximBLE

main.cpp

Committer:
enginerd
Date:
2016-08-18
Revision:
2:6f76d6160601
Parent:
1:1876781d537e

File content as of revision 2:6f76d6160601:

/*******************************************************************************
 * Copyright (C) 2015 Maxim Integrated Products, Inc., All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Maxim Integrated
 * Products, Inc. shall not be used except as stated in the Maxim Integrated
 * Products, Inc. Branding Policy.
 *
 * The mere transfer of this software does not imply any licenses
 * of trade secrets, proprietary technology, copyrights, patents,
 * trademarks, maskwork rights, or any other form of intellectual
 * property whatsoever. Maxim Integrated Products, Inc. retains all
 * ownership rights.
 *******************************************************************************
 */

#include <string.h>
#include "mbed.h"
#include "btle_addr.h"
#include "BLE.h"
#include "DeviceInformationService.h"
#include "EnvironmentalService.h"
#include "CurrentTimeService.h"
#include "BMP180.h"
#include "Si7020.h"
#include "LED.h"
#include "pwrseq_regs.h"
#include "pwrman_regs.h"
#include "ioman_regs.h"

#define DEMO_DURATION           30      // sec
#define SENSOR_DEMO_PERIOD      1       // sec
#define NORMAL_SENSOR_PERIOD    60      // sec
#define SENSOR_DELAY_US         30000   // usec

// in multiples of 0.625ms.
#define ADVERT_HIGH_PERIOD      1600    // 1000ms
#define ADVERT_LOW_PERIOD       8000    // 5000ms
#define BLE_UPDATE_DURATION     20      // sec

#define PIN_NUMBER(pinname) ((PINNAME_TO_PORT(pinname) << 3) + PINNAME_TO_PIN(pinname))
#define PIN_MASK(pinname)   (1 << PIN_NUMBER(pinname))

#ifdef BTLE_PWR
DigitalOut ble_pwr(BTLE_PWR, PWR_ON);
#endif

// User I/O objects
DigitalIn sw1(SW1);
DigitalOut red(LED_RED, LED_OFF);
DigitalOut blue(LED_BLUE, LED_OFF);

// BLE Objects
static BLE ble;
static EnvironmentalService *envService;
static CurrentTimeService *timeService;
static LowPowerTimeout bleUpdateTimeout;
static const char     DEVICE_NAME[] = "Environmental Board";
static const uint16_t uuid16_list[] = {GattService::UUID_DEVICE_INFORMATION_SERVICE};
static const Gap::ConnectionParams_t paramsLowPower = {
    400,    /**< Minimum Connection Interval in 1.25 ms units, see @ref BLE_GAP_CP_LIMITS.*/
    400,    /**< Maximum Connection Interval in 1.25 ms units, see @ref BLE_GAP_CP_LIMITS.*/
    60,     /**< Slave Latency in number of connection events, see @ref BLE_GAP_CP_LIMITS.*/
    3100    /**< Connection Supervision Timeout in 10 ms units, see @ref BLE_GAP_CP_LIMITS.*/
};
static Gap::Handle_t connHandle = 0;
static bool connUpdateRequested;

// Sensor objects
I2C i2c(I2C_SDA, I2C_SCL);
BMP180 bmp180(&i2c);
Si7020 si7020(&i2c);
static LowPowerTimeout sampleTimeout;
static LowPowerTicker sampleTicker;
static LowPowerTimeout demoTimeout;
static bool demoMode;

static void bleUpdateCallback(void);

// *****************************************************************************
static void sampleComplete(void) 
{
    int currentPressure;
    float currentHumidity;
    float currentTemperature;
    time_t currentTime;

    red = LED_OFF;
    blue = LED_OFF;

    // Update timestamp
    currentTime = time(NULL);
    if (demoMode) {
        printf("\n%s", ctime(&currentTime));
    }

    /* Get next pressure reading, can be oversampled */
    if(bmp180.getPressure(&currentPressure) != 0) {
        printf("Error getting pressure\n");
    } else {
        envService->updatePressure((float)(currentPressure)/1000.0, currentTime);
        if (demoMode) {
            printf("Press = %0.1f kPa\n", (float)(currentPressure/1000.0));
        }
    }

    // Get Humidity
    if(si7020.checkHumidity(&currentHumidity) != 0) {
        printf("Error getting humidity\n");
    } else {
        envService->updateHumidity(currentHumidity, currentTime);
        if (demoMode) {
            printf("Humid = %0.1f %%\n", currentHumidity);
        }
    }

    // Get temperature
    if(si7020.getPrevTemperature(&currentTemperature) != 0) {
        printf("Error getting temperature\n");
    } else {
        envService->updateTemperature(currentTemperature, currentTime);
        if (demoMode) {
            printf("Temp = %0.1f C\n", currentTemperature);
        }
    }

    // Update the CurrentTime characteristic value
    timeService->updateCurrentTimeValue();
}

// *****************************************************************************
static void sampleStart(void)
{
    if (demoMode) {
        // Blink red LED if connected, blue if disconnected
        if (ble.getGapState().connected) {
            red = LED_ON;
            blue = LED_OFF;
        } else {
            blue = LED_ON;
            red = LED_OFF;
        }
    }

    // Start pressure conversion
    bmp180.startPressure(BMP180::STANDARD);

    /* Start taking measurement for next reading*/
    si7020.startHumidity();

    // Set timeout to read sensors and sample
    sampleTimeout.attach_us(sampleComplete, SENSOR_DELAY_US);
}

// *****************************************************************************
static void setAdvertising(uint16_t interval)
{
    static uint16_t currentInterval = 0;

    if (interval == 0) {
        ble.gap().stopAdvertising();
        currentInterval = 0;
    } else if (!ble.getGapState().connected && (interval != currentInterval)) {
        if (interval > currentInterval) {
            bleUpdateTimeout.attach(bleUpdateCallback, BLE_UPDATE_DURATION);
        }
        ble.gap().stopAdvertising();
        ble.gap().setAdvertisingInterval(interval);
        ble.gap().startAdvertising();
        currentInterval = interval;
    }
}

// *****************************************************************************
static void bleUpdateCallback(void)
{
    if (ble.getGapState().connected) {
        // Reduce connection interval
        ble.gap().updateConnectionParams(connHandle, &paramsLowPower);
        connUpdateRequested = true;
    } else {
        // Reduce advertising interval
        setAdvertising(ADVERT_LOW_PERIOD);
    }
}

// *****************************************************************************
static void demoTimeoutCallback(void)
{
    sampleTicker.attach(sampleStart, NORMAL_SENSOR_PERIOD);
    demoMode = false;
}

// *****************************************************************************
static void demoStart(void)
{
    sampleTicker.attach(sampleStart, SENSOR_DEMO_PERIOD);
    demoMode = true;

    if (!ble.getGapState().connected) {
        // Increase advertising interval
        setAdvertising(ADVERT_HIGH_PERIOD);
    }

    demoTimeout.attach(demoTimeoutCallback, DEMO_DURATION);
}

// *****************************************************************************
void connectionCallback(const Gap::ConnectionCallbackParams_t *params)
{
    /* Record handle and connection settings */
    connHandle = params->handle;
    setAdvertising(0);

    /* Prepare for a connection update */
    connUpdateRequested = false;
    bleUpdateTimeout.attach(bleUpdateCallback, BLE_UPDATE_DURATION);

    printf("Connected\n");
}

// *****************************************************************************
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
    /* Restart Advertising on disconnection */
    connHandle = 0;
    setAdvertising(ADVERT_HIGH_PERIOD);
    printf("Disconnected\n");
}

// *****************************************************************************
void dataReadCallback(const GattReadCallbackParams *eventDataP)
{
    if (!connUpdateRequested) {
        /* Restart the connection update timeout */
        bleUpdateTimeout.attach(bleUpdateCallback, BLE_UPDATE_DURATION);
    }
}

//******************************************************************************
static int setGPIOWUD(PinName pinname, int act_high)
{
  MXC_IOMAN->wud_req0 |= PIN_MASK(pinname);
  if (!(MXC_IOMAN->wud_ack0 & PIN_MASK(pinname))) {
    printf("setGPIOWUD() failed to set P%u.%u as a WUD\n", PINNAME_TO_PORT(pinname), PINNAME_TO_PIN(pinname));
    return -1;
  }

  // Configure WUD for active high/low
  MXC_PWRMAN->wud_ctrl = (PIN_NUMBER(pinname) << MXC_F_PWRMAN_WUD_CTRL_PAD_SELECT_POS) |
                         (MXC_E_PWRMAN_PAD_MODE_ACT_HI_LO << MXC_F_PWRMAN_WUD_CTRL_PAD_MODE_POS);
  if (act_high) {
    MXC_PWRMAN->wud_pulse0 = 1;
  } else {
    MXC_PWRMAN->wud_pulse1 = 1;
  }

  // Activate WUD
  MXC_PWRMAN->wud_ctrl = (PIN_NUMBER(pinname) << MXC_F_PWRMAN_WUD_CTRL_PAD_SELECT_POS) |
                         (MXC_E_PWRMAN_PAD_MODE_CLEAR_SET << MXC_F_PWRMAN_WUD_CTRL_PAD_MODE_POS);
  MXC_PWRMAN->wud_pulse1 = 1; // activate

  // Try to clear I/O wakeup flag
  MXC_PWRSEQ->flags = MXC_F_PWRSEQ_FLAGS_PWR_IO_WAKEUP;
  if (MXC_PWRSEQ->flags & MXC_F_PWRSEQ_FLAGS_PWR_IO_WAKEUP) {
    return -1;
  }

  return 0;
}

//******************************************************************************
static void clearGPIOWUD(PinName pinname)
{
  // I/O must be a wakeup detect to clear
  MXC_IOMAN->wud_req0 |= PIN_MASK(pinname);

  // Clear WUD
  MXC_PWRMAN->wud_ctrl = (PIN_NUMBER(pinname) << MXC_F_PWRMAN_WUD_CTRL_PAD_SELECT_POS) |
                         (MXC_E_PWRMAN_PAD_MODE_CLEAR_SET << MXC_F_PWRMAN_WUD_CTRL_PAD_MODE_POS);
  MXC_PWRMAN->wud_pulse0 = 1; // clear
  MXC_PWRMAN->wud_pulse1 = 1; // activate
  MXC_PWRMAN->wud_pulse0 = 1; // clear

  MXC_IOMAN->wud_req0 &= ~PIN_MASK(pinname);
}

// *****************************************************************************
int main(void)
{
    printf("\n\nEnvironmental demo\n");

    // This wait is needed to prevent the micro from entering DeepSleep before it can be halted by the debugger
    wait(1);

    // Initialize the RTC
    time(NULL);

    // clear wakeup detect requests, flags and mask all wakeups
    MXC_IOMAN->wud_req0 = 0;
    MXC_PWRSEQ->msk_flags = ~MXC_F_PWRSEQ_MSK_FLAGS_RTC_ROLLOVER;

    /* Initialize BLE baselayer */
    ble.init();

    /* Set MAC Address */
    Gap::addr_type_t typeP = BLEProtocol::AddressType::RANDOM_STATIC;
    Gap::address_t address;
    getBtleAddress((uint8_t*)&address);
    ble.gap().setAddress(typeP, address);
    ble.gap().getAddress(&typeP, address);
    printf("BTLE Addr %02X:%02X:%02X:%02X:%02X:%02X\n", (unsigned int)address[5],
                                                        (unsigned int)address[4],
                                                        (unsigned int)address[3],
                                                        (unsigned int)address[2],
                                                        (unsigned int)address[1],
                                                        (unsigned int)address[0]);

    /* Setup Advertising */
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t*)DEVICE_NAME, sizeof(DEVICE_NAME));

    ble.gap().accumulateScanResponse(GapAdvertisingData::INCOMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t*)uuid16_list, sizeof(uuid16_list));
    ble.gap().accumulateScanResponse(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, envServiceUUID, sizeof(envServiceUUID));

    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t*)DEVICE_NAME, sizeof(DEVICE_NAME));
    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);

    /* Prepare Services */
    new DeviceInformationService(ble, "Maxim Integrated",
                                                       "WSNENV",
                                                       "000001",
                                                       "1",
                                                       "1",
                                                       "1");

    envService = new EnvironmentalService(ble, PressureChar::PRES_UNIT_KPA, TemperatureChar::TEMP_UNIT_C);

    timeService = new CurrentTimeService(ble);

    /* Setup Callbacks */
    ble.gap().onDisconnection(disconnectionCallback);
    ble.gap().onConnection(connectionCallback);
    ble.gattServer().onDataRead(dataReadCallback);

    /* Setup BMP180 */
    if (bmp180.init() != 0) {
        printf("Failed to initialize barometer\n");
        while(1);
    }

    /* Start non-blocking pressure reading */
    bmp180.startPressure(BMP180::STANDARD);
    si7020.startHumidity();

    /* Start Advertising */
    setAdvertising(ADVERT_HIGH_PERIOD);

    // Setup reset sample, sample every second for first 10 seconds
    sampleTicker.attach(sampleStart, SENSOR_DEMO_PERIOD);
    demoTimeout.attach(demoTimeoutCallback, DEMO_DURATION);
    demoMode = true;

    while (true) {

        // Prepare to wakeup from SW1
        if (sw1) {
            setGPIOWUD(SW1, 0);
            MXC_PWRSEQ->msk_flags &= ~MXC_F_PWRSEQ_MSK_FLAGS_PWR_IO_WAKEUP;
        }

        ble.waitForEvent();

        // Check if the button was pressed
        if (MXC_PWRMAN->wud_seen0 & PIN_MASK(SW1)) {
            demoStart();
        }

        // disable wakeup
        clearGPIOWUD(SW1);
    }
}