how to transmit float via uart - Page 1
Forum section: electronics / general

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 04:23:08 pm
content: So here is the problem, i've tried this method:void usart_Transmit( float data ){char *ptr;char output;snprintf(output, 10, "%f", data);ptr = output;for (int i = 0 ; i < sizeof(data); i++){/* Wait for empty transmit buffer */while ( !( UCSR0A & (1<<UDRE0)) );UDR0 = ptr;}}but it dosnt work... is there any way to transmitfloat value via USART ?Im sorry if there is not enough info.Best Regards.

author: MosherIV
author_rank: Super Contributor
timestamp: August 01, 2016, 04:28:16 pm
content: HiYou have not said which microcontroller abd tool you are using.It is common on microcontrollers to disable the float part of printf/sprintf in the compiler because it take up quite a lot of binary space, normally you have to go and enable it somehow.

author: salviador
author_rank: Regular Contributor
timestamp: August 01, 2016, 04:30:44 pm
content: Quote from: mzdenkov on August 01, 2016, 04:23:08 pmsizeof(data)??sizeof(output)

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 04:34:55 pm
content: Im using ATMEGA 88 and Atmel Studiothis method works in Visual studio, but the method dosnt work on ATMEGA 88

author: MosherIV
author_rank: Super Contributor
timestamp: August 01, 2016, 04:48:56 pm
content: never used Atmel Studio but a quick search found thishttp://stackoverflow.com/questions/22526319/floating-point-value-not-being-displayed-using-printf-functionAny good?

author: vidi
author_rank: Contributor
timestamp: August 01, 2016, 04:58:21 pm
content: Without checking a purpose of UCSR0A, UDR0 registers, your loop seems to be wrong because you don't use 'i', neither 'ptr' to refer to next data in output string. Btw. Is sending a float as "decimal" string really what you want ?

author: MosherIV
author_rank: Super Contributor
timestamp: August 01, 2016, 05:01:04 pm
content: Oh, and yes, you got bugs in your code as well (as salviador and vidi mentioned)

