Published

Dependencies:   BLE_API TLC5955 mbed nRF51822

Fork of BLE_LoopbackUART by Bluetooth Low Energy

main.cpp

Committer:
roysandberg
Date:
2018-06-09
Revision:
14:73923b07ae4a
Parent:
13:15764cc1f12c

File content as of revision 14:73923b07ae4a:

/* 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 "ble/BLE.h"
#include "nrf_soc.h" // for internal temp sensor
#include "ble/services/UARTService.h"
#include "TLC5955.h"
#include "sequencer.h"

#include <ctype.h>

#define INPUT_BUFFER_SIZE 256


#define NEED_CONSOLE_OUTPUT 1 /* Set this if you need debug messages on the console;
                               * it will have an impact on code-size and power consumption. */

#if NEED_CONSOLE_OUTPUT
#define DEBUG(...) { printf(__VA_ARGS__); }
#else
#define DEBUG(...) /* nothing */
#endif /* #if NEED_CONSOLE_OUTPUT */

extern void Sequencer();
extern void SequencerConfig();
extern void setChannelToRGB(int channel, uint16_t r, uint16_t g, uint16_t b);
extern int getMode();
extern void setMode(int mode);
extern uint32_t TheElapsedTime;

extern uint16_t debugRedOut[(CHANNELS_PER_IC*NUMBER_OF_ICS)];
extern uint16_t debugGreenOut[(CHANNELS_PER_IC*NUMBER_OF_ICS)];
extern uint16_t debugBlueOut[(CHANNELS_PER_IC*NUMBER_OF_ICS)];

InterruptIn button1(P0_17); // button 1 on nRF51-DK. 
InterruptIn button2(P0_18); // button 2
InterruptIn button3(P0_19); // button 3
InterruptIn button4(P0_20); // button 4

uint8_t lastAmplitude = 100;

uint8_t allRed=0;
uint8_t allGreen=0;
uint8_t allBlue=0;

BLEDevice*  ble;
TLC5955* theChip;
UARTService *uartServicePtr;

// for use by string tokenizer. 10 tokens max
char* tokenList[10];

char inputBuffer[INPUT_BUFFER_SIZE];
int inputBufferOffset = 0;
int lastProcessedOffset = 0;
int currentProcessedOffset = -1;
int dataToProcess = FALSE;

extern uint16_t UserAdjustableMovementInterval;

void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
    DEBUG("BLE Disconnected.\n\r");
    ble->startAdvertising();
}

void connectCallback(const Gap::ConnectionCallbackParams_t *params)
{
    DEBUG("BLE Connected.\n\r");
    ble->stopAdvertising();
}

void onDataWritten(const GattWriteCallbackParams *params)
{
    if ((uartServicePtr != NULL) && (params->handle == uartServicePtr->getTXCharacteristicHandle())) {
        uint16_t bytesRead = params->len;
       DEBUG("received %u bytes\n\r", bytesRead);
       for (int i=0; i< bytesRead;i++) {
            inputBuffer[inputBufferOffset] = params->data[i];
            if (inputBuffer[inputBufferOffset] == 13 || inputBuffer[inputBufferOffset] == 10) {
                dataToProcess = TRUE;
                lastProcessedOffset = currentProcessedOffset;
                currentProcessedOffset = inputBufferOffset;
            }
            inputBufferOffset++;
            inputBufferOffset = inputBufferOffset % INPUT_BUFFER_SIZE;
        }
    }
}

// limited to 20 chars
void writeToBLE(const uint8_t* text) {
    ble->updateCharacteristicValue(uartServicePtr->getRXCharacteristicHandle(), text, strlen((char*)text));
}
    

void periodicCallback(void)
{
    Sequencer();
}

int temperature_data_get(void)
{
    int32_t temp;
    uint32_t err_code;
    
    err_code = sd_temp_get(&temp);
    
    // temp is recorded in 0.25C increments, accurate to +/-4 degrees
    return (int) (temp / 4);

}

