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