PIR + LoRa

Dependencies:   BLE_API LMiC SX1276Lib mbed nRF51822 nrf51_rtc

Fork of BLE_PhysicalWeb by Bluetooth Low Energy

main.cpp

Committer:
janjongboom
Date:
2015-09-24
Revision:
12:940eb88a2104
Parent:
11:bbe4157401e2

File content as of revision 12:940eb88a2104:

/* mbed Microcontroller Library
 * Copyright (c) 2006-2013 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mbed.h"
#include "BLEDevice.h"
#include "URIBeaconConfigService.h"
#include "DFUService.h"
#include "DeviceInformationService.h"
#include "nrf51_rtc/nrf51_rtc.h"

#include "lmic.h"
#include "oslmic.h"
#include "debug.h"

/*!
 * When set to 1 the application uses the Over-the-Air activation procedure
 * When set to 0 the application uses the Personalization activation procedure
 */
#define OVER_THE_AIR_ACTIVATION                     1

#if( OVER_THE_AIR_ACTIVATION == 0 )

/*!
 * Defines the network ID when using personalization activation procedure
 */
#define LORAWAN_NET_ID                              ( uint32_t )0x00000000

/*!
 * Defines the device address when using personalization activation procedure
 */
#define LORAWAN_DEV_ADDR                            ( uint32_t )0x73318891

#endif

/*!
 * Defines the application data transmission duty cycle
 */
#define APP_TX_DUTYCYCLE                            5000 // 5 [s] value in ms
#define APP_TX_DUTYCYCLE_RND                        1000 // 1 [s] value in ms

/*!
 * LoRaWAN Adaptative Data Rate
 */
#define LORAWAN_ADR_ON                              1

/*!
 * LoRaWAN confirmed messages
 */
#define LORAWAN_CONFIRMED_MSG_ON                    0

/*!
 * LoRaWAN application port
 */
#define LORAWAN_APP_PORT                            2

/*!
 * User application data buffer size
 */
#if ( LORAWAN_CONFIRMED_MSG_ON == 1 )
#define LORAWAN_APP_DATA_SIZE                       9

#else
#define LORAWAN_APP_DATA_SIZE                       4

#endif

//////////////////////////////////////////////////
// CONFIGURATION (FOR APPLICATION CALLBACKS BELOW)
//////////////////////////////////////////////////

// application router ID (LSBF)
static const uint8_t AppEui[8] =
{ // 13-37-aa-bb-cc-dd-ee-ff
    0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x37, 0x13
    // so its read rtl in the NS so when creating need to flip it in server
    // compared to here. Really weird.
};

// unique device ID (LSBF)
static const u1_t DevEui[8] =
{
    0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF
};

// device-specific AES key (derived from device EUI)
static const uint8_t DevKey[16] =
{
    0xC1, 0x20, 0x2B, 0xB0, 0xAE, 0x97, 0x20, 0xC8,
    0x18, 0xA3, 0x0F, 0xFA, 0x05, 0x14, 0xC5, 0x4F
};

#if( OVER_THE_AIR_ACTIVATION == 0 )
// network session key
static uint8_t NwkSKey[] =
{
    0x50, 0x12, 0x78, 0x26, 0x23, 0x9A, 0x5E, 0x39,
    0x86, 0x8A, 0xCA, 0x7B, 0xDE, 0x16, 0xE1, 0x32
};
static uint8_t ArtSKey[] =
{
    0x54, 0xEB, 0x54, 0x4B, 0x07, 0x0C, 0x10, 0x69,
    0xB1, 0x44, 0x94, 0x44, 0xF1, 0xCC, 0x13, 0xDA
};

#endif

Serial pc(USBTX, USBRX); // tx, rx

// LEDs and Frame jobs
osjob_t rxLedJob;
osjob_t txLedJob;
osjob_t sendFrameJob;

// LED state
static bool AppLedStateOn = false;

//////////////////////////////////////////////////
// Utility functions
//////////////////////////////////////////////////
/*!
 * \brief Computes a random number between min and max
 *
 * \param [IN] min range minimum value
 * \param [IN] max range maximum value
 * \retval random random value in range min..max
 */
int32_t randr( int32_t min, int32_t max )
{
    return ( int32_t )rand( ) % ( max - min + 1 ) + min;
}

//////////////////////////////////////////////////
// APPLICATION CALLBACKS
//////////////////////////////////////////////////

// provide application router ID (8 bytes, LSBF)
void os_getArtEui( uint8_t *buf )
{
    memcpy( buf, AppEui, 8 );
}

// provide device ID (8 bytes, LSBF)
void os_getDevEui( uint8_t *buf )
{
    memcpy( buf, DevEui, 8 );
}

// provide device key (16 bytes)
void os_getDevKey( uint8_t *buf )
{
    memcpy( buf, DevKey, 16 );
}

//////////////////////////////////////////////////
// MAIN - INITIALIZATION AND STARTUP
//////////////////////////////////////////////////