typedef enum {
    STR2INT_SUCCESS,
    STR2INT_OVERFLOW,
    STR2INT_UNDERFLOW,
    STR2INT_INCONVERTIBLE
} str2int_errno;

/*
Convert string s to int out.

@param[out] out The converted int. Cannot be NULL.

@param[in] s Input string to be converted.

    The format is the same as strtol,
    except that the following are inconvertible:

    - empty string
    - leading whitespace
    - any trailing characters that are not part of the number

    Cannot be NULL.

@param[in] base Base to interpret string in. Same range as strtol (2 to 36).

@return Indicates if the operation succeeded, or why it failed.
*/
str2int_errno str2int(long *out, char *s, int base) {
    char *end;
    //printf("str2int: s=%s\n\r",s);
    if (s[0] == '\0' || isspace((unsigned char) s[0]))
        return STR2INT_INCONVERTIBLE;
    //errno = 0;
    long l = strtol(s, &end, base);
    //printf("str2int: l=%ld\n\r", l);
    /* Both checks are needed because INT_MAX == LONG_MAX is possible. */
    if (l > 65535 ) {
        printf("Overflow.\n\r");
        return STR2INT_OVERFLOW;
    }
    if (l < 0 ) {
        printf("Underflow.\n\r");
        return STR2INT_UNDERFLOW;
    }
    if (*end != '\0') {
        printf("Inconvertible.\n\r");
        return STR2INT_INCONVERTIBLE;
    }
    printf("OK\n\r");
    *out = l;
    return STR2INT_SUCCESS;
}

// duplicate string
char* strdup ( char* s) {
    char *d = (char*) malloc (strlen (s) + 1);   // Space for length plus nul
    if (d == NULL) return NULL;          // No memory
    strcpy (d,s);                        // Copy the characters
    return d;                            // Return the new string
}

// http://stackoverflow.com/questions/9210528/split-string-with-delimiters-in-c
char** str_split(char* a_str, char a_delim)
{
    //char** result    = 0;
    size_t count     = 0;
    char* tmp        = a_str;
    char* last_comma = 0;
    char delim[2];
    delim[0] = a_delim;
    delim[1] = 0;

    /* Count how many elements will be extracted. */
    while (*tmp)
    {
        if (a_delim == *tmp)
        {
            count++;
            last_comma = tmp;
        }
        tmp++;
    }

    /* Add space for trailing token. */
    count += last_comma < (a_str + strlen(a_str) - 1);

    /* Add space for terminating null string so caller
       knows where the list of returned strings ends. */
    count++;

    //result = malloc(sizeof(char*) * count);


    size_t idx  = 0;
    char* token = strtok(a_str, delim);

    while (token)
    {
        //assert(idx < count);
        *(tokenList + idx++) = strdup(token);
        token = strtok(0, delim);
    }
    //assert(idx == count - 1);
    *(tokenList + idx) = 0;
    
    return tokenList;
}

int getTokenCount(char** tokens) {
    int i;
    for (i=0; tokens[i] != 0;i++);
    return i;
}

void RemoveSpaces(char* source)
{
  char* i = source;
  char* j = source;
  while(*j != 0)
  {
    *i = *j++;
    if(*i != ' ')
      i++;
  }
  *i = 0;
}



