How to convert and store uint16_t value into 4 Hex chars?

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

How to update the code to convert and store uint16_t value into 4 Hex chars (including possible leading "0"s). The program should update

char *AT_Send = "AT$SlhLH\n"; // 9x

 

to

char *AT_Send = "AT$S04ED\n"; // 9x

 

I thought to use utoa() string function but it doesn't work with string declaration by pointer and it is ignoring leading "0"s. Maybe I should create own function for it to be able to send the final string to USART (IoT)
 

#define F_CPU 3333333

#include <avr/io.h>
#include <xc.h>         // F_CPU
#include <util/delay.h> // _delay_ms()
#include <stdlib.h>     // utoa()
#include <string.h>     //


volatile union u_type //Setup a Union
{
    unsigned int IntVar;
    unsigned char Bytes[2];
}
temp;


char *AT_Send = "AT$SlhLH\n"; // 9x
char *buffer = "0000";
//uint16_t Voltage = 1261; // 0x04ED, remark 12.61 Volts * 100, from ADC

uint8_t n;

int main(void) {

    temp.IntVar = 1261; // Voltage
    n = temp.Bytes[0];	// Just for variable watch
    utoa(temp.Bytes[0], buffer, 16); // Lo)
    utoa(temp.Bytes[1], buffer, 16); // Hi)

    while (1) {
    }
}

 

 

 

Last Edited: Wed. Sep 25, 2019 - 04:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

utoa into a buffer then just memcpy the 4 characters to the destination but make it a proper char array not an initialised pointer

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

So, one of the problems here: String literals are, in principle, not modifiable storage. It's undefined behavior to write into a pointer to a string literal.

 

You don't need the union for this; you're allowed to just cast a pointer to unsigned char and look at bytes without a union. I'm not familiar with utoa, looks like it's an AVR stdlib thing. If you want leading zeroes, you may be better off doing it yourself. Get a progmem buffer containing the characters, and do something like:

char hexchars[16] PROGMEM = "0123456789abcdef";

output[0] = hexchars[(temp >> 12) & 0xf];
output[1] = hexchars[(temp >> 8) & 0xf];
output[2] = hexchars[(temp >> 4) & 0xf];
output[3] = hexchars[(temp >> 0) & 0xf];

It's not necessarily any better than utoa, but it lets you control the leading zero behavior.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
int k=4;

...

*(AT_Send + 4) = '0'; // Just for test, overwrite "l" by "0" 
if (*(AT_Send + k) == '0')
        printf("They match!\n");

 

IF unfortunately doesn't work. How to define type of "k" variable?

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

int is fine for that. but you could also write this as "AT_Send[4]" or "AT_Send[k]". If it's not working, probably something else is up.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
if (*(AT_Send + 4) == '0')

 

Works well.

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

Hang on.

 

+4?

 

Shouldn't the 4th element be [3], or *(AT_Send + 3)?

 

0, 1, 2, 3. [4] of a 4-character string is outside the string.

 

This is why it's important not to "..." out the variable declarations.

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

Kevil wrote:

if (*(AT_Send + 4) == '0')

 

Works well.

Strange syntax! Most people write that as:

if (AT_Send[4] == '0')

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

clawson wrote:
Strange syntax!

Perfectly valid, of course - but agree that it is not the usual idiom to use here.

 

It's more typing, too!

 

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: 0

Note it is not a strange syntax but string declaration with a pointer. You can't use AT_Send[4], Array variable.

I was inspired by the article Pointers and Strings

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

Kevil wrote:
Note it is not a strange syntax

As noted, it is perfectly legal - but an unusual idiom.

 

but string declaration with a pointer. You can't use AT_Send[4]

Of course you can!

 

In 'C', the square brackets [ ] are the offset opperator - they can be applied to any pointer.

 

Similarly, an array name is a pointer to the first element of the array.

 

http://c-faq.com/aryptr/

 

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...
Last Edited: Mon. Sep 23, 2019 - 09:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Kevil wrote:
You can't use AT_Send[4], Array variable.
You need to dig out your C manual again. Of course you can use array syntax. In fact when you type foo[4] into a C program this is almost immediately broen down in to *(foo + 4) anyway.

 

Arrays and pointers are (almost!) completely interchangeable. The one thing you can do to a pointer that you can't to array is change the base address. So you can do things like:

