Redbear Nano firmware using Sandroide and showing how to send custom strings and parameters to the Android application

Dependencies:   BLE_API MbedJSONValue mbed nRF51822

Fork of BLE_RedBearNano-SAndroidEDevice by gio wild

main.cpp

Committer:
giowild
Date:
2016-11-28
Revision:
0:8d3dd6e411bf
Child:
1:ff6ec7837f46

File content as of revision 0:8d3dd6e411bf:

/*

Copyright (c) 2016 Giovanni Lenzi

*/

/*
 *    This firmware is a port the RedBear BLE Shield Arduino Sketch(https://github.com/RedBearLab/nRF8001/blob/master/examples/BLEControllerSketch/BLEControllerSketch.ino), 
 *    to Redbear Nano (http://redbearlab.com/blenano/).
 *    After connection of Nano to PC using the provided MK20 USB board, 
 *    you need to download the compiled firmware to your local disk 
 *    and drag and drop it to the newly created MBED drive. 
 *    Once flashed, you may access your Nano device with the NanoChat test application 
 *    or from any SAndroidE powered application (http://es3.unibs.it/SAndroidE/)
 */

#include "mbed.h"
#include "ble/BLE.h"

#define BLE_UUID_TXRX_SERVICE            0x0000 /**< The UUID of the Nordic UART Service. */
#define BLE_UUID_TX_CHARACTERISTIC       0x0002 /**< The UUID of the TX Characteristic. */
#define BLE_UUIDS_RX_CHARACTERISTIC      0x0003 /**< The UUID of the RX Characteristic. */

#define TXRX_BUF_LEN                     20

// pin modes
#define PIN_INPUT                 0 // digital input pin
#define PIN_OUTPUT                1 // digital output pin
#define PIN_ANALOG                2 // analog pin in analogInput mode
#define PIN_PWM                   3 // digital pin in PWM output mode
#define PIN_SERVO                 4 // digital pin in Servo output mode
#define PIN_NOTSET                5 // pin not set

BLE  ble;

