BLE data inconsistency with HM-10 and Arduino Nano - c

We have a project that controls a led strip of 80, that is connected via mobile app. The components we used are as follows:
Arduino Nano Board
HM - 10 Bluetooth module
WS2812b Led Strip
On the app, user selects the colors(up to 5), animation (optional), animation speed and brightness. The selected configuration is transmitted to the BLE module with a certain throttle and debouncing. The color(selected on a color wheel on the app) and brightness transmits fine, without any issues.
The problem we encounter, is that when certain animation is active, if the user changes the animation speed via the app, the arduino part locks itself and will not accept any more commands.
To activate a configuration with animation, we send the data from app to arduino as follows:
<l255180200,240135068:089;04200>
The format is: < [mode] [colors(with ',')] : [brightness] ; [animationCode(2 digit)] [animationSpeed] >
At first, we had some inconsistencies with consecutive data, so we implemented the following for data acquisition:
void loop()
{
// Read all serial data available, as fast as possible
while (bleSerial.available() > 0)
{
char inChar = bleSerial.read();
if (inChar == SOP)
{
index = 0;
inData[index] = '\0';
started = true;
ended = false;
}
else if (inChar == EOP)
{
ended = true;
break;
}
else
{
if (index < 79)
{
inData[index] = inChar;
index++;
inData[index] = '\0';
}
}
}
// We are here either because all pending serial
// data has been read OR because an end of
// packet marker arrived. Which is it?
if (started && ended)
{
// The end of packet marker arrived. Process the packet
Serial.println(inData);
Serial.println(inData[0]);
char mode = inData[0];
if (mode == 'p')
{
togglePower(inData);
finalizeRead();
return;
}
if (mode == 'b')
{
changeBrightness(inData);
finalizeRead();
return;
}
if (mode == 't')
{
char *themeNo = strtok(NULL, ";");
int themeCode = valueFromString(themeNo, 0, 2);
theme(themeCode);
}
// if (mode == 'e') {
// sound();
// return;
// }
char *colorsWithBrightness = strtok(inData, ";");
char *animation = strtok(NULL, ";");
char *colors = strtok(colorsWithBrightness, ":");
char *brightness = strtok(NULL, ":");
custom(colors, brightness, animation);
finalizeRead();
}
}
void finalizeRead()
{
// Reset for the next packet
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
int valueFromString(char *string, int start, int width)
{
int value = 0;
for (int n = 0; n < width; n++)
value = value * 10 + string[start + n] - '0';
return value;
}
So the custom function is called when the mode == 'l'. And in custom function, we perform as follows:
void custom(char *colors, char *brightness, char *animation)
{
char *trueColors = strtok(colors, "l");
char *colorsSplit = strtok(trueColors, ",");
int colorCount = 0;
uint32_t colorArray[5] = {};
while (colorsSplit != NULL)
{
int r = valueFromString(colorsSplit, 0, 3);
int g = valueFromString(colorsSplit, 3, 3);
int b = valueFromString(colorsSplit, 6, 3);
colorArray[colorCount] = strip.Color(r, g, b);
colorCount++;
colorsSplit = strtok(NULL, ",");
}
int ledsPerSegment = ledCount / colorCount;
for (int n = 0; n < colorCount; n++)
{
int currentSegment = (n + 1) * ledsPerSegment;
Serial.println(currentSegment);
for (int z = n * ledsPerSegment; z < (n + 1) * ledsPerSegment; z++)
{
strip.setPixelColor(z, colorArray[n]);
}
}
strip.setBrightness(atoi(brightness));
strip.show();
Serial.println("Done");
if (animation)
{
int animationCode = valueFromString(animation, 0, 2);
int animationSpeed = valueFromString(animation, 2, 3);
Serial.println(animationSpeed);
if (animationCode == 1)
breath(animationSpeed, atoi(brightness));
else if (animationCode == 7)
pulse(animationSpeed, colorArray, colorCount);
}
}
The problem occurs in pulse animation, which is like this:
void pulse(int wait, uint32_t colorArray[], int colorCount)
{
black();
while (bleSerial.available() <= 0)
{
for (i = 0; i < colorCount && bleSerial.available() <= 0; i++)
{
for (j = 0; j < ledCount / 2 && bleSerial.available() <= 0; j++)
{
strip.setPixelColor(39 - j, colorArray[i]);
strip.setPixelColor(40 + j, colorArray[i]);
strip.show();
delay(wait);
}
black();
}
}
}
We initialize the animation without any issues using the following:
<l255180200,240135068:089;04200>
As soon as we adjust the animation speed from the app, we send about 2 new configurations per second, which is the same as above, only with different speed (last 3 characters before '>'). The arduino part randomly receives the data incorrectly, losing 2-3 characters, which is like <l2551800,240135068:089;04200>
The loss of characters usually occurs on different parts of the string, but always consecutive characters are misread happen each time. Also, sometimes we get a backwards question mark.
We are not sure where we implement wrong, so any help is appreciated on solving this issue.
Thanks!

Related

Memcpy in C causes STM to freeze after turning power off and on

I have a problem with STM32F401CCU. After executing a code disconnecting and connecting power the STM freezes and to unfreeze it I need to reload the build.
I have a complex set of functions that are used to turn an array of int values into a single very long char array. The char array is then written to the long term Flash memory. In order to execute that I do many operations with char arrays and they appear to be executed properly, as saving to Flash is done correctly - I tested Flash content with ST LINK and they are what they should be. But after turning power off/on to actually test if long term Flash memory works I encounter a fatal bug - upon powering STM back on it freezes and the only way to unfreeze it is to reload the build.
After many hours of tests I figured that the problem is with memcpy usage in StringCreateStrForFlashOneProduct. Presumably I created a sort of memory leak, but I don't get what's wrong.
A piece of example operation that causes STM to freeze:
static char send_save_string_final[2048];
char sssssend_char[2048] = "000000NewProduct";
char *StringCreateStrForFlashOneProduct(const int someint)
{
memcpy(sssssend_char, "000000008888008000000000888800800000000088880080", 48);
memcpy(send_save_string_final, sssssend_char, 48);
return send_save_string_final;
}
The code I actually use:
FUNCTION ONE - Creates a single array of chars and then saves it to flash memory
static char output_all_sorts[6001] = "";
char all_save_string[6001] = "";
char sssssend_char_all_sorts[2048] = "00000AllProducts";
char append_char_numz_all_sorts[17] = "0000000000000000";
void FlashSaveAllSorts(void)
{
strcpy(sssssend_char_all_sorts, "00000AllProducts");
sssssend_char_all_sorts[2047] = 0;
strcpy(output_all_sorts, "0");
output_all_sorts[6000] = 0;
strcpy(all_save_string, "00000AllProducts");
all_save_string[6000] = 0;
strcpy(append_char_numz_all_sorts, "0000000000000000");
append_char_numz_all_sorts[16] = 0;
for (int doin_int = 0; doin_int <= 6; doin_int++)
{
strcpy(sssssend_char_all_sorts, StringCreateStrForFlashOneProduct(doin_int));
strcat(all_save_string, sssssend_char_all_sorts);
strcpy(output_all_sorts, all_save_string);
}
strcpy(output_all_sorts, all_save_string);
output_all_sorts[strlen(output_all_sorts)] = 0;
Flash_Write_Data(0x08020000, output_all_sorts, 3000);
}
FUNCTION TWO - Creates a single line with all data for one product
static char send_save_string_final[2048] = "0";
char sssssend_char[2048] = "000000NewProduct";
char append_char_numz[17] = "0000000000000000";
char *StringCreateStrForFlashOneProduct(const int someint)
{
strcpy(send_save_string_final, "0");
send_save_string_final[2047] = 0;
strcpy(sssssend_char, "000000NewProduct");
sssssend_char[2047] = 0;
strcpy(append_char_numz, "0000000000000000");
append_char_numz[16] = 0;
strncpy(append_char_numz, StringCreateStringFromIntTwo(someint), 17);
append_char_numz[16] = 0;
strcat(sssssend_char, append_char_numz);
for (int kk = 0; kk < 3; kk = kk + 1)
{
char append_char_one[17] = "0000000000000000";
for (int jj = 0; jj < 12; jj = jj + 1)
{
char append_char[17] = "0000000000000000";
memcpy(append_char, StringCreateStringFromIntTwo(tunable_vars_machine_for_flash[someint][kk][jj]), 17);
strcat(sssssend_char, append_char);
}
memcpy(append_char_one, "000000000MenuEnd", 17);
strcat(sssssend_char, append_char_one);
}
char append_char_end_zeros[17] = "0000000000000000";
memcpy(append_char_end_zeros, StringCreateStringFromIntTwo(0), 17);
strcat(sssssend_char, append_char_numz);
memcpy(send_save_string_final, sssssend_char, 2047);
return send_save_string_final;
}
FUNCTION THREE - Creates a 16-chars array with a following format 000000000000001333, zeros are required to have consistent length of saved string, 1333 is example data value:
char *StringCreateStringFromIntTwo(int base_int_base)
{
//clearprint("StringCreateStri");
int base_int = base_int_base;
if (base_int == 0)
{
base_int = 999999; /// avoid saving zero to flash memory
}
static char send_char_final[17] = "0000000000000000";
char send_char[16] = "00000000";
static char send_char_sixteen_zeros[17] = "0000000000000000";
int legnewpigwphjh = strlen(send_char);
char str_zero[2] = "0";
char str_two_zeros[3] = "00";
char str_three_zeros[4] = "000";
char str_four_zeros[5] = "0000";
char str_five_zeros[6] = "00000";
char str_six_zeros[7] = "000000";
char str_seven_zeros[8] = "0000000";
char str_eight_zeros[9] = "00000000";
char str_sixteen_zeros[17] = "0000000000000000";
int int_mem_length = countDigits(base_int);
char str_mem_write_int[9];
sprintf(str_mem_write_int, "%d", base_int);
if (int_mem_length == 7)
{
strcat(send_char, str_zero);
}
if (int_mem_length == 6)
{
strcat(send_char, str_two_zeros);
}
if (int_mem_length == 5)
{
strcat(send_char, str_three_zeros);
}
if (int_mem_length == 4)
{
strcat(send_char, str_four_zeros);
}
if (int_mem_length == 3)
{
strcat(send_char, str_five_zeros);
}
if (int_mem_length == 2)
{
strcat(send_char, str_six_zeros);
}
if (int_mem_length == 1)
{
strcat(send_char, str_seven_zeros);
}
strcat(send_char, str_mem_write_int);
strcpy(send_char_final, str_sixteen_zeros);
strcpy(send_char_final, send_char);
return send_char_final;
}
Your code is way too complicated. There are too many string copies and concatenations, and countless useless operations. You do not seem to master pointers, nor basic memory layout concepts. Above all, given your skill level, you should absolutely avoid strncpy and memcpy.
Here is a simpler approach with functions that construct the appropriate string format at the end of the buffer and return the number of characters written. With this method, composing a long string is much simpler and much less error prone:
// function to construct a fixed length string of digits with leading 0s
// buffer must point to an array at least 17 bytes long
// returns the number of characters, ie: 16
int FormatNumber16(char *buffer, int someint) {
if (someint == 0) {
// This test seems bogus: the product number 0 will be written as 999999
// and so will the MenuEnd number
someint = 999999; // avoid writing 0 ???
}
return snprintf(buffer, 17, "%016d", someint);
}
// function to construct the product string to store to flash
// buffer must point to an array at least 641 bytes long
// returns the number of characters, ie: 640
int FormatOneProduct(char *buffer, int product_nb) {
int pos = 0;
pos += snprintf(buffer + pos, 17, "%016s", "NewProduct");
pos += FormatNumber16(buffer + pos, product_nb);
for (int kk = 0; kk < 3; kk++) {
for (int jj = 0; jj < 12; jj++) {
pos += FormatNumber16(buffer + pos,
tunable_vars_machine_for_flash[product_nb][kk][jj]);
}
}
pos += snprintf(buffer + pos, 17, "%016s", "MenuEnd");
// vvv this will actually append 0000000000999999 ?
pos += FormatNumber16(buffer + pos, 0);
// pos should be 640 = 16 + 16 + 12*3*16 + 16 + 16
return pos;
}
// function to write all product data to flash (7 products)
void FlashSaveAllSorts(void) {
char buffer[6001] = ""; // no need for global data
int pos = 0;
pos += snprintf(buffer + pos, 17, "%016s", "AllProducts");
// append the description of all 7 products (0 to 6 included)
for (int product_nb = 0; product_nb <= 6; product_nb++) {
pos += FormatOneProduct(buffer + pos, product_nb);
}
// buffer contains 4496 = 16 + 640*7 characters, why write 3000 bytes?
Flash_Write_Data(0x08020000, buffer, 3000);
}
Some example functions:
char *StringCreateStrForFlashOneProduct(char *buff, size_t len, int someint)
{
int mask = 1000000000;
char *wrk = buff;
if(!someint) someint = 999999;
memset(buff, '0', len - 10);
wrk += len - 10;
while(mask)
{
*wrk++ = '0' + abs(someint / mask);
someint %= mask;
mask /= 10;
}
*wrk = 0;
return buff;
}
example usage:
int main(void)
{
char str[17];
printf("`%s`\n", StringCreateStrForFlashOneProduct(str, 16, 1234));
}

Format any number in Indian currency style using C language

Indian Currency format is 12345 → "12,345" (for odd length) and 123456 → "1,23,456" (for even length). I have included all possibilities such as
1. Minus sign: "-12,345".
2. Decimal Point: "-12,345.345" or "12,345.123".
3. Zero Condition 000000.123 → "0.123".
4.Minus and Zero Condition '-000000.123' -> "-0.123"
int currencyFormatter(char av_currency[], int av_strLen, char *ap_formattedNumber)
{
char flag = 'N'; //Taking a Flag to know whether thier is a decimal Point in Currency or not
int lengthOf = 0, index = 0, i = 0, j = 0;
char *decAr = NULL;
char *tmpCurrency = NULL;//Taking two Pointers one for Array with Commas(tmpCurrency) and decAr pointer for decimal Point array
char *s = NULL;
s = strstr(av_currency, ".");//Checking for decimal Point in array
if (s > 0)
{
flag = 'D'; // Changing Flag to show Decimal Point is Present in Array
s = strchr(av_currency, '.');
index = s - av_currency; //Index at which Decimal Point is present
av_strLen = strlen(av_currency) - index; // calculated formula to know length of an array needed to contain decimal point and Numbers after that
decAr = (char*)malloc(av_strLen*sizeof(char*));//allocated Memory using malloc
decAr[av_strLen] = '\0';
memmove(decAr, &av_currency[index], av_strLen); //memmove from decimal till end of array.
av_currency[index] = '\0';
if (!decAr)//Handled Null Condition for Pointer
{
return -1;//All errors for Negative Number
}
}
lengthOf = strlen(av_currency) + (strlen(av_currency) / 2); // Derived Formula(It Works for Indian Currency Format) to know the length of an array is needed to contain numbers and Commas Together.
tmpCurrency = (char*)malloc(lengthOf*sizeof(char*));
strrev(av_currency); //Reversed Array as commas comes at multiple of 3. eg=12345 reverse=54321 wdComma=543,21 index is 3 if number would had been bigger commas would had come at 3,6.
while (av_currency[i] != '\0')
{
if (j % 3 == 0 && j >= 3 && av_currency[i] != '-')//all Commas come at multiple of 3 when you reverse an amount
{
tmpCurrency[j] = ',';//If an , is found Increment only J as
is used as index number to store in tmpcurrency
j++;
continue;
}
tmpCurrency[j] = av_currency[i];//storing the Value in tmpCurrency
i++;//Incrementing
j++;//Incrementing
}
tmpCurrency[j] = '\0';//Null Condition
if (!tmpCurrency) // Checking for NULL Pointer
{
return -2; //all errors for Negative value
}
flag == 'D' ? strcpy(av_currency, (strcat(strrev(tmpCurrency), decAr))) : strcpy(av_currency, (strrev(tmpCurrency)));//Ternary Operator
strcpy(ap_formattedNumber,av_currency);//Copying formated number into original array
free(tmpCurrency);//Releasing the memory
free(decAr);//Releasing the Memory
return 0;
}
I have solution for above question.
Please try this code.
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
char *printComma(double input_number,char *demo,char ap_it[],char ap_type[])
{
char *result = NULL;
char *lp_decimal_number = NULL;
char *main_number = NULL;
char *decimal_pos = NULL;
char zero[1] = {0};
int i = 0;
int j = 0;
int z = 0;
int cnt = 0;
int decimal_index = 0;
int lp_decimal_numberLen = 0 ;
int flag_dec = 0;
int flag_minus = 0 ;
int length_main;
int k = 0;
int length_demo=0;
sprintf(demo,"%lf",input_number);
if(strcmp(ap_type,"P") == 0)
{
if(strcmp(ap_it,"A") == 0 || strcmp(ap_it,"B") == 0)
{
sprintf_s(demo,40,"%0.4lf",input_number);
}
else
{
sprintf_s(demo,40,"%0.2lf",input_number);
}
}
else
{
sprintf_s(demo,40,"%.0lf",input_number);
}
length_demo = strlen(demo); // finds the length of original string
result = (char *)malloc((length_demo+10)*sizeof(char));
main_number = (char *)malloc((length_demo+50)*sizeof(char));
z = strspn(demo[0] == '-' ? (demo + 1) : demo , "0");
if(z != 0)
{
if(demo[0] != '-')
{
memcpy(main_number,&demo[z],length_demo);
main_number[length_demo]='\0';
}
else
{
puts(main_number);
main_number[length_demo]='\0';
flag_minus=1;
}
}
else
{
memcpy(main_number,&demo[0],length_demo);
main_number[length_demo]='\0';
}
length_main=strlen(main_number);
decimal_pos = strstr(main_number,".");
if(decimal_pos > 0)
{
decimal_index = decimal_pos - main_number ; // Getting postion of decimal
lp_decimal_numberLen = length_main - decimal_index; // Calculating the endpoint for decimal number
if(length_main > 3) //Copying the decimal part to a separate array
{
lp_decimal_number = (char *) malloc(lp_decimal_numberLen+1);
memcpy( lp_decimal_number, &main_number[decimal_index], lp_decimal_numberLen );
lp_decimal_number[lp_decimal_numberLen] = '\0';
flag_dec=1;
main_number[decimal_index]='\0';
}
}
//logic for comma starts here
strrev(main_number);
i = 0;
while(main_number[i] != '\0')
{
if (j%3 == 0 && j>=3 && main_number[i]!='-' && main_number[i]!='$')
{
result[j] = ',';
cnt++;
j++;
continue;
}
else if(cnt==1 || cnt==2)
{
result[j] = main_number[i];
}
else
{
result[j] = main_number[i];
}
i++;
j++;
}
result[j] = '\0';
if(flag_dec==0)
{
if(flag_minus==0)
return(strrev(result));
else
{
strcat(result,"-");
return(strrev(result));
}
}
else
{
if(flag_minus==0)
return(strcat(strrev(result),lp_decimal_number));
else
{
strcat(result,"-");
return(strcat(strrev(result),lp_decimal_number));
}
}
}
int main()
{
double number;
char num[25] = {0};
char it_type[] = "A";
char ap_type[] = "P";
char *formattedNumber = NULL;
printf("\n Enter the number n: ");
scanf("%lf",&number);
formattedNumber=printComma(number,num,it_type,ap_type);
printf("\n Final Result = %s ",formattedNumber);
getch();
return 0;
}
Do only positive values with your function!
Check if the value is negative before calling the function; call the function with the positive value; if it was negative to start with, add the minus sign afterwards.
int needssign = 0;
if (val < 0) needssign = 1;
indianformat(res, abs(val));
if (needssign) sprintf(res, "-%s", res);
Or make your current function a helper function and use the code above for the new improved function for formatting in Indian format.

Why the code block is not Autovectorized by VC2013

The code block is from mozilla firefox's qcms transform_util.c
void build_output_lut(struct curveType *trc,
uint16_t **output_gamma_lut, size_t *output_gamma_lut_length)
{
if (trc->type == PARAMETRIC_CURVE_TYPE) {
float gamma_table[256];
uint16_t i;
uint16_t *output = malloc(sizeof(uint16_t)*256);
if (!output) {
*output_gamma_lut = NULL;
return;
}
compute_curve_gamma_table_type_parametric(gamma_table, trc->parameter, trc->count);
*output_gamma_lut_length = 256;
for(i = 0; i < 256; i++) {
output[i] = (uint16_t)(gamma_table[i] * 65535);
}
*output_gamma_lut = output;
} else {
if (trc->count == 0) {
*output_gamma_lut = build_linear_table(4096);
*output_gamma_lut_length = 4096;
} else if (trc->count == 1) {
float gamma = 1./u8Fixed8Number_to_float(trc->data[0]);
*output_gamma_lut = build_pow_table(gamma, 4096);
*output_gamma_lut_length = 4096;
} else {
//XXX: the choice of a minimum of 256 here is not backed by any theory,
// measurement or data, however it is what lcms uses.
*output_gamma_lut_length = trc->count;
if (*output_gamma_lut_length < 256)
*output_gamma_lut_length = 256;
*output_gamma_lut = invert_lut(trc->data, trc->count, *output_gamma_lut_length);
}
}
}
For this loop:
for(i = 0; i < 256; i++) {
output[i] = (uint16_t)(gamma_table[i] * 65535);
}
VC2013 will show:
e:\mozilla\hg\nightly\mozilla-central\gfx\qcms\transform_util.c(490) : info C5002: 由于“500”,循环未向量化
MSDN (http://msdn.microsoft.com/en-us/library/jj658585.aspx) shows:
// Code 500 is emitted if the loop has non-vectorizable flow.
// This can include "if", "break", "continue", the conditional
// operator "?", or function calls.
// It also encompasses correct definition and use of the induction
// variable "i", in that the increment "++i" or "i++" must be the last
// statement in the loop.
But the loop above has no if/break/continue, I don't know why it can't be vectorized.
I suspect that this is due to the fact that variable "i" lies in scope of "if"-statement. Thus it does not belong entirely to "for-loop" scope like it would have been in
for(int i = 0; /* etc. */
At such, the compiler's logic would have been like: "I need not only to make gammatable filles with needed values, but to assign same value to "i" as at loop end". Therefore, no vectorization.

Split String into String array

I have been playing around with programming for arduino but today i've come across a problem that i can't solve with my very limited C knowledge.
Here's how it goes.
I'm creating a pc application that sends serial input to the arduino (deviceID, command, commandparameters). This arduino will transmit that command over RF to other arduino's. depending on the deviceID the correct arduino will perform the command.
To be able to determine the deviceID i want to split that string on the ",".
this is my problem, i know how to do this easily in java (even by not using the standard split function), however in C it's a totally different story.
Can any of you guys tell me how to get this working?
thanks
/*
Serial Event example
When new serial data arrives, this sketch adds it to a String.
When a newline is received, the loop prints the string and
clears it.
A good test for this is to try it with a GPS receiver
that sends out NMEA 0183 sentences.
Created 9 May 2011
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/SerialEvent
*/
String inputString; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
String[] receivedData;
void setup() {
// initialize serial:
Serial.begin(9600);
// reserve 200 bytes for the inputString:
inputString.reserve(200);
}
void loop() {
// print the string when a newline arrives:
if (stringComplete) {
Serial.println(inputString);
// clear the string:
inputString = "";
stringComplete = false;
}
}
/*
SerialEvent occurs whenever a new data comes in the
hardware serial RX. This routine is run between each
time loop() runs, so using delay inside loop can delay
response. Multiple bytes of data may be available.
*/
void serialEvent() {
while (Serial.available()) {
// get the new byte:
char inChar = (char)Serial.read();
if (inChar == '\n') {
stringComplete = true;
}
// add it to the inputString:
if(stringComplete == false) {
inputString += inChar;
}
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
}
}
String[] splitCommand(String text, char splitChar) {
int splitCount = countSplitCharacters(text, splitChar);
String returnValue[splitCount];
int index = -1;
int index2;
for(int i = 0; i < splitCount - 1; i++) {
index = text.indexOf(splitChar, index + 1);
index2 = text.indexOf(splitChar, index + 1);
if(index2 < 0) index2 = text.length() - 1;
returnValue[i] = text.substring(index, index2);
}
return returnValue;
}
int countSplitCharacters(String text, char splitChar) {
int returnValue = 0;
int index = -1;
while (index > -1) {
index = text.indexOf(splitChar, index + 1);
if(index > -1) returnValue+=1;
}
return returnValue;
}
I have decided I'm going to use the strtok function.
I'm running into another problem now. The error happened is
SerialEvent.cpp: In function 'void splitCommand(String, char)':
SerialEvent:68: error: cannot convert 'String' to 'char*' for argument '1' to 'char* strtok(char*, const char*)'
SerialEvent:68: error: 'null' was not declared in this scope
Code is like,
String inputString; // a string to hold incoming data
void splitCommand(String text, char splitChar) {
String temp;
int index = -1;
int index2;
for(temp = strtok(text, splitChar); temp; temp = strtok(null, splitChar)) {
Serial.println(temp);
}
for(int i = 0; i < 3; i++) {
Serial.println(command[i]);
}
}
This is an old question, but i have created some piece of code that may help:
String getValue(String data, char separator, int index)
{
int found = 0;
int strIndex[] = {0, -1};
int maxIndex = data.length()-1;
for(int i=0; i<=maxIndex && found<=index; i++){
if(data.charAt(i)==separator || i==maxIndex){
found++;
strIndex[0] = strIndex[1]+1;
strIndex[1] = (i == maxIndex) ? i+1 : i;
}
}
return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
}
This function returns a single string separated by a predefined character at a given index. For example:
String split = "hi this is a split test";
String word3 = getValue(split, ' ', 2);
Serial.println(word3);
Should print 'is'. You also can try with index 0 returning 'hi' or safely trying index 5 returning 'test'.
Hope this help!
Implementation:
int sa[4], r=0, t=0;
String oneLine = "123;456;789;999;";
for (int i=0; i < oneLine.length(); i++)
{
if(oneLine.charAt(i) == ';')
{
sa[t] = oneLine.substring(r, i).toInt();
r=(i+1);
t++;
}
}
Result:
// sa[0] = 123
// sa[1] = 456
// sa[2] = 789
// sa[3] = 999
For dynamic allocation of memory, you will need to use malloc, ie:
String returnvalue[splitcount];
for(int i=0; i< splitcount; i++)
{
String returnvalue[i] = malloc(maxsizeofstring * sizeof(char));
}
You will also need the maximum string length.
The C way to split a string based on a delimiter is to use strtok (or strtok_r).
See also this question.
I think your idea is a good start point. Here is a code that i use (to parse HTTP GET REST requests with an Ethernet shield).
The idea is to use a while loop and lastIndexOf of and store the strings into an array (but your could do something else).
"request" is the string you want to parse (for me it was called request because.. it was).
int goOn = 1;
int count = -1;
int pos1;
int pos2 = request.length();
while( goOn == 1 ) {
pos1 = request.lastIndexOf("/", pos2);
pos2 = request.lastIndexOf("/", pos1 - 1);
if( pos2 <= 0 ) goOn = 0;
String tmp = request.substring(pos2 + 1, pos1);
count++;
params[count] = tmp;
// Serial.println( params[count] );
if( goOn != 1) break;
}
// At the end you can know how many items the array will have: count + 1 !
I have used this code successfully, but i thing their is an encoding problem when i try to print params[x]... i'm alos a beginner so i don't master chars vs string...
Hope it helps.
I believe this is the most straight forward and quickest way:
String strings[10]; // Max amount of strings anticipated
void setup() {
Serial.begin(9600);
int count = split("L,-1,0,1023,0", ',');
for (int j = 0; j < count; ++j)
{
if (strings[j].length() > 0)
Serial.println(strings[j]);
}
}
void loop() {
delay(1000);
}
// string: string to parse
// c: delimiter
// returns number of items parsed
int split(String string, char c)
{
String data = "";
int bufferIndex = 0;
for (int i = 0; i < string.length(); ++i)
{
char c = string[i];
if (c != ',')
{
data += c;
}
else
{
data += '\0';
strings[bufferIndex++] = data;
data = "";
}
}
return bufferIndex;
}

Simple problem moving enemy in game (C / SDL)

I'm hacking away at a simple game to teach myself C and I've come up against an infuriatingly simple problem that I haven't been able to Google an answer to.
Code follows, apologies for its noobie terribleness (criticisms appreciated!):
#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>
#define AMOUNT_OF_ENEMIES 10
#define AMOUNT_OF_PIXELS_TO_MOVE 50.0
struct enemy
{
int alive;
SDL_Rect rect;
};
void create_enemy(struct enemy *position)
{
// Take a pointer to an array. Iterate through array looking for any 'dead' instances.
// (Re)initialise when found, ignore entirely if array is full of alive instances.
int j = 0;
while(position[j].alive == 1 && j < AMOUNT_OF_ENEMIES)
{
++j;
}
if(position[j].alive == 0)
{
position[j].alive = 1;
position[j].rect.y = 0;
}
}
void update_enemies(struct enemy *position)
{
// Iterate through a passed array looking for alive instances. If found increment vertical position,
// unless instance is at bottom of screen in which case it's marked as dead.
int j = 0;
while(j < AMOUNT_OF_ENEMIES)
{
if(position[j].alive == 1)
{
position[j].rect.y += 1;
if(position[j].rect.y > 570)
{
position[j].alive = 0;
}
}
++j;
}
}
int main(void)
{
// INITS *********************************************************************
int k;
int current_time = 0;
int previous_time = 0;
float difference_in_time = 0.0;
// Load SDL library
if(SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
printf("Problem, yo\n");
return 1;
}
// Setup event queue
SDL_Event event;
// Create array to store enemys, initialise it
struct enemy *enemy_array = malloc(sizeof(struct enemy) * AMOUNT_OF_ENEMIES);
int j;
for(j = 0; j < AMOUNT_OF_ENEMIES; ++j)
{
enemy_array[j].alive = 0;
enemy_array[j].rect.x = 150;
enemy_array[j].rect.y = 0;
}
// Create an array to flag keypresses, initialise it
int pressed_keys[323];
int l;
for(l = 0; l < 323; ++l)
{
pressed_keys[l] = 0;
}
// Create surfaces
SDL_Surface *screen = SDL_SetVideoMode(300, 600, 0, SDL_HWSURFACE);
int black = SDL_MapRGB(screen->format, 0, 0, 0);
SDL_Surface *tower = SDL_LoadBMP("tower.bmp");
SDL_Rect tower_rect;
tower_rect.x = 50;
tower_rect.y = 0;
tower_rect.w = 200;
tower_rect.h = 600;
SDL_Surface *dude = SDL_LoadBMP("dude.bmp");
float dude_x = 0.0;
SDL_Rect dude_rect;
dude_rect.x = 120;
dude_rect.y = 500;
dude_rect.w = 60;
dude_rect.h = 100;
SDL_Surface *enemy = SDL_LoadBMP("enemy.bmp");
// GAME LOOP *****************************************************************
while(1)
{
current_time = SDL_GetTicks();
difference_in_time = (float)(current_time - previous_time) / 1000;
previous_time = current_time;
if(SDL_PollEvent(&event))
{
if(event.key.keysym.sym == SDLK_DOWN)
{
create_enemy(enemy_array);
}
else
{
switch(event.type)
{
case SDL_QUIT:
printf("NOOOOOO\n");
SDL_FreeSurface(screen);
SDL_FreeSurface(tower);
SDL_FreeSurface(enemy);
free(enemy_array);
SDL_Quit();
return 0;
case SDL_KEYDOWN:
pressed_keys[event.key.keysym.sym] = 1;
break;
case SDL_KEYUP:
pressed_keys[event.key.keysym.sym] = 0;
break;
}
}
}
if(pressed_keys[SDLK_LEFT] && dude_rect.x > 50)
{
dude_rect.x -= (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
if(pressed_keys[SDLK_RIGHT] && dude_rect.x < 190)
{
dude_rect.x += (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
update_enemies(enemy_array);
SDL_FillRect(screen, NULL, black);
SDL_BlitSurface(tower, NULL, screen, &tower_rect);
for(k = 0; k < AMOUNT_OF_ENEMIES; ++k)
{
if(enemy_array[k].alive == 1)
{
SDL_BlitSurface(enemy, NULL, screen, &enemy_array[k].rect);
}
}
SDL_BlitSurface(dude, NULL, screen, &dude_rect);
SDL_Flip(screen);
}
return 0;
}
The issue arises at this part:
if(pressed_keys[SDLK_LEFT] && dude_rect.x > 50)
{
dude_rect.x -= (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
if(pressed_keys[SDLK_RIGHT] && dude_rect.x < 190)
{
dude_rect.x += (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
The 'dude' object moves to the left correctly, but nothing happens when the right arrow key is pressed.
Adding a printf tells me the if statement is being executed correctly. Removing difference_in_time makes it work, so it's either something to do with that variable or the operation of it and AMOUNT_OF_PIXELS_TO_MOVE.
I just can't for the life of me figure out why the former block executes correctly and the latter (which is essentially the same thing) doesn't. I'm sure it's something simple I've overlooked but I'm going insane trying to find it.
Your problem is due to rounding.
For your "dude" you are using a SDL_Rect, that uses integer coordinates (short int if I remember correct).
You configured your dude speed to 50 and if your game is running at 60fps (probably due to its simplicity and it may be much more if vsync is off) you will get each frame a movement value of 0.83333.
This value will be truncated to a int and the result will be zero, for example, if dude.x is 10 and you press right, the calculated value will be 10.83 and when truncated this will result in 10.
For left, it works because the value is rounded down, assuming again dude.x is 10, when left is pressed, on the first iteration the calculated value would be 9.17, truncating this will give you 9.
Simple, bad and Hack Solution
Increase AMOUNT_OF_PIXELS_TO_MOVE to a higher value that forces the int to increase, this will fix the problem.
Good Solution
Does not use SDL_Rect for storing your characters position, create a "MyRect" and use float values in it and only does rounding when drawing the character. Actually you only need to store the character position, so I would create a Point2D struct with only x and y and use this to keep track of characters position.

Resources