char * foo;
foo = <address of some storage>;
foo [5] = 'X';

or alternatively:

char bar[10] = "hello";
PORTB = *(bar + 3); 

But while you can do:

char * foo;
foo += 3;

you cannot do:

char bar[10];
bar += 3;

but otherwise you can use them pretty much interchangeably. In fact a classic case of this is:

char string[] = "hello\n";
UART_sendstring(string);

...

UART_sendstring(char * str){
    while (*str) {
        UART_sendchar(*str++);
    }
}

In this the text starts as an array but is passed and used as a char pointer (which can then be adjusted to step through the string).

 

PS going right back to your original code in #1 why did you do this:

char *AT_Send = "AT$SlhLH\n"; // 9x

when

char AT_Send[] = "AT$SlhLH\n"; // 9x

is far more obvious  ? Also, as already noted, if you plan to update the elements in this you need to use the array syntax anyway.

Last Edited: Mon. Sep 23, 2019 - 09:14 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kevil wrote:

if (*(AT_Send + k) == '0')
        printf("They match!\n");

 

IF unfortunately doesn't work. 

What do you mean, "doesn't work"  - doesn't compile, or doesn't do what you intended?

 

The code shown is missing  a '(' on the if - so it won't even compile.

 

EDIT: that's rubbish - see #14!  blush

 

If that's just a typo (which is why you should never type code into a post), then see #7

 

and it's good  to get into the habit of always including the braces:

