Solved: Simplest way of putting multiple variables into ASCII array

Go To Last Post
17 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

I am working on a project, where I have an Attiny4313 reading a few sensors, and need to send the values of the sensors read in a string packet over UART.

All variables are of int16_t type and should be compiled into a string that looks something like this:

<213,4538,4023,2853>  

The string should begin with "<" and end with ">", and the values must be separated by ",".

The length of the string isn't important, however there should't be any empty spaces inside of the "<>" brackets.

 

The problem I'm having is how to move the values after I convert them to strings into a single one, that looks like the one above.

 

I tried doing this with one variable using "itoa" adnd "memcpy", but I got some weird results on the output:

 

b717`d``````````````````��          --   717 is the correct value I converted, however all other characters are wrong.

 

The code I used in this can bee seen here:

 

char data_packet[24];
int16_t temperatura;

int main(void)
{
	packet_assembler(temperatura);
	uart_print_string(data_packet,1);

}

void packet_assembler(int16_t temp)
{

	char temp_array[4];
	memset(data_packet, " ", 4);

	memset(data_packet, " ", 24);
	data_packet[0]="<";

	itoa(temp,temp_array,10);
	memcpy(data_packet+1, temp_array, 3);

	data_packet[5]=">";

}

void uart_print_string(char array[], char newline) {

	for(char a=0; a<strlen(array); a++){

		loop_until_bit_is_set(UCSRA, UDRE);
		UDR = array[a];
	}
	if(newline != 0)
	{
		loop_until_bit_is_set(UCSRA, UDRE);
		UDR = '\n';
		loop_until_bit_is_set(UCSRA, UDRE);
		UDR = '\r';
	}
}

 

I hope someone can help me with this, since I'm running out of ideas, patience and time with this project.

Also, please note that I'm working in Atmel studio 7 with an Attiny4313, so there isn't a lot of memory I could afford to use "printf" or anything fancy like this.

 

This topic has a solution.
Last Edited: Tue. Apr 17, 2018 - 05:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jug5525 wrote:
I got some weird results on the output

but is that a problem with your conversion, or with your serial transmission - see Tip #1 ...

 

 

EDIT

 

Sorry - I mean Tip #2

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I think that you want to use strcat to add the comma after each value. itoa() will append the value to the string buffer if you give it the location to put it. I do something like

 

MyLen = strlen(csvstrptr);		//new length
itoa(adc_bval,csvstrptr+MyLen,10);	//the battery value
strcat(csvstrptr,",");			//field separator
MyLen = strlen(csvstrptr);		//new length
itoa(adc_rval,csvstrptr+MyLen,10);	//the reference value
strcat(csvstrptr,"\n");			//add final CR

where csvstrptr points to the string buffer. You can continue this pattern for as many variables as you need.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

C strings end with a null char, you need to be sure your strings ends with null.

Start by using memset() to write nulls to all locations, then fill in your string data into the array.

 

Jim

 

Click Link: Get Free Stock: Retire early!

share.robinhood.com/jamesc3274

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for your suggestions. I will try them out as soon as I can.

 

Also, I checked again to see if there is any problem with serial transmission, and there isn't, as I can transmit a made up string just fine.

 

Thank you again.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There’s also sprintf(data_packet,”<%03d,%03d>”,temp,temp1);

If you’re sending the data to a PC, you might want a line ending on it like \n

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You have a fairly large number of errors here in that sample code.You're calling memset on data_packet twice, I think you wanted the first one to be temp_array, but also temp_array isn't big enough; you should have at least 7 bytes, and you don't have any reason to set the contents at all; itoa() will already be providing a null terminator. (But the largest integer values are 6 digits, and you need space for a terminating nul byte.) Your memcpy() unconditionally specifies "3" as its size, which is also wrong. Then you set data_packet[5] to '>', but that's going to be at least one space after the last digit you copied in. And you never null-terminate the string.

 

You should probably just use sprintf(), which will be simpler to use.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
There’s also sprintf

the_real_seebs wrote:
You should probably just use sprintf()

+1

 

jug5525 wrote:
 I'm working in Atmel studio 7 with an Attiny4313, so there isn't a lot of memory I could afford to use "printf" or anything fancy like this.

So have you looked at how much sprintf actually "costs" ... ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Does the entire string have to be stored at once?

send <
while true:
    send integer
    if done : break
    send ,
send >

 

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The second parameter to memset() should be an int. Not a string.

You can use a character constant as a placeholder for an int or character, but then use single quotes.

memset(data_packet, ' ', 4);

It is also not needed to use memset at all. You can simply write the

jug5525 wrote:
b717`d``````````````````�� -- 717 is the correct value I converted, however all other characters are wrong.

Are all these "wrong" characters coming from this 4 byte array:

char temp_array[4];

itoa() is (and C in general) are a bit dumb. Itoa() can produce a much longer string, and if you only reserve 4 bytes you WILL overwrite other memory.

 

When developing and debugging parts like this of a program it is often more convenient to do it on a PC than on a uC.

I do not like "big" functions such as printf much for uC use. Normally I write simple functions which are just able to format a single number.

Then I combine a bunch of those functions to build a more complex string in a buffer.

The program below is a small PC program as a demonstration of this.

#include <stdint.h>
#include <stdio.h>

