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:
2017-10-05
Revision:
3:16b7d807fa8c
Parent:
2:6c45738bba43
Child:
4:2ef9b332fa7c

File content as of revision 3:16b7d807fa8c:

/*

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"
//#include "UARTService.h"
#include "Queue.h"


//#define MAX_REPLY_LEN           (UARTService::BLE_UART_SERVICE_MAX_DATA_LEN)

#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

#define NO_GROUP                  0 // no_group means that current pin is sampled and transmitted individually


#define ANALOG_MAX_VALUE          1024 // this is uint16 max value: 65535
#define DEFAULT_SAMPLING_INTERVAL 1000   // 1 second
#define DEFAULT_DELTA             10    // this is uint16 in range [0-65535], 655 is 1% delta

BLE  ble;
Queue *recvQueue = NULL, *toSendQueue =NULL;

// 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 *));
//UARTService *m_uart_service_ptr;


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

int pinSamplingIntervals[maxPins];
int pinTimers[maxPins];
uint16_t pinDelta[maxPins];

DigitalInOut digitals[] = {P0_0,P0_7,P0_8,P0_9,P0_10,P0_11,P0_15,P0_19,P0_28,P0_29};
int mapDigitals[] = { 0,-1,-1,-1,-1,-1,-1, 1,2,3,4,5,-1,-1,-1, 6,-1,-1,-1, 7,-1,-1,-1,-1,-1,-1,-1,-1, 8, 9,-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 enqueueItem(Queue *queue, uint8_t *buf, int len) {
    if (ble.getGapState().connected) {
        NODE *item = NULL;
        item = (NODE*) malloc(sizeof (NODE));
        memcpy(item->data.payload, buf, len); 
        item->data.length = len;
        Enqueue(queue, item);
    }
}

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


NODE *enqItem = NULL;
int bytesCmd = 0;
bool endsOnNewLine = true;
#define NOT_FOUND -1

void WrittenHandler(const GattWriteCallbackParams *params)
{   
    uint8_t buf[TXRX_BUF_LEN];
    uint16_t bytesRead, i;

    if (params->handle == txCharacteristic.getValueAttribute().getHandle()) { 
        ble.readCharacteristicValue(params->handle, buf, &bytesRead);
        
        if (enqItem == NULL){
            endsOnNewLine = buf[0]=='{';
        }

        if (!endsOnNewLine) {
            enqueueItem(recvQueue, buf, bytesRead);
        } else {
            for (i=0; i<bytesRead; i++) {
                if (endsOnNewLine && (buf[i]=='\n' || bytesCmd>QUEUE_STRING_LENGTH)) {
                    if (enqItem != NULL && enqItem->data.length>0) {  
                        // push string to queue
                        Enqueue(recvQueue, enqItem);
                    }
                    enqItem = NULL;                
                } else {
                    // enqueue character
                    if (enqItem == NULL) {
                        enqItem = (NODE*) malloc(sizeof (NODE));
                        bytesCmd = 0;
                    }
                    enqItem->data.payload[bytesCmd++]=buf[i];
                    enqItem->data.length = bytesCmd;
                }
            }
        }
    }   
}

uint16_t readPin(uint8_t pin) {
    uint8_t mode = pinTypes[pin];
    if (mode==PIN_INPUT) { // exists and is initialized as digital output
        return digitals[mapDigitals[pin]].read()==0?0:ANALOG_MAX_VALUE;
    } else if (mode==PIN_OUTPUT) { // exists and is initialized as digital output
        return digitals[mapDigitals[pin]].read()==0?0:ANALOG_MAX_VALUE;
    } else if (mode==PIN_ANALOG) { // exists and is initialized as digital output
        return analogs[mapAnalogs[pin]].read_u16();  // 10 bits only
    }
    return 0;
}

uint16_t readPinOld(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(uint8_t 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<<8)>>8);
    enqueueItem(toSendQueue, buf, 4);
    /*int len = sprintf((char *)buf,"{\"pin\":%d,\"v\":%4.3f}",pin,(float)value/(float)ANALOG_MAX_VALUE);
    enqueueItem(toSendQueue,buf, len);*/
    
    prevValues[pin] = value;
}