static void onRxLed( osjob_t* j )
{
    debug_val("LED2 = ", 0 );
}

static void onTxLed( osjob_t* j )
{
    debug_val("LED1 = ", 0 );
}

bool rise_state = false;
uint16_t last_rise = 0;

static void prepareTxFrame( void )
{
    uint16_t seconds_ago = rtc.time() - last_rise;
    
    LMIC.frame[0] = 0x02; // PIR sensor
    LMIC.frame[1] = rise_state ? 1 : 0;
    LMIC.frame[2] = ( seconds_ago >> 8 ) & 0xFF;
    LMIC.frame[3] = seconds_ago & 0xFF;
    
    printf("prepareTxFrame %02x %02x %02x %02x\r\n", 
        LMIC.frame[0], LMIC.frame[1], LMIC.frame[2], LMIC.frame[3]);
    
#if ( LORAWAN_CONFIRMED_MSG_ON == 1 )
    LMIC.frame[4] = LMIC.seqnoDn >> 8;
    LMIC.frame[5] = LMIC.seqnoDn;
    LMIC.frame[6] = LMIC.rssi >> 8;
    LMIC.frame[7] = LMIC.rssi;
    LMIC.frame[8] = LMIC.snr;
#endif
}

static void prepareTxFrameLoRaMote( void )
{
    pc.printf("prepareTxFrameLoRaMote\r\n");
    
    uint16_t pressure = 0;
    int16_t altitudeBar = 0;
    int16_t temperature = 0;
    int32_t latitude, longitude = 0;
    uint16_t altitudeGps = 0xFFFF;
    uint8_t batteryLevel = 0;
    
    pressure = 10132;             // in hPa / 10
    temperature = 2300;       // in °C * 100
    altitudeBar = 70;           // in m * 10
    batteryLevel = 160;                        // 1 (very low) to 254 (fully charged)
    altitudeGps = 73;                           // in m
    
    LMIC.frame[0] = AppLedStateOn;
    LMIC.frame[1] = ( pressure >> 8 ) & 0xFF;
    LMIC.frame[2] = pressure & 0xFF;
    LMIC.frame[3] = ( temperature >> 8 ) & 0xFF;
    LMIC.frame[4] = temperature & 0xFF;
    LMIC.frame[5] = ( altitudeBar >> 8 ) & 0xFF;
    LMIC.frame[6] = altitudeBar & 0xFF;
    LMIC.frame[7] = batteryLevel;
    LMIC.frame[8] = ( latitude >> 16 ) & 0xFF;
    LMIC.frame[9] = ( latitude >> 8 ) & 0xFF;
    LMIC.frame[10] = latitude & 0xFF;
    LMIC.frame[11] = ( longitude >> 16 ) & 0xFF;
    LMIC.frame[12] = ( longitude >> 8 ) & 0xFF;
    LMIC.frame[13] = longitude & 0xFF;
    LMIC.frame[14] = ( altitudeGps >> 8 ) & 0xFF;
    LMIC.frame[15] = altitudeGps & 0xFF;
#if ( LORAWAN_CONFIRMED_MSG_ON == 1 )
    LMIC.frame[16] = LMIC.seqnoDn >> 8;
    LMIC.frame[17] = LMIC.seqnoDn;
    LMIC.frame[18] = LMIC.rssi >> 8;
    LMIC.frame[19] = LMIC.rssi;
    LMIC.frame[20] = LMIC.snr;
#endif    
}

DigitalOut led2(LED2);

void processRxFrame( void )
{
    switch( LMIC.frame[LMIC.dataBeg - 1] ) // Check Rx port number
    {
        case 1: // The application LED can be controlled on port 1 or 2
        case 2:
            if( LMIC.dataLen == 1 )
            {
                AppLedStateOn = LMIC.frame[LMIC.dataBeg] & 0x01;
                led2 = AppLedStateOn;
                debug_val( "LED3 = ", AppLedStateOn );
            }
            break;
        default:
            break;
    }
}

static void onSendFrame( osjob_t* j )
{
    prepareTxFrame( );
    LMIC_setTxData2( LORAWAN_APP_PORT, LMIC.frame, LORAWAN_APP_DATA_SIZE, LORAWAN_CONFIRMED_MSG_ON );

    // Blink Tx LED
    debug_val( "LED1 = ", 1 );
    os_setTimedCallback( &txLedJob, os_getTime( ) + ms2osticks( 25 ), onTxLed );
}

// Initialization job
static void onInit( osjob_t* j )
{
    // reset MAC state
    LMIC_reset( );
    LMIC_setAdrMode( LORAWAN_ADR_ON );
    LMIC_setDrTxpow( DR_SF12, 14 );

    // start joining
#if( OVER_THE_AIR_ACTIVATION != 0 )
    pc.printf("startJoining\n");
    LMIC_startJoining( );
#else
    pc.printf("gonne send a frame yo\n");
    LMIC_setSession( LORAWAN_NET_ID, LORAWAN_DEV_ADDR, NwkSKey, ArtSKey );
    onSendFrame( NULL );
#endif
    // init done - onEvent( ) callback will be invoked...
}