void parseInputData() {
    char inputLine[256];
    char outputLine[256];
    int j=0;
    long channel, r, g, b;
    
    for (int i=lastProcessedOffset+1; i != currentProcessedOffset; j++, i++) {
        i = i % INPUT_BUFFER_SIZE;
        inputLine[j] = tolower(inputBuffer[i]);    
    }
    inputLine[j] = 0;
    
    RemoveSpaces(inputLine);
    
    // TODO: Define input command to set channel to color name or to R,G,B setting
    // Define way to set color correction factors for R,G,B
    // Test color correction
    char** tokens = str_split(inputLine, ',');
    int count = getTokenCount(tokens);
    printf("%d tokens:\n\r", count);
    for (int i=0; i< count; i++) {
        printf("%s\n\r",tokens[i]);
    }
    // inputLine is the input string without the trailing carriage return, all lower case
    if (!strcmp(tokens[0],"temp") || !strcmp(tokens[0],"temperature")) {
        // get the temperature  
        sprintf(outputLine,"%dC",temperature_data_get());
        writeToBLE((const uint8_t*)outputLine);  
    } else if (!strcmp(tokens[0],"a") || !strcmp(tokens[0],"amp") || !strcmp(tokens[0],"amplitude")) {
        long val;
        if (str2int(&val, tokens[1], 10) == STR2INT_SUCCESS) {
            if (val < 5) val = 5;
            if (val > 100) val = 100;
            lastAmplitude = (uint8_t) val;
            rebuildGammaTables((uint8_t) ((val*0xFF)/100));
        }

    } else if (!strcmp(tokens[0],"c") || !strcmp(tokens[0],"chan") || !strcmp(tokens[0],"channel")) {
        if (getTokenCount(tokens) == 5) {
            //printf("Processing chan.\n\r");
            // tokens are command name, channel, r, g, b
            if (str2int(&channel, tokens[1], 10) == STR2INT_SUCCESS &&
                str2int(&r, tokens[2], 16) == STR2INT_SUCCESS &&
                str2int(&g, tokens[3], 16) == STR2INT_SUCCESS &&
                str2int(&b, tokens[4], 16) == STR2INT_SUCCESS) {
                    setMode(0);
                    sprintf(outputLine, "%d: %x,%x,%x", (uint16_t) channel, (uint16_t) r, (uint16_t) g, (uint16_t) b);
                    printf ("%s\n\r", outputLine);
                    //wait(0.03)
                    writeToBLE((const uint8_t*)outputLine);
                    setChannelToRGB( (int) channel,  (uint16_t) r,  (uint16_t) g,  (uint16_t) b);
                    theChip->latchData();
            }
        }
    } else if (!strcmp(tokens[0],"list")) {
            printf("Processing list.\n\r");

        // output all channel settings
        for (int i=0; i< CHANNELS_PER_IC * NUMBER_OF_ICS; i++) {
            sprintf(outputLine, "%d: %x, %x, %x", i, debugRedOut[i], debugGreenOut[i], debugBlueOut[i]);
            writeToBLE((const uint8_t*)outputLine);  
            wait(0.03);
        }    
    } else if (!strcmp(tokens[0],"time")) {
            long theTime;
            if (str2int(&theTime, tokens[1], 10) == STR2INT_SUCCESS) {
                TheElapsedTime = ((theTime/100)-1)*60*60*1000 + ((theTime%100)*60*1000) - CLOCK_GRANULARITY;
            }
    } else if (!strcmp(tokens[0],"m") || !strcmp(tokens[0],"mode")) {
        long mode;
        if (str2int(&mode, tokens[1], 10) == STR2INT_SUCCESS) {
            setMode(mode);
            printf("Set mode to %d\n\r", getMode());
            writeToBLE("Set mode.");
        }        
    } else if (!strcmp(tokens[0],"interval") || !strcmp(tokens[0],"int")) {
        long movementInterval;
        if (str2int(&movementInterval, tokens[1], 10) == STR2INT_SUCCESS) {
            if (movementInterval < 300) movementInterval = 300;
            if (movementInterval > 10000) movementInterval = 10000;
            UserAdjustableMovementInterval = (uint16_t) movementInterval;
        }   
    } else if (!strcmp(tokens[0],"base") || !strcmp(tokens[0],"b")) {
        printf ("Got base.\n\r");
        if (getTokenCount(tokens) == 5) {            
            int dither = ditherToEnum(tokens[1]);
            int effect = effectToEnum(tokens[2]);
            const int* colorList = colorListToPointer(tokens[3]);
            long timeConstant;
            // set a fixed base until mode is reset
            if (dither != -1 &&
                effect != -1 &&
                colorList != NULL &&
                str2int(&timeConstant, tokens[4], 10) == STR2INT_SUCCESS) {
    
                if (getMode() == USER_MOVEMENT || getMode() == USER_MOVEMENT_AND_BASE) {
                    setMode(USER_MOVEMENT_AND_BASE);
                } else {
                    setMode(USER_BASE);
                }
                printf("Setting base.\n\r");
                setBaseEffect( dither, effect, colorList, (int) timeConstant*100 );
                writeToBLE("Base set.");
            } else { 
                writeToBLE("Err: Parsing.");
            }
        } else {
                writeToBLE("Err: Token count.");        
        }        
    } else if (!strcmp(tokens[0],"effect") || !strcmp(tokens[0],"e")) {
        if (getTokenCount(tokens) == 8) {  
            int movement = movementToEnum(tokens[1]);          
            int dither = ditherToEnum(tokens[2]);
            int fill = fillToEnum(tokens[3]);
            int effect = effectToEnum(tokens[4]);
            const int* colorList = colorListToPointer(tokens[5]);
            long timeConstant;
            long moveTimeConstant;
            // set a fixed base until mode is reset
            if (movement != -1 &&
                dither != -1 &&
                effect != -1 &&
                colorList != NULL &
                str2int(&timeConstant, tokens[6], 10) == STR2INT_SUCCESS &&
                str2int(&moveTimeConstant, tokens[7], 10) == STR2INT_SUCCESS) {
    
                if (getMode() == USER_BASE || getMode() == USER_MOVEMENT_AND_BASE) {
                    setMode(USER_MOVEMENT_AND_BASE);
                } else {
                    setMode(USER_MOVEMENT);
                }
                setOverlayEffect( movement, dither, fill, effect, colorList, (int) timeConstant*100, (int) moveTimeConstant*100 );
                writeToBLE("Effect set.");
            } else { 
                writeToBLE("Err: Parsing.");
            }
        } else {
                writeToBLE("Err: Token count.");
        }
    } else if (!strcmp(tokens[0],"help") || !strcmp(tokens[0],"h")) {
            int hour = 1 + (TheElapsedTime / (60*60*1000));
            int minute = (TheElapsedTime % (60*60*1000))/(60*1000);
            int second = (TheElapsedTime % (60*1000)) / 1000;
            sprintf(outputLine, "T:%02d:%02d:%02d,M=%d,A=%d", hour, minute, second, getMode(), lastAmplitude);
            writeToBLE((const uint8_t*)outputLine);  
            wait(0.03);
            writeToBLE("BASE:dither,effect");
            wait(0.03);
            writeToBLE(",color,time");  
            wait(0.03);
            writeToBLE("EFFECT:move,dither");
            wait(0.03);
            writeToBLE(",fill,effect,color");  
            wait(0.03);
            writeToBLE(",time,moveTime");  
            wait(0.03);
            writeToBLE("MOVE:v+,v-,h+,h-,r+,");  
            wait(0.03);
            writeToBLE("r-,b+,b-,al,of");  
            wait(0.03);
            writeToBLE("DITHER:Fixed,Cross,");  
            wait(0.03);
            writeToBLE("In,Out,Strob, Puls");
            wait(0.03);
            writeToBLE("FILL:Line,Fill");  
            wait(0.03);
            writeToBLE("EFFECT:Const,Ran");               
            wait(0.03);
            writeToBLE("Seq,Fixed");   
            wait(0.03);
            writeToBLE("COLOR:al,te,el,pa,");
            wait(0.03);
            writeToBLE("pu,bl,gr,re,su,ye,");
            wait(0.03);
            writeToBLE("da,go,sh");                      
    } else {
        writeToBLE("Unknown command.");  
    }        
    
    for (int i=0; tokens[i] != 0;i++) {
        free(tokens[i]);
    }
    
    dataToProcess = FALSE;
}