void sendGroup(uint8_t groupno) {
    uint8_t buf[TXRX_BUF_LEN], i=0;
    buf[i++]='G';
    for (uint8_t pin=0; pin<maxPins;pin++){
        if (pinGroups[pin]==groupno) {
            uint16_t value = readPin(pin);
            buf[i++] = pin;
            buf[i++] = (uint8_t)(value>>8); 
            buf[i++] = (uint8_t) ((value<<8)>>8);
            prevValues[pin] = value;
        }
    }
    if (i>1) { // at least 1 pin value to send
        enqueueItem(toSendQueue, buf, i);
    }
}

bool isInputPin(uint8_t pin) {
    if (pin<maxPins){
        uint8_t type = pinTypes[pin];
        return type==PIN_INPUT||type==PIN_ANALOG;
    }
    return false;
}

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


static volatile int gcd=-1;
Ticker *pTicker;
//Timeout timeout;
static volatile bool recalcTimer = false;
static volatile bool triggerSensorPolling = false;
static volatile bool gcdChanged =false;

int calc_gcd(int n1,int n2) {
    int lgcd=1;
    for(int i=2; i <= n1 && i <= n2; ++i)
    {
        // Checks if i is factor of both integers
        if(n1%i==0 && n2%i==0)
            lgcd = i;
    }
    return lgcd;
}

void check_pin_changed(void) 
{
    uint8_t buf[QUEUE_STRING_LENGTH];
    if (gcd>0) {
        for (int pin=0; pin<maxPins;pin++){
            if (isInputPin(pin)) {
                if (pinTimers[pin] < 0) {
                    pinTimers[pin] = pinSamplingIntervals[pin];
                } else {
                    pinTimers[pin]-=gcd;
                }
                if (pinTimers[pin]==0) {
                    pinTimers[pin] = pinSamplingIntervals[pin];
                    uint16_t value = readPin(pin);
                    if (abs(prevValues[pin]-value) >= pinDelta[pin]) {
                        if (pinGroups[pin]!=NO_GROUP) { // enqueue sending operation for group
                            int len = sprintf((char *)buf,"R%c",pinGroups[pin]);
                            enqueueItem(recvQueue, buf, len);                        
                        } else { // send the pin
                            sendPinValue(pin,value);    
                        }
                    }
                }
            }
        }
    }  
}

void calc_timer_interval()
{
    gcd = -1;
    for (int pin=0; pin<maxPins;pin++){
        if (isInputPin(pin) && pinSamplingIntervals[pin]>0) {
            uint8_t buf[TXRX_BUF_LEN];
            int len = sprintf((char *)buf,"TIMER %d@%d",pin,pinSamplingIntervals[pin]);
            //int len = sprintf((char *)buf,"check-gcd");
            enqueueItem(toSendQueue, buf, len);
            
            if (gcd==-1) {
                gcd = pinSamplingIntervals[pin];
            } else {
                gcd = calc_gcd(gcd,pinSamplingIntervals[pin]);
            }
        }
    }
}

bool initPin(uint8_t pin, uint8_t type){
    bool ret=false,wasset=true,armTimer=false;
   
    //uint8_t buf[TXRX_BUF_LEN];
    //buf[0]='Y';buf[1]=pin;buf[2]=type;
    //int len = sprintf((char *)buf,"ASD%d-%c",pin,pin);
    //enqueueItem(toSendQueue, buf, 3);    
    
    if (pin<maxPins) {       // "initPin(): Pin number out of bounds"
        wasset = pinTypes[pin]!=PIN_NOTSET;
        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;
        }
        if (!wasset && ret && (type==PIN_INPUT||type==PIN_ANALOG)) armTimer=true;
    }
    if (armTimer) {
        pinSamplingIntervals[pin] = DEFAULT_SAMPLING_INTERVAL;
        //pinTimers[pin]=pinSamplingIntervals[pin];
        recalcTimer = true;
    }
    
    return ret;
}

bool initPin(uint8_t pin, uint8_t type, uint8_t group){
    bool ret = initPin(pin, type);
    if (ret){
        pinGroups[pin]=group;
    }
    return ret;
}

