Published
Dependencies: BLE_API TLC5955 mbed nRF51822
Fork of BLE_LoopbackUART by
Diff: main.cpp
- 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(); + } } }