int main( void) {
	char buffer[40];
	char *ptr;
	
	ptr = buffer;
	
	*ptr++ = '<';
	ptr += sprintf( ptr, "%d", 42);
	*ptr++ = ',';
	ptr += sprintf( ptr, "%d", 412345);
	*ptr++ = ',';
	ptr += sprintf( ptr, "%d", 987);
	*ptr++ = ',';
	ptr += sprintf( ptr, "%d", 4444);
	*ptr++ = '>';
	*ptr++ = 0;		// Add terminating nul.
	
	printf( "%s", buffer);	// Print the build string to output.
	printf( "\n C'est Fini.\n");
}

The compilation and output of this program (on a linux box) is as follows:

paul@dualcore:~$ gcc asdf.c
paul@dualcore:~$ chmod +x a.out
paul@dualcore:~$ ./a.out
<42,412345,987,4444>
 C'est Fini.

gcc        # Compile the program adsf.c. The default output of gcc is always the file "a.out".

chmod   # +x = make the program "a.out" an executable.

./a.out    # Exectute the program a.out in the current directory. (Linux does not normally execute programs from the working directory as a malware protection measure).

 

If you run windows you probably want to compile with something like:

gcc asdf.c -o asdf.exe

Then you can probably just run it, as all files which end with "exe" are executable and windows also executes programs from the working directory by default.

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve's method should work just fine. It does have a "problem" in that it violates the initial request for stringification.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Apr 16, 2018 - 09:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve's method should work just fine.

 

Jim

And probably faster too, since no overhead of string manipulations functions, which can be somewhat inefficient

  ...just send what you want to spit out to a serial TX ring buffer, be it 1 character, a 3 digit asciii number, etc. 

When in the dark remember-the future looks brighter than ever.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One more example (inspired by ka7ehk #3)

char temp[6];
char string_to_send[26];
uint16_t num[5] ={1000,200,30,40,50};

    string_to_send[0] = '<';
    
    for (int i=0;i<5;i++)
    {
        itoa(num[i], temp,10);
        strcat (string_to_send, temp);
        strcat (string_to_send, ",");
    }
    
    string_to_send[strlen(string_to_send)-1] = '>'; // rewrite last ',' with '>'
    uart_putstring(string_to_send);
 
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
skeeve's method should work just fine. It does have a "problem" in that it violates the initial request for stringification.
To be pedantic, it doesn't.

OP did not specify a C string.

More importantly, it gives OP something he claimed to need,

a way of sending a particular sequence of characters to the UART.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

I ended up using ka7ehk's method, which turned out to work perfectly for what I need it and not waste too much precious memory.

I could probably also use skeeve's method of sending one thing after another, but I prefer the first one and speed isn't really an issue.

I might also do it with a for loop as Visovian suggested, but for now it will have to do as it is.

 

I also tried using "sprintf" to do this but the program size grew up another 38%, which I cannot afford.

 

Thanks to everyone, who posted their suggestions on this problem.

 

Here is the program I wrote in case it comes useful to someone in the future:

 


...

int main(void)
{
	...
	
	char data_packet[24];
	
	...
	
	while (1) 
    {
		temp = ...
		fotoupor = ...
		...
		
		
		packet_assembler(temperatura,fotoupor,baterija,hum);
		uart_print_string(data_packet,1);
		
		...
		
	}
}



void packet_assembler(int16_t temp, int16_t fotoupor, int16_t baterija, int16_t hum_1)
{
	
	memset(data_packet, '\0', 17);      // Clear string
	
	strcat(data_packet,"<");			// start byte
	
	char MyLen = strlen(data_packet);		
	itoa(temperatura,data_packet+MyLen,10);		// temperature
	
	strcat(data_packet,",");			        // ,
	
	MyLen = strlen(data_packet);		
	itoa(fotoupor,data_packet+MyLen,10);		// photoresistor
	
	strcat(data_packet,",");					// ,
	
	MyLen = strlen(data_packet);				
	itoa(baterija,data_packet+MyLen,10);		// battery
	
	strcat(data_packet,",");					// ,
	
	MyLen = strlen(data_packet);
	itoa(hum_1,data_packet+MyLen,10);			// hummidity

	strcat(data_packet,">");			// end byte
	
}


void uart_print_string(char array[], char newline) {
	
	for(char a=0; a<strlen(array); a++){
		
		loop_until_bit_is_set(UCSRA, UDRE);
		UDR = array[a];
	}
	if(newline != 0)
	{
		loop_until_bit_is_set(UCSRA, UDRE);
		UDR = '\n';
		loop_until_bit_is_set(UCSRA, UDRE);
		UDR = '\r';
	}
}

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just a note: While this will work, it is extremely inefficient. Each time you're calling strlen, it's running through the whole string. The memset is unnecessary, and probably-wrong since the length given is 17 for a string of 24. But itoa and strcat both null-terminate anyway.

 

I'd probably do something like (untested):

 

char *s = data_packet;
s += sprintf(s, "<%d", temperatura);
s += sprintf(s, ",%d", fotoupor);
s += sprintf(s, ",%d", baterija);
s += sprintf(s, ",%d>", hum_1);

You can experiment with variants, but the basic idea is that instead of recomputing the length of the whole string, you just keep a pointer to the current location.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

the_real_seebs wrote:
instead of recomputing the length of the whole string, you just keep a pointer to the current location.

+1

 

Or you could keep an index to the current location, if you prefer.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...