void changeDelta(uint8_t pin, uint16_t delta) {
    uint8_t buf[TXRX_BUF_LEN];
    int len = sprintf((char *)buf,"DELTA %d@%d",pin,delta);
    enqueueItem(toSendQueue, buf, len);
    
    //float fdelta = delta / ANALOG_MAX_VALUE;
    if (delta > ANALOG_MAX_VALUE) delta=ANALOG_MAX_VALUE;
    if (isInputPin(pin)) {
        pinDelta[pin] = delta;
    }
}

void changeDeltaPercent(uint8_t pin, float fdelta) {
    changeDelta(pin, (uint16_t)(fdelta*ANALOG_MAX_VALUE));
}
void changeSamplingInterval(uint8_t pin, int interval) {
    if (isInputPin(pin)) {
        pinSamplingIntervals[pin]= interval;
        recalcTimer = true;
    }
}

bool writeDigital(uint8_t pin, bool value){
    if (mapDigitals[pin]>=0) {
        digitals[mapDigitals[pin]].write(value?1:0);
        //sendPinValue(pin,readPin(pin));
    }
}

void parseRedBearCmd(uint8_t* cmdString){
    uint8_t buf[TXRX_BUF_LEN];
    memset(buf, 0, TXRX_BUF_LEN);
    int len=0, scanned=-1, sampling=-1;
    float fdelta=-1; 
    
    uint8_t startOffset = cmdString[0]==0?1:0;
    uint8_t index = startOffset;
    uint8_t cmd = cmdString[index++], pin=cmdString[index++], mode=PIN_NOTSET, group=NO_GROUP;
    pin = pin>=48?pin-48:pin;

    switch (cmd) {
        case '{':
            //snprintf((char*) buf, MAX_REPLY_LEN, "ERROR: Unknown char\n");
            //m_uart_service_ptr->writeString((char*)buf);
            break;
        case 'Y':
            uint8_t value2write = cmdString[index++]-48;
            value2write = value2write>=48?value2write-48:value2write;
            writeDigital(pin,value2write!=0);
            break;

        case 'M': //pc.printf("Querying pin %u mode\n",pin);
            buf[0]=cmd;buf[1]=pin;buf[2]=pinTypes[pin];
            enqueueItem(toSendQueue, buf, 3);
            break;
            
        case 'S': // set pin mode
            mode = cmdString[index++];
            mode = mode>=48?mode-48:mode;
            group = cmdString[index++];
            if (initPin(pin, mode, group)) { // analogs are already initialized
            //if (initPin(pin, mode)) { // analogs are already initialized
                sendPinValue(pin,readPin(pin));
            }
            break;
            
        case 'D': // delta to consider value changed (as percentage [0-1] of Voltage range)
            scanned = sscanf( (char *)&cmdString[2], "%f", &fdelta);
            
            if (scanned==1 && fdelta>=0 && fdelta<=1) {
                len = sprintf((char *)buf,"DELTA%d@%f",(int)pin,fdelta);
                enqueueItem(toSendQueue, buf, len);
                changeDeltaPercent(pin, fdelta);
                /*changeDelta           ( pin,((uint16_t)cmdString[index+0]) << 8  |
                                            ((uint16_t)cmdString[index+1]) );*/
            } else {
                len = sprintf((char *)buf,"DELTA%d@ERR",(int)pin);
                enqueueItem(toSendQueue, buf, len);
            }
            break;
            
        case 'I': // sampling interval
            scanned = sscanf( (char *)&cmdString[2], "%d", &sampling);

            if (scanned==1 && sampling>=0) {
                len = sprintf((char *)buf,"SAMPL%d@%d",(int)pin,sampling);
                enqueueItem(toSendQueue, buf, len);
                changeSamplingInterval( pin, sampling);
            /*changeSamplingInterval( pin,((int)cmdString[index+0]) << 24 | 
                                        ((int)cmdString[index+1]) << 16 | 
                                        ((int)cmdString[index+2]) << 8  |
                                        ((int)cmdString[index+3]) );*/
            } else {
                len = sprintf((char *)buf,"SAMPL%d@ERR",(int)pin);
                enqueueItem(toSendQueue, buf, len);
            }
            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;
                    enqueueItem(toSendQueue, buf, 3);
                    break; 
            }
            break; 
            
        case 'T':
            switch (pinTypes[pin]) {
                case PIN_OUTPUT:
                    uint8_t value2write = cmdString[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';
                    enqueueItem(toSendQueue, buf, 3);
                    break; 
            }
            break; 
        case 'R':
            // pin variable contains the group, not the pin number
            sendGroup(pin);
            break;
        default:
            // echo received buffer
            enqueueItem(toSendQueue, &cmdString[startOffset], strlen((char *)&cmdString[startOffset]));
            break;
    }   
}


