Port of RedBear BLE Shield Arduino Sketch to RedBear Nano. This firmware enables BLE clients to initialize, read and write Nano pins over the air via Bluetooth, using the same protocol used by Redbear BLE Shield. This enables working with Nano devices from any SAndroidE powered application (http://es3.unibs.it/SAndroidE/).

Dependencies:   BLE_API mbed nRF51822 MbedJSONValue

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