// The Nordic UART Service
static const uint8_t uart_base_uuid[] = {0x71, 0x3D, 0, 0, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
static const uint8_t uart_tx_uuid[]   = {0x71, 0x3D, 0, 3, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
static const uint8_t uart_rx_uuid[]   = {0x71, 0x3D, 0, 2, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
static const uint8_t uart_base_uuid_rev[] = {0x1E, 0x94, 0x8D, 0xF1, 0x48, 0x31, 0x94, 0xBA, 0x75, 0x4C, 0x3E, 0x50, 0, 0, 0x3D, 0x71};

uint8_t payloadTicker[TXRX_BUF_LEN] = {0,};
uint8_t txPayload[TXRX_BUF_LEN] = {0,};
uint8_t rxPayload[TXRX_BUF_LEN] = {0,};

GattCharacteristic  txCharacteristic (uart_tx_uuid, txPayload, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);
GattCharacteristic  rxCharacteristic (uart_rx_uuid, rxPayload, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
GattCharacteristic *uartChars[] = {&txCharacteristic, &rxCharacteristic};
GattService         uartService(uart_base_uuid, uartChars, sizeof(uartChars) / sizeof(GattCharacteristic *));

static const int maxPins = 30;
uint8_t pinTypes[maxPins];
uint16_t prevValues[maxPins];

DigitalInOut digitals[] = {P0_0,P0_7,P0_15,P0_19,P0_28,P0_29};
int mapDigitals[] = { 0,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1, 2,-1,-1,-1, 3,-1,-1,-1,-1,-1,-1,-1,-1, 4, 5,-1};
AnalogIn analogs[] = {P0_1, P0_2, P0_3, P0_4, P0_5, P0_6};
int mapAnalogs[] =  {-1, 0, 1, 2, 3, 4, 5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};


void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
    //pc.printf("Disconnected \r\n");
    //pc.printf("Restart advertising \r\n");
    ble.startAdvertising();
}

bool initPin(int pin, uint8_t type){
    bool ret=false;
    if (pin>=0 && pin<31) {       // "initPin(): Pin number out of bounds"
        if ((type==PIN_INPUT||type==PIN_OUTPUT) && mapDigitals[pin]>=0) {
            if (type==PIN_INPUT) digitals[mapDigitals[pin]].input();  // initialize as input
            if (type==PIN_OUTPUT) digitals[mapDigitals[pin]].output(); // initialize as input
            pinTypes[pin] = type; // mark the pin as initialized
            ret =true;
        } else if (type==PIN_ANALOG && mapAnalogs[pin]>=0) {
            pinTypes[pin] = type; // mark the pin as initialized
            ret =true;
        }
    }
    return ret;
}

uint16_t readPin(int pin) {
    uint8_t mode = pinTypes[pin];
    if (mode==PIN_INPUT) { // exists and is initialized as digital output
        mode = 0;
        return (((uint16_t)mode)<<8 | (uint16_t)(digitals[mapDigitals[pin]].read()));
    } else if (mode==PIN_OUTPUT) { // exists and is initialized as digital output
        mode = 1;
        return (((uint16_t)mode)<<8 | (uint16_t)(digitals[mapDigitals[pin]].read()));
    } else if (mode==PIN_ANALOG) { // exists and is initialized as digital output
        mode = 2;
        uint16_t value = analogs[mapAnalogs[pin]].read_u16();
        uint8_t value_lo = value;
        uint8_t value_hi = value>>8;
        mode = (value_hi << 4) | mode;
        return (((uint16_t)mode)<<8) | (uint16_t)value_lo;
    }
    return 0;
}

void sendPinValue(int pin, uint16_t value) {
    uint8_t buf[TXRX_BUF_LEN];
    buf[0]='G';
    buf[1]=pin;
    buf[2]=(uint8_t)(value>>8); buf[3]=(uint8_t)value;
    ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, 4);
    prevValues[pin] = value;
}

void parseRedBearCmd(){
    uint8_t buf[TXRX_BUF_LEN];
    memset(buf, 0, TXRX_BUF_LEN);
    
    uint8_t startOffset = txPayload[0]==0?1:0;
    uint8_t index = startOffset;
    uint8_t cmd = txPayload[index++], pin=txPayload[index++], mode=PIN_NOTSET;
    pin = pin>=48?pin-48:pin;

    switch (cmd) {
        case 'Z':sendPinValue(pin,readPin(pin));
            break;
        case 'X':initPin(7, PIN_OUTPUT);
            break;
        case 'Y':uint8_t value2write = txPayload[index++]-48;
                    if (mapDigitals[pin]>=0) {
                        digitals[mapDigitals[pin]].write(value2write==0?0:1);
                        sendPinValue(pin,readPin(pin));
                    }
            break;

        case 'M': //pc.printf("Querying pin %u mode\n",pin);
            buf[0]=cmd;
            buf[1]=pin;
            buf[2]=pinTypes[pin];
            ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, 3);
            break;
            
        case 'S': // set pin mode
            mode = txPayload[index++];
            mode = mode>=48?mode-48:mode;
            if (initPin(pin, mode)) { // analogs are already initialized
                sendPinValue(pin,readPin(pin));
            }
            break;
            
        case 'G': //pc.printf("Reading pin %u\n",pin);
            switch (pinTypes[pin]) {
                case PIN_INPUT:
                case PIN_ANALOG:
                    sendPinValue(pin,readPin(pin));
                    break;
                case PIN_OUTPUT: // TODO: send warning pin not readable (is an output)
                default:  // TODO: send warning pin not initialized
                    buf[0]=PIN_NOTSET;
                    buf[1]=PIN_NOTSET;
                    buf[2]=PIN_NOTSET;
                    ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, 3);
                    break; 
            }
            break; 
            
        case 'T':
            switch (pinTypes[pin]) {
                case PIN_OUTPUT:
                    uint8_t value2write = txPayload[index++];
                    if (mapDigitals[pin]>=0) {
                        digitals[mapDigitals[pin]].write(value2write==0?0:1);
                        sendPinValue(pin,readPin(pin));
                    }
                    break;
                case PIN_INPUT: // TODO: send warning pin not writable (is an input) 
                case PIN_ANALOG: // TODO: send warning pin not writable (is an input) 
                default:  // TODO: send warning pin not initialized
                    buf[0]='T';
                    buf[1]='T';
                    buf[2]='T';
                    ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, 3);
                    break; 
            }
            break; 
            
        default:
            // echo received buffer
            ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), &txPayload[startOffset], strlen((char *)&txPayload[startOffset])); // FIXME
            break;
    }   
}


void WrittenHandler(const GattWriteCallbackParams *Handler)
{   
    uint8_t buf[TXRX_BUF_LEN];
    uint16_t bytesRead;
    
    if (Handler->handle == txCharacteristic.getValueAttribute().getHandle()) //only if empty
    {
        ble.readCharacteristicValue(txCharacteristic.getValueAttribute().getHandle(), buf, &bytesRead);
        memset(txPayload, 0, TXRX_BUF_LEN);
        memcpy(txPayload, buf, TXRX_BUF_LEN); 
        
        ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, bytesRead);
        parseRedBearCmd();
    }
}

void m_status_check_handle(void)
{   
    for (int pin=0; pin<maxPins;pin++){
        if (pinTypes[pin]==PIN_INPUT||pinTypes[pin]==PIN_ANALOG) {
            uint16_t value = readPin(pin);
            //uint16_t prevValue = 33;
            if (prevValues[pin] != value) {
                sendPinValue(pin,value);
            }
        }
    }
}

int main(void)
{
    for (int i=0;i<maxPins;i++) {
        pinTypes[i] = PIN_NOTSET;
        prevValues[i] = 0;
    }
    
    //memset(txPayload, 0, TXRX_BUF_LEN);

    ble.init();
    ble.onDisconnection(disconnectionCallback);
    ble.onDataWritten(WrittenHandler);  
    
   // setup advertising 
    ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
    ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,
                                    (const uint8_t *)"MyNano", sizeof("MyNano") - 1);
    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
                                    (const uint8_t *)uart_base_uuid_rev, sizeof(uart_base_uuid));
    // 100ms; in multiples of 0.625ms. 
    ble.setAdvertisingInterval(160);

    ble.addService(uartService);
    
    ble.startAdvertising(); 

    Ticker ticker;
    ticker.attach(m_status_check_handle, 0.2);
    
    while(1)
    {
        ble.waitForEvent();
    }
}