void triggerSensor(){
    triggerSensorPolling=true;
}


void changeGcdTiming(){
    uint8_t buf[TXRX_BUF_LEN];
    int len = sprintf((char *)buf,"check-gcd %d",gcd);
    enqueueItem(toSendQueue, buf, len);
}


int main(void)
{
    
    for (int i=0;i<maxPins;i++) {
        pinTypes[i] = PIN_NOTSET;
        prevValues[i] = 0;
        pinSamplingIntervals[i] = -1;
        pinTimers[i]=-1;
        pinDelta[i]=DEFAULT_DELTA;
        pinGroups[i]=NO_GROUP;
    }

    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.attach(m_status_check_handle, 0.2);
    
    // Create a UARTService object (GATT stuff).
    //UARTService myUartService(ble);
    //m_uart_service_ptr = &myUartService;

    Ticker ticker;
    //pTicker = &ticker;
    
    //Ticker pinTicker;
    //pinTicker.attach(triggerSensor, 5);

    Ticker gcdTicker;
    gcdTicker.attach(changeGcdTiming, 5);
    
    recvQueue = ConstructQueue(40);
    toSendQueue = ConstructQueue(40);
    
    uint8_t buf[QUEUE_STRING_LENGTH];
    NODE *deqItem = NULL;
     /*
    // set pin 7 as VCC and pin 28 as GND
    int len = sprintf((char *)buf,"S%c%c",7,1);
    enqueueItem(recvQueue, buf, len);
    len = sprintf((char *)buf,"S%c%c",28,1);
    enqueueItem(recvQueue, buf, len);
    len = sprintf((char *)buf,"Y%c%c",7,'1');
    enqueueItem(recvQueue, buf, len);
    len = sprintf((char *)buf,"Y%c%c",28,'0');
    enqueueItem(recvQueue, buf, len);*/
    
    while(1)
    {
        if (ble.getGapState().connected) {
            if (recalcTimer) {
                recalcTimer =false;
                calc_timer_interval();
                //gcdChanged =true;
                if (gcd>0) {
                    ticker.attach(NULL,5);
                    ticker.attach(triggerSensor, 0.001*gcd);
                } else {
                    ticker.attach(NULL,5);
                }
            } else if (!isEmpty(toSendQueue)) {
                //while (!isEmpty(toSendQueue)) {
                    deqItem = Dequeue(toSendQueue);
                    //memset(buf, 0, QUEUE_STRING_LENGTH);  // useless
                    memcpy(buf, (uint8_t *)deqItem->data.payload, deqItem->data.length); 
                    ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, deqItem->data.length);
                    //ble.updateCharacteristicValue(m_uart_service_ptr->getRXCharacteristicHandle(), buf, deqItem->data.length);
                    free(deqItem);
                //}
            } else if (!isEmpty(recvQueue)) {
                //if (!isEmpty(recvQueue)) {
                    deqItem = Dequeue(recvQueue);
                    memset(buf, 0, QUEUE_STRING_LENGTH); // maybe useless: TO CHECK its handling in parseRedBearCmd
                    memcpy(buf, (uint8_t *)deqItem->data.payload, deqItem->data.length); 
                    //ble.updateCharacteristicValue(m_uart_service_ptr->getRXCharacteristicHandle(), buf, deqItem->data.length);
                    //ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, deqItem->data.length);
                    parseRedBearCmd(buf);
                    free(deqItem);
                //}  
            //} else if (!isEmpty(toSendQueue)) {
            } else if (triggerSensorPolling) {
                triggerSensorPolling = false;
                check_pin_changed();
            } else {
                ble.waitForEvent();    
            }
        } else {
            ble.waitForEvent();
        }
        
        
    }
}