if( (*(AT_Send + k) == '0' )
{
        printf( "They match!\n" );
}

EDIT: ... which means the above will not compile - as it has too many parentheses! blush 

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...
Last Edited: Mon. Sep 23, 2019 - 10:15 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
The code shown is missing  a '(' on the if - so it won't even compile.
You sure? Maybe it's my eyesight but I see two open parenthesis and two close parenthesis. That matches doesn't it? In your later:

if( (*(AT_Send + k) == '0' )
{

I see three open, two close - how does that work?

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

When I used
 

if (*(AT_Send + k) == '0')

with k = 4 the IF command was not evaluated correctly, although there was '0' at the 4th position (verified by if (*(AT_Send + 4) == '0')

 

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

Can you post your most recent code that is still failing? (so we can also put it into AS7 and simulate what is happening)

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

clawson wrote:
You sure?

Ha ha - you are correct!

 

blush

 

That's the trouble with using unusual idioms!

 

and some whitespace would help.

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...
Last Edited: Mon. Sep 23, 2019 - 10:12 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kevil wrote:
there was '0' at the 4th position

Are you sure it was '0' - or 0 ... ?

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

Hence...

clawson wrote:
Can you post your most recent code that is still failing?
;-)

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

clawson wrote:
so we can also put it into AS7 and simulate what is happening

Actually, as this is all just about data in memory - we could do it in any IDE on any platform!

 

laugh

 

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: 0

Er no...

    unsigned int IntVar;

How wide is that going to be in arbitrarily chose IDE X, Y or Z?

 

(I'd stick to an AVR IDE when looking at AVR code unless you are positive the entire thing has been written from the ground up with portability in mind (not in this case))

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

now where's your post saying not to use the "plain" C types for that very reason ... ?

 

cheeky

 

EDIT

 

https://www.avrfreaks.net/commen...

 

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...
Last Edited: Mon. Sep 23, 2019 - 12:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The most recent test code. My goal is to replace 4 chars in AT_Send string by ASCII hex digits (Voltage 16 bits value).

 

#define F_CPU 3333333

#include <avr/io.h>
#include <xc.h>         // F_CPU
#include <util/delay.h> // _delay_ms()
#include <stdlib.h>     // utoa()
#include <string.h>     // For string functions

volatile union u_type //Setup a Union
{
    unsigned int IntVar;
    unsigned char Bytes[2];
}
temp;


char *AT_Sleep = "AT$P2\n"; // 6x
char *AT_Send = "AT$SlhLH\n"; // 9x
char *buffer = "0000";
//uint16_t Voltage = 1261; // 0x04ED 1261; 12.61 Volts * 100

uint8_t n = 7;
int k = 4;

int main(void) {
    /* Replace with your application code */

    temp.IntVar = 1261; // Voltage
    if (0 == strcmp(AT_Send, "AT$SlhLH\n"))
        printf("They match!\n");
    //    *(buffer) = 'R';

    *(AT_Send + 4) = '0'; // Store a char into string

    if (*(AT_Send + k) == '0')
        printf("They match!\n");	// Breakpoint 1
 to test IF result i.e. stops here if I replace k by digit 4 

    n = temp.Bytes[0];			// Breakpoint 2 to test IF result i.e. stops here for (*(AT_Send + k) == '0'
    //    utoa(temp.Bytes[0], buffer, 16); // Lo)
    //    utoa(temp.Bytes[1], buffer, 16); // Hi)

    while (1) {
        PORTA.DIR = (1 << 1);
        _delay_ms(500);
    }
}

 

 

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

Just start with something simple that works.

Using hexchars from post #3

char send_buf[10]; // make sure this is big enough
uint16_t voltage = 1261;

send_buf[0] = 'A';
send_buf[1] = 'T';
send_buf[2] = '$';
send_buf[3] = 'S';
send_buf[4] = hexchars[(voltage >> 12) & 0x0f];
send_buf[5] = hexchars[(voltage >> 8) & 0x0f];
send_buf[6] = hexchars[(voltage >> 4) & 0x0f];
send_buf[7] = hexchars[(voltage >> 0) & 0x0f];
send_buf[8] = '\n';
send_buf[9] = '\0'; // null terminate so can be used as a string

uart_send_string(send_buf); // or whatever function you are using 

 

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

On Sunday evening I ran your code in #1 on Linux GCC and got a Segmentation Fault when writing to AT_Send, but didn't write a reply. You were advised not to do this by clawson in #2 and the reason for the error was explained by the_real_seebs in #3.

Why then do you ignore this advise and produce test code thus:?

char *AT_Sleep = "AT$P2\n"; // 6x
char *AT_Send = "AT$SlhLH\n"; // 9x
char *buffer = "0000";

you should al least write this instead:

char AT_Sleep[] = "AT$P2\n"; // 6x
char AT_Send[] = "AT$SlhLH\n"; // 9x
char buffer[] = "0000";

Kevil wrote:

The most recent test code. My goal is to replace 4 chars in AT_Send string by ASCII hex digits (Voltage 16 bits value).

At the implementation level that may be the wrong goal. I see you've already used printf so let's use that to build the whole command instead of this modifying substring shenanigans.

 

int main (void)
{
    char cmd_buffer[16]; // let's have plenty of room

    IntVar = 1261;
    sprintf(cmd_buffer, "AT$S%04X\n", IntVar);

    printf("Hey! I just built this Send Command: %s" cmd_buffer);
}

Prints  Hey! I just built this Send Command: AT$S04ED

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

N.Winterbottom wrote:

Kevil wrote:

The most recent test code. My goal is to replace 4 chars in AT_Send string by ASCII hex digits (Voltage 16 bits value).

At the implementation level that may be the wrong goal. I see you've already used printf so let's use that to build the whole command instead of this modifying substring shenanigans.

 

int main (void)
{
    char cmd_buffer[16]; // let's have plenty of room

    IntVar = 1261;
    sprintf(cmd_buffer, "AT$S%04X\n", IntVar);

    printf("Hey! I just built this Send Command: %s" cmd_buffer);
}

Prints  Hey! I just built this Send Command: AT$S04ED

 

Perfect, I really appreciate your help very much, it works ;-). I did just small modification:

 

#include <stdio.h>      // sprintf()

volatile union u_type //Setup a Union
{
    uint16_t IntVar;
    unsigned char Bytes[2];
}
temp;

int main(void) {

    char cmd_buffer[16]; // let's have plenty of room

    temp.IntVar = 1261;
    sprintf(cmd_buffer, "AT$S%04X\n", temp.IntVar);
}

 

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

You do realise that would be exactly the same if you used?...

#include <stdio.h>      // sprintf()

uint16_t IntVar;

int main(void) {

    char cmd_buffer[16]; // let's have plenty of room

    IntVar = 1261;
    sprintf(cmd_buffer, "AT$S%04X\n", IntVar);
}

??

 

No need for the union - you aren't splitting 16 bits into two lots of 8 bits!

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

Sure sprintf makes sense if you have the code space available.

From a quick test, using sprintf vs the roll your own hexchars, uses about 1400 extra bytes of code (mega 328p).

Fine if you have the space available.

Of course it would also depend on whether you need to do any other types of conversions throughout the rest of the program.

 

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

I can confirm the

uint16_t IntVar;

 

works great. I need to check if I have enough space in ATtiny202 (2 kB) available.

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

sprinff() has occupied to much space so I created a function for uint16_t data to ASCII HEX.

 

char AT_Send [] = "AT$SF=1122\n";
volatile uint16_t Voltage;  // Used in ISR
int k;

void One_HEX(uint8_t x) {
    x += 0x30; // Add '0'
    if (x > 0x39) // > '9' i.e. 'A'...
        x += 7;
    AT_Send[k] = x;
    k++;
}

Voltage = ADC_0_get_conversion_result();

k = 6; // Start to store data from this position to AT_Send[] in Little Endian
One_HEX((Voltage & 0xF0) >> 4);     // H1 byte
One_HEX((Voltage & 0x0F));          // L1 byte
One_HEX((Voltage & 0xF000) >> 12);  // H2 byte
One_HEX((Voltage & 0x0F00) >> 8);   // L2 byte

 

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


Yikes

Kevil wrote:
I need to check if I have enough space in ATtiny202 (2 kB) available.

Would you like to explain why you chose such a crippled micro-controller ?

 

Ha Ha I Iound the killer spec. item - It's the 128KB of SRAM.

 

 

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

ATtiny202 perfectly fits to my project, measuring a car battery voltage each hour and send data through SigFox modem (Wisol BRKWS01). The program is almost ready, occupying about 800 bytes wink.  Sleep consumption is about 1 µA.

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

he program is almost ready, occupying about 800 bytes

800 bytes to read & spit out a voltage??!!...seems like it might be readily done in 125 bytes, if even that.  You can setup & get an adc reading in maybe 25 bytes, easy.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

I think it is reasonable especially under the situation that the core program has been generated automatically by using start.atmel.com generator i.e. ATtiny202 proper initialization it's components (RTC...). It is not just the ADC conversion but  waking up from sleep, re-enable and pin settings, relatively heavy USART communication with IoT modem including chip selection by pin etc.

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

Kevil wrote:
that the core program has been generated automatically by using start.atmel.com generator
Which is probably why it is 800 bytes and not 200 bytes! 

 

There's little/no merit in using code generators like Start when programming 2K micros. There simply cannot be anything that complex in 2K that you can't just write it direct using datasheet info (that is "bare metal" programming). also if you do do it that way you end up with code where you understand 100% what ever last line of it is doing (which later makes debugging/maintenance easier).

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

Kevil wrote:

sprinff() has occupied to much space so I created a function for uint16_t data to ASCII HEX.

 

    if (x > 0x39) // > '9' i.e. 'A'...
        x += 7;

 

 

Of course, that gets you UPPER-CASE hex, but who cares?  S.

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

Kevil wrote:

    x += 0x30; // Add '0'
    if (x > 0x39) // > '9' i.e. 'A'...

Rather than putting "Magic Numbers" in your code, and then having to comment it, why not just use the character literals:

    x += '0';
    if (x > '9')

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: 0

awneil wrote:

 

Rather than putting "Magic Numbers" in your code, and then having to comment it, why not just use the character literals:

 

    x += '0';
    if (x > '9')

 

Good tip, thank you smiley. I am a beginner.

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

Kevil wrote:
I am a beginner.
In which case do you have a copy of Kerninghan and Ritchie? It's possibly the ultimate guide to writing C because one of the authors is the chap who invented C. It should be able to tell you about things like character constants.: '3', 'r', 'Z' etc.

 

For example section 2.3 has this:

A character constant is an integer, written as one character within single quotes, such as 'x'. The value of a character constant is the numeric value of the character in the machine's character set. For example, in the ASCII character set the character constant '0' has the value 48, which is unrelated to the numeric value 0. If we write '0' instead of a numeric value like 48 that depends on the character set, the program is independent of the particular value and easier to read. Character constants participate in numeric operations just as any other integers, although they are most often used in comparisons with other characters.

Last Edited: Wed. Sep 25, 2019 - 04:38 PM