Published

Dependencies:   BLE_API TLC5955 mbed nRF51822

Fork of BLE_LoopbackUART by Bluetooth Low Energy

Revision:
14:73923b07ae4a
Parent:
13:15764cc1f12c
--- a/main.cpp	Tue Sep 29 12:02:15 2015 +0000
+++ b/main.cpp	Sat Jun 09 23:23:06 2018 +0000
@@ -16,10 +16,17 @@
 
 #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"
 
-#define NEED_CONSOLE_OUTPUT 0 /* Set this if you need debug messages on the console;
+#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
@@ -28,58 +35,513 @@
 #define DEBUG(...) /* nothing */
 #endif /* #if NEED_CONSOLE_OUTPUT */
 
-BLEDevice  ble;
-DigitalOut led1(LED1);
+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("Disconnected!\n\r");
-    DEBUG("Restarting the advertising process\n\r");
-    ble.startAdvertising();
+    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);
-        ble.updateCharacteristicValue(uartServicePtr->getRXCharacteristicHandle(), params->data, bytesRead);
+       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)
 {
-    led1 = !led1;
+    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)
 {
-    led1 = 1;
+    unsigned short dotCorrect[48];
     Ticker ticker;
-    ticker.attach(periodicCallback, 1);
+    
+    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.onDataWritten(onDataWritten);
+    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 *)"BLE UART", sizeof("BLE UART") - 1);
-    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
+    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();
+    ble->setAdvertisingInterval(1000); /* 1000ms; in multiples of 0.625ms. */
+    ble->startAdvertising();
 
-    UARTService uartService(ble);
+    UARTService uartService(*ble);
     uartServicePtr = &uartService;
 
+    DEBUG("Entering infinite loop.\n\r");
+
     while (true) {
-        ble.waitForEvent();
+        ble->waitForEvent();
+        if (dataToProcess) {
+            parseInputData();
+        }
     }
 }