//int main( void )
//{
//    osjob_t initjob;
//
//    // initialize runtime env
//    os_init( );
//    // setup initial job
//    os_setCallback( &initjob, onInit );
//    // execute scheduled jobs and events
//    os_runloop( );
//    // (not reached)
//}

//////////////////////////////////////////////////
// LMIC EVENT CALLBACK
//////////////////////////////////////////////////
void onEvent( ev_t ev )
{
    pc.printf("onEvent\r\n");
    bool txOn = false;
    debug_event( ev );

    switch( ev ) 
    {
    // network joined, session established
    case EV_JOINED:
        pc.printf("Joined, ID=%d\n", LMIC.netid);
        debug_val( "Net ID = ", LMIC.netid );
        txOn = true;
        break;
    // scheduled data sent (optionally data received)
    case EV_TXCOMPLETE:
        pc.printf("TXComplete, datarate=%d\n", LMIC.datarate);
        debug_val( "Datarate = ", LMIC.datarate );
        // Check if we have a downlink on either Rx1 or Rx2 windows
        if( ( LMIC.txrxFlags & ( TXRX_DNW1 | TXRX_DNW2 ) ) != 0 )
        {
            debug_val( "LED2 = ", 1 );
            os_setTimedCallback( &rxLedJob, os_getTime( ) + ms2osticks( 25 ), onRxLed );

            if( LMIC.dataLen != 0 )
            { // data received in rx slot after tx
                debug_buf( LMIC.frame + LMIC.dataBeg, LMIC.dataLen );
                processRxFrame( );
            }
        }
        txOn = true;
        break;
    default:
        break;
    }
    if( txOn == true )
    {
        //Sends frame every APP_TX_DUTYCYCLE +/- APP_TX_DUTYCYCLE_RND random time (if not duty cycle limited)
        os_setTimedCallback( &sendFrameJob,
                             os_getTime( ) + ms2osticks( APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ) ),
                             onSendFrame );
        
        ////Sends frame as soon as possible (duty cylce limitations)
        //onSendFrame( NULL );
    }
}


InterruptIn motion(p7);
DigitalOut led(LED4); 

BLEDevice ble;
URIBeaconConfigService *uriBeaconConfig;

void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason)
{
    ble.startAdvertising();
}

void riseHandler(void)
{
    led = 0;
    pc.printf("rise\r\n");
    last_rise = rtc.time();
    rise_state = true;
}

void fallHandler(void)
{
    led = 1;
    pc.printf("fall\r\n");
    last_rise = rtc.time();
    rise_state = false;
}

void lastRiseCallback(void)
{
    pc.printf("Current state %d, last_rise %d\r\n", rise_state, rtc.time() - last_rise);
}

void run_ble() {
    ble.init();
    ble.onDisconnection(disconnectionCallback);

    uriBeaconConfig = new URIBeaconConfigService(ble, "http://goo.gl/H9YK6O");
    if (!uriBeaconConfig->configuredSuccessfully()) {
        error("failed to accommodate URI");
    }
    /* optional use of the API offered by URIBeaconConfigService */
    const int8_t powerLevels[] = {-20, -4, 0, 10};
    uriBeaconConfig->setTxPowerLevels(powerLevels);
    uriBeaconConfig->setTxPowerMode(URIBeaconConfigService::TX_POWER_MODE_HIGH);

    static const uint8_t BEACON_UUID[] = {0xD8, 0xFE};
    static const uint8_t urldata[] = {
        BEACON_UUID[0],
        BEACON_UUID[1],
        0x00, // flags
        0x20, // power
        0x00, // http://www.
        'g',
        'o',
        'o',
        '.',
        'g',
        'l',
        '/',
        'H',
        '9',
        'Y',
        'K',
        '6',
        'O',
    };

    ble.clearAdvertisingPayload();
    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_UUID, sizeof(BEACON_UUID));
    ble.accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, urldata, sizeof(urldata));

    ble.setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
    ble.setAdvertisingInterval(800); /* .5s; in multiples of 0.625ms. */
    ble.startAdvertising();
}

int main(void)
{    
    Ticker ticker;
    ticker.attach(&lastRiseCallback, 10);

    motion.rise(&riseHandler);
    motion.fall(&fallHandler);
    
    run_ble();
    
//    run_ble();
    
//    osjob_t initjob;
////
//    // initialize runtime env
//    os_init( );
//    // setup initial job
//    os_setCallback( &initjob, onInit );
//    
//    os_runloop();

    while (true) {
        // execute scheduled jobs and events
//        os_inner_loop();
        ble.waitForEvent();
    }


    
//    os_runloop( );
    // (not reached)
}