void buttonHandlerAllRed() {
    setMode(0);
    for (int i=0; i< CHANNELS_PER_IC * NUMBER_OF_ICS; i++) {
        setChannelToRGB( (int) i,  (uint16_t) allRed ? 0xFF : 0,  (uint16_t) 0,  (uint16_t) 0);
    }
    theChip->latchData();
    printf("All red: %d\n\r",allRed);
    allRed = !allRed;
}

void buttonHandlerAllGreen() {
    setMode(0);
    for (int i=0; i< CHANNELS_PER_IC * NUMBER_OF_ICS; i++) {
        setChannelToRGB( (int) i,  (uint16_t) 0, (uint16_t) allGreen ? 0xFF : 0, (uint16_t) 0);
    }
    theChip->latchData();
    printf("All green: %d\n\r",allGreen);
    allGreen = !allGreen;
}

void buttonHandlerAllBlue() {
    setMode(0);
    for (int i=0; i< CHANNELS_PER_IC * NUMBER_OF_ICS; i++) {
        setChannelToRGB( (int) i,  (uint16_t) 0,  (uint16_t) 0, (uint16_t) allBlue ? 0xFF : 0);
    }
    theChip->latchData();
    printf("All blue: %d\n\r",allBlue);
    allBlue = !allBlue;
}
    
