# Convert DS18B20 temperature to string

I want to convert DS18B20 temperature (4-bytes code from datasheet) to string with accuracy 0.1°C (like sprintf %.1f). AVR. C language (avr-gcc). I need for small code, so sprintf, floating-point types and round from math.h is bad idea. My following current code is bad also:

void reverse_string(char *s)
{
char *p, c;

for (p = s + strlen(s) - 1; s <= p; ++s, --p) {
c = *p;
*p = *s;
*s = c;
}
}

void ts_to_string(uint16_t ts, char *s)
{
int8_t n = (int8_t)(ts >> 4);
uint8_t neg = n & 0x80;
char *p = s;
float f;

if (neg)
n = -n-1;
do {
*p++ = n % 10 + '0';
} while ((n /= 10) > 0);

if (neg)
*p++ = '-';

*p = '\0';
reverse_string(s);
*p++ = '.';

f = (float)(ts & 0xf) / 16.0;
if (neg)
f = 1.0-f;
*p++ = (char)round(f * 10.0) + '0';
*p = '\0';
}

-
Welcome on board. Never apologize for your bad English. My and most other users' Russian is totally non-existant! –  stevenvh Jun 2 '12 at 15:22
The question of excluding printf is very intersting. But at the end you will get one more implementation of printf. Shouldn't the optimizing linker do the work and eliminate dead code, leaving exactly the minimal set of minifunctions ? –  user924 Jun 2 '12 at 15:40
@Rocket Surgeon: I think a linker will include whole sprintf function to my program. But 3/4 of its code I don't use. I need only for %.1f. –  Corvus Jun 2 '12 at 15:55
Fine. Post the solution as an answer. You'll be able to accept it in a few days as the best answer. You won't get reputation points for it, but others will see immediately that your question has been answered. (You will get reputation points for the upvotes it would get.) –  stevenvh Jun 2 '12 at 17:34

(Conversion) algorithms are a trade-off between size and speed. The fastest conversion is via a lookup table, but that takes a lot of code size, especially for 16-bit!

This AVR application note shows methods for converting between binary and BCD for different word lengths. The application note is written for AVR assembly, but describes the algorithms in text and flowcharts, so it should be usable for other controllers as well.
You'll have to take the absolute value first, since the algorithms seem to be written for unsigned numbers.

If you don't understand the algorithm completely,use the BCD as an intermediate step. The last step, from BCD to ASCII should be easy. Otherwise you can tweak the algorithm so that MSD and LSD are shifted to different bytes, so that you only have to OR with 0x30 to get the ASCII value.

Since this was written for assembly it may be most code-efficient to write it in in-line assembly, instead of C.

-
DS18B20 uses not BCD. –  Corvus Jun 2 '12 at 15:42
I know, it uses signed binary. But you can use the algorithm to convert binary to BCD, and then it's easy to go to ASCII. –  stevenvh Jun 2 '12 at 15:56

My final solution:

void ts_to_string(uint16_t ts, char *s)
{
int8_t t, neg;
char *p = s;

// integer part processing
//
t = (int8_t)(ts >> 4);
neg = t & 0x80;
if (neg)
t = ~t;
do {
*p++ = t % 10 + '0';
} while ((t /= 10) > 0);

*p++ = neg ? '-' : '+';

*p = '\0';
reverse_string(s);

// fractional part processing
//
*p++ = '.';
t = (int8_t)ts & 0xf;
if (neg)
t = (-t) & 0xf;
t = ((int16_t)t * 100) >> 4; // shift on 2 dec. digit, then mul 0.0625
if ((t % 10) >= 5) // round
t += 10;
t /= 10; // shift on 1 decimal digit
*p++ = (char)t + '0';

*p = '\0';
}


Its size approx. 100 bytes, that enough for me. (Solution from question has size approx. 3 KB because of float and round.)

-