author: IanB
author_rank: Super Contributor
timestamp: August 01, 2016, 05:01:36 pm
content: 1. How do you know the ascii representation of a float can fit into 10 characters (hint: it can't always)2. Why are you using sizeof(data) instead of sizeof(output)?3. Why haven't you explicitly specified the format and precision of the output string?4. Have you allowed room for the null termination character in the output string?5. Your loop doesn't look logical (where do you advance ptr?)

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 05:07:37 pm
content: Quote from: vidi on August 01, 2016, 04:58:21 pmI do use "i", but forum didnt write "[]".here is the thing.Ill have to transmit this:float distanceCalculate(uint16_t timeOfFlight0,bool option){float waterLevel = 0;waterLevel = ( speedOfSoundInFluid * timeOfFlight0 ) / 2;/*divide by 2, because sound needs to travel from:transceiver-->surfice of water-->transceiver*/if (option == MILLIMETER){return (waterLevel * 1000); // to express value in millimeter}else if(option == METER){return waterLevel; // value in meter}else return 0;}Uart is only for me to see what is the value which function above returns... so yes i need string value of float resault..That is why i ask for help, I dont quite know how to send this, and do it...

author: suicidaleggroll
author_rank: Super Contributor
timestamp: August 01, 2016, 05:37:29 pm
content: Why does it need to print a float if this just for monitoring purposes? What kind of range/resolution do you expect? You can probably just use fixed point by multiplying by a scalar and rounding to an integer. Eg: if you want the level in mm to three decimal places, just multiply by 1000 and print the output in um instead. Floats should generally be avoided on MCUs anyway, unless you can't get around it for a given application (yours looks like it can easily get around it).

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 05:41:41 pm
content: Quote from: suicidaleggroll on August 01, 2016, 05:37:29 pmIts not about resolution or anything...i just need to know how to turn float in to string/char and send that infobyte by byte to my pc.Im sorry if i was unclear before.

author: vidi
author_rank: Contributor
timestamp: August 01, 2016, 05:42:07 pm
content: Hi,could you please post the code again ? The square bracketsCode:will remain if put code into 'code section' ('#' icon in text editor). Btw: The conditions in second code are redundant for 'bool' type. I would suggest to use "enum distanceUnit" instead of macros that just redefine TRUE/FALSE. Function returns two different meaning of value. "meter" or "milimeter". That is, generally, a bad programming attitude. ATMEGA 88 is 8 bit MCU, without FPU. Using a float seems to be overkill since you can simply count (use) 'milimeter' as default unit.Is it problem only for float ? Does sending simple string work ? Do you have any logical analyzer ?

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 05:49:42 pm
content: after all editing(nicer code, but still hates me):Code://aftervoid usart_Transmit( float f ){char buffer;snprintf(buffer,64 ,"%f" , f);int i = 0;while (buffer != '\0') {while ( !( UCSR0A & (1<<UDRE0)) );UDR0 = buffer[i++];}}Code://beforevoid usart_Transmit( float data ){char *ptr;char output;sprintf(output, 10, "%f", data);ptr = output;for (int i = 0 ; i < sizeof(output); i++){/* Wait for empty transmit buffer */while ( !( UCSR0A & (1<<UDRE0)) );UDR0 = ptr;}}better representation of code.

author: suicidaleggroll
author_rank: Super Contributor
timestamp: August 01, 2016, 05:50:40 pm
content: Quote from: mzdenkov on August 01, 2016, 05:41:41 pmFloats are not ints.https://en.wikipedia.org/wiki/Floating_pointThere is no 1:1 mapping between the binary data stored in a float and its decimal representation. The mantissa and exponent information has to be translated to be readable. You cannot print a float without specifying the precision you want, you cannot give precision without knowing the range in order to specify your format code and control the number of digits.If you truly want the exact float value to be transmitted in ascii, your only option is to send the 8-digit hex representation of the value. Of course then you have to do the translation to decimal on your computer instead of the MCU, and you still get back to the precision/range question.

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 05:59:36 pm
content: Ok, i understand more now than before.But how does it work in arduino? my roomate used Serial.print(x) when he was monitoring ADC values for PID on hisinverted pendulum project.... and now im confused abit.

author: vidi
author_rank: Contributor
timestamp: August 01, 2016, 06:00:31 pm
content: Accordinghttp://www.atmel.com/Images/Atmel-2545-8-bit-AVR-Microcontroller-ATmega48-88-168_datasheet.pdfpage 238 it's fine (well except name: UDRE0 vs UDRE, but I guess it's the same). Do you have any logical analyzer ? Does sending simple string work ?

author: richardman
author_rank: Frequent Contributor
timestamp: August 01, 2016, 06:05:42 pm
content: Your code looks to be fine and should work. There are all the other considerations on decimal vs. float etc. and "why use float" etc., but as written, and especially if you confirm that it can transmit other data (e.g. a character), then there's nothing obvious.What kind of output do you actually see?

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 06:06:42 pm
content: Quote from: vidi on August 01, 2016, 06:00:31 pmWhen i send a simple char or int it works..but other than that it dosnt.. i get some wild chars...

author: suicidaleggroll
author_rank: Super Contributor
timestamp: August 01, 2016, 06:14:39 pm
content: Quote from: mzdenkov on August 01, 2016, 05:59:36 pmArduino's serial.print(), like most other Arduino functions, has a gigantic bloated back end to support it with default precision settings and format codes.

author: vidi
author_rank: Contributor
timestamp: August 01, 2016, 06:31:13 pm
content: Quote from: mzdenkov on August 01, 2016, 06:06:42 pmWild chars might be caused by imprecise baudrate (did you call USART_Init before?), or snprintf does not terminate buffer with '\0' (it should) and you are printing random data from stack. But I don't see anything wrong with your new code. Well ... could you please try one experiment ? Could you change function toCode:void usart_Transmit( void){char buffer;float f = 5.5;snprintf(buffer,64 ,"%f" , f);int i = 0;while (buffer != '\0') {while ( !( UCSR0A & (1<<UDRE0)) );UDR0 = buffer[i++];}}and call it without parameter ? Just to eliminate the ABI issues.

author: janoc
author_rank: Super Contributor
timestamp: August 01, 2016, 06:33:36 pm
content: Does the default avr-libc sprintf() implementation actually support floats? I believe it does not unless you explicitly enable it (and pay the price by a huge code size increase).

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 06:34:28 pm
content: Code:void usart_Init(){DDRD |= (1 << PIND1); // set TXD as outputunsigned int USART_BOUDRATE = 9600;unsigned int ubrr = (((F_CPU/(USART_BOUDRATE * 16UL))) - 1);//set baud rateUBRR0H = (unsigned char)(ubrr >> 8);UBRR0L = (unsigned char)ubrr;UCSR0B |= (1 << TXEN0); // enable TXUCSR0C |= (1 << USBS0)|(3 << UCSZ00); // 8data, 2 stop bits}

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 06:36:33 pm
content: here is the pic

author: suicidaleggroll
author_rank: Super Contributor
timestamp: August 01, 2016, 06:39:35 pm
content: http://www.avrfreaks.net/forum/using-sprintf-having-trouble-doublefloatcliff notes: janoc is right, snprintf is returning a question mark for your %f format code because it doesn't support floats.

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 06:41:20 pm
content: Quote from: suicidaleggroll on August 01, 2016, 06:39:35 pmso is there any way to send "float" value via USART?

author: PartialDischarge
author_rank: Super Contributor
timestamp: August 01, 2016, 06:45:37 pm
content: You CAN send a float as if it were and int and then recompose into a float again, you just have to trick the compiler.For a float 32:float32 varf;int32 x;varf=123.33; //for examplex= *(int32*)(& varf);transmit(x);//at receiverint32 y;y=receive();varf=*(float32 *)(& y);Not only is this technique useful to send floats as ints in serial streams, but to store floats in EEPROM memory where the architecture only allows byte writes to memory

author: suicidaleggroll
author_rank: Super Contributor
timestamp: August 01, 2016, 06:50:25 pm
content: Quote from: mzdenkov on August 01, 2016, 06:41:20 pmThe link that I posted (and you quoted) contains the flags you need to add to the linker to properly support floats in snprintf, that is if your device is big enough to handle the massive increase in size.

author: LazyJack
author_rank: Frequent Contributor
timestamp: August 01, 2016, 06:51:47 pm
content: Quote from: mzdenkov on August 01, 2016, 06:41:20 pmWell. Strictly speaking USART sends a string of characters. So anything can be sent over it, if you convert it to a string. Given that what is your question? How to send a string of characters over USART or how to convert a float value into a string?

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 07:17:15 pm
content: Quote from: suicidaleggroll on August 01, 2016, 06:50:25 pmAm i doing something wrong?

author: vidi
author_rank: Contributor
timestamp: August 01, 2016, 07:32:29 pm
content: Put those options somewhere into "Linker - Miscellaneous". The "Libraries" field won't work (it forces -l flag for all items)

author: IanB
author_rank: Super Contributor
timestamp: August 01, 2016, 07:47:53 pm
content: Quote from: suicidaleggroll on August 01, 2016, 05:50:40 pmThis is not strictly true. If you include enough ascii digits in the mantissa of a float or double it is usually possible to read back in and recover the exact binary value that was written out.

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 08:17:42 pm
content: Ok, now i get chars on output.. but first is always wrong.im sending 123456, and now look at the picIm using Atmel ICE and i can see that registers are ok, but hardware output isnt

author: vidi
author_rank: Contributor
timestamp: August 01, 2016, 09:13:31 pm
content: When you put underscore (or any other dummy character) before float formatter "_%f" what does it do ? Do you have the same settings (9600, 2 stop bits) for UART/Saleae/Terminal ? Can you upload the data sample ?

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 10:24:40 pm
content: In every case First bit is some crap:Code:UDR0 = /*buffer[i++]*/ 'a';

author: vidi
author_rank: Contributor
timestamp: August 01, 2016, 10:47:08 pm
content: In screenshot you have set baudrate to 9200 instead of 9600 ( that you used in code ). You could also post here (or somewhere else, depending on size) raw data by click-ing on "option -> save capture".

author: suicidaleggroll
author_rank: Super Contributor
timestamp: August 01, 2016, 10:58:54 pm
content: It looks to me like the LA is detecting a glitch on the line as the start bit and shifting its interpretation of the character appropriately. What does your port initialization look like? Is it possible you're inadvertently setting the line to output low before configuring it for UART? What if you don't transmit anything, and just monitor the Tx line like you're doing while the MCU starts up, do you get the same glitch?

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 11:00:55 pm
content: Quote from: vidi on August 01, 2016, 10:47:08 pm

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 11:02:41 pm
content: Quote from: suicidaleggroll on August 01, 2016, 10:58:54 pmyou have code on page 1when i dont transmit the line is clear.

author: Pack34
author_rank: Frequent Contributor
timestamp: August 01, 2016, 11:10:07 pm
content: A float is a collection of four bytes in IEEE754 format.http://www.h-schmidt.net/FloatConverter/IEEE754.htmlTry something along the lines of...unsigned char byteArray;memcpy(byteArray, floatNumber, sizeof(byteArray));for (int i = 0; i < sizeof(byArray); i++){Uart1_Write(byteArray);}

author: suicidaleggroll
author_rank: Super Contributor
timestamp: August 01, 2016, 11:15:17 pm
content: Quote from: mzdenkov on August 01, 2016, 11:02:41 pmWhen you don't transmit or when you don't init? And are you sure? I just found the init code and you are indeed telling the line to output low before reconfiguring it for UART duty, that could definitely cause this glitch. Most of the MCUs I have experience with will automatically set the necessary pin directions when you set the mode, no need to manually make it an output before-hand.

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 11:32:52 pm
content: when i put //DDRD |= (1 << PIND1); // set TXD as outputin a comment... NO MORE GLITCH...

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 01, 2016, 11:43:46 pm
content: Quote from: Pack34 on August 01, 2016, 11:10:07 pmmemcpy(byteArray, floatNumber, sizeof(byteArray));wont work with Float...

author: suicidaleggroll
author_rank: Super Contributor
timestamp: August 02, 2016, 12:07:00 am
content: Quote from: Pack34 on August 01, 2016, 11:10:07 pmHe said earlier he wants it in ASCII.

author: Pack34
author_rank: Frequent Contributor
timestamp: August 02, 2016, 02:35:42 am
content: Quote from: suicidaleggroll on August 02, 2016, 12:07:00 amThen that's even easier.1. Write an itoa function.1a. An ascii character is simply the integer value plus 0x30.1b. You can isolate the ONES position using a modulus, then iterate through the number by dividing by ten.2. Cast the float as an int to isolate the higher end of the number.3. Print out the int portion using the itoa function4. Print out a decimal point5. Subtract the casted float from the original to isolate the decimal precision.6. Assemble the desired decimal precision by multiplying by ten and print them out, one by one using the itoa function.

author: PartialDischarge
author_rank: Super Contributor
timestamp: August 02, 2016, 05:09:36 am
content: Quote from: mzdenkov on August 01, 2016, 11:43:46 pmnot sure if you saw my reply, please try this, it will work:int32_var = * (int32 *) (& fl32_var);thats all you need to treat a float32 as an int32.

author: richardman
author_rank: Frequent Contributor
timestamp: August 02, 2016, 05:23:25 am
content: Quote from: suicidaleggroll on August 02, 2016, 12:07:00 amYes, but you meant something more likememcpy(byteArray, &floatNumber, sizeof(byteArray));

author: helius
author_rank: Super Contributor
timestamp: August 02, 2016, 05:53:32 am
content: Quote from: IanB on August 01, 2016, 07:47:53 pmThis is possible, but only when the printing and reading algorithms are carefully written to be stable. It's not trivial. You should also consider what happens with denormals, infinities and NotANumbers. There's no standard printed format for NaNs, so this is very hard to do portably.

author: Ravenghost
author_rank: Contributor
timestamp: August 02, 2016, 07:25:43 am
content: Quote from: mzdenkov on August 01, 2016, 06:41:20 pmYou should use dtostrf() functionhttp://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__stdlib_1ga060c998e77fb5fc0d3168b3ce8771d42.htmlWith Peter Fleury UART libraryhttp://homepage.hispeed.ch/peterfleury/avr-software.htmlit should look something like this:Code:char output;uart_puts(dtostrf(data, 10, 6, output));

author: andersm
author_rank: Super Contributor
timestamp: August 02, 2016, 07:57:46 am
content: Quote from: MasterTech on August 02, 2016, 05:09:36 amThat breaks the strict aliasing rule, and is not valid C. Also, if you transmit the binary representation of a floating-point number you still have to account for endianess. Most systems these days are little-endian, but it's someting to keep in mind.

author: janoc
author_rank: Super Contributor
timestamp: August 02, 2016, 09:56:10 am
content: Mamma mia ...He just wants to print a float as a string, that's all. Let's try to not engineer a moon rocket around it.Printf() family of functions are usually not usable on microcontrollers, because the float code is too huge, so it is disabled by default. However, writing a small function to convert a float value to a string is not difficult and certainly order of magnitude smaller than re-enabling the printf() support.E.g. something like this (didn't test that code, but I wrote a lot of similar things myself):http://www.microchip.com/forums/FindPost/53508Basically take the integer part of the float, convert to string, add a decimal point and then print out the decimal part of it, taking care of any leading zeroes. Adding rounding is a trivial matter too. 10 minutes job, modulo some stupid bugs that I always make.

author: IanB
author_rank: Super Contributor
timestamp: August 02, 2016, 11:15:34 am
content: It is, probably, a sign that something has gone a bit wrong when you start using floating point calculations on a little microcontroller for basic arithmetic.For instance, if the speed of sound in water is 1500 m/s, then a signal will travel 1500 m in a second, or 1500 mm in a millisecond, or 1.5 mm in a microsecond. So you can just measure the time interval in microseconds, multiply by 1.5 (trivial in binary arithmetic), and you have the distance in mm.

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 02, 2016, 12:37:21 pm
content: Thank you guys. snprintf works... as it looks like sir " suicidaleggroll" found other mistake in seting TX as output.After 20 times of sending random numbers ... i see what i need to see... FLOAT as ASCII on TX line.

author: mzdenkov
author_rank: Regular Contributor
timestamp: August 02, 2016, 12:52:41 pm
content: One more thing, i have some time to calculate values:Mesurements are started in interval of ~ 1 sec with timer... other is happens in interrupts like on flow chart