void buttonHandlerMode1() {
    printf("Movement enabled.\n\r");
    setMode(1);
}    
    
int main(void)
{
    unsigned short dotCorrect[48];
    Ticker ticker;
    
    button1.rise(&buttonHandlerAllRed);
    button2.rise(&buttonHandlerAllGreen);
    button3.rise(&buttonHandlerAllBlue);
    button4.rise(&buttonHandlerMode1);
    
    // Set dot correct to maximum brightness for all channels
    for (int i=0;i<48;i++) {
        dotCorrect[i] = 0x7F;
    }
    
    // SCLK = P0_1, MOSI = P0_2, GSCLK = P0_3, LAT = P0_4
    theChip = new TLC5955(P0_1, P0_2, P0_3, P0_4);
    
    
        
    wait(0.01);
    
    // Initial settings
    
    theChip->setNewControlData(0x7F, 0x7F, 0x7F,  // global brightness set to max for R, G, and B channels
                                TLC5955::I_3_2_MA, TLC5955::I_3_2_MA, TLC5955::I_3_2_MA, // maximum current set to 3.2mA for R, G, and B channels
                                dotCorrect);
    
    wait(0.01);
    
    theChip->latchData(); // set all channels to zero, which is the default
    
    DEBUG("\n\r\n\rTLC5955 Reset.\n\r");

    SequencerConfig();
        
    ticker.attach(periodicCallback, SEQUENCER_RATE);

    // In case setup takes some amount of time, we're doing it after we turn off the LEDs.
    ble = new BLEDevice();

    DEBUG("Initialising the nRF51822\n\r");
    ble->init();
    ble->onDisconnection(disconnectionCallback);
    ble->onConnection(connectCallback);
    ble->onDataWritten(onDataWritten);

    /* setup advertising */
    ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
    ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble->accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,
                                     (const uint8_t *)"DuckLights", sizeof("DuckLights") - 1);
    ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
                                     (const uint8_t *)UARTServiceUUID_reversed, sizeof(UARTServiceUUID_reversed));

    ble->setAdvertisingInterval(1000); /* 1000ms; in multiples of 0.625ms. */
    ble->startAdvertising();

    UARTService uartService(*ble);
    uartServicePtr = &uartService;

    DEBUG("Entering infinite loop.\n\r");

    while (true) {
        ble->waitForEvent();
        if (dataToProcess) {
            parseInputData();
        }
    }
}