Hi all, I'm new to C++ and have this issue. I have a number of Arduino nanos talking over RS485. I convert some "floats" to 2 bytes successfully and transmit them. I then try to convert them back to an int16_t but the result is a 32 bit word, not a 16 bit. After a lot of searching I found a compiler switch (compiler.cpp.extra_flags=-fwrapv) that works fine on IDE 1.8.16. How can I implement this on IDE 2.0.0 rc-8? Also can someone explain why by default this happens? Try and keep it simple for a newby Thanks all. B'
Hi, I think this should be a good explanation for what happens
https://stackoverflow.com/questions/47232954/what-does-fwrapv-do
If you want a sketch to convert something from whatever to int16_t it is much better to design your code to take care of and avoid possible problems ...
If you can convert floating variables to 2 bytes, it means that either their value is less than what you can transport in 16 bit ... or you lose a part of the value. I assume you have taken care of that, didn't you?
If you manage to receive the two consecutive bytes and stuff them into one variable (e.g. like the following snippet)
uint8_t LowByte = 255;
uint8_t HighByte = 255;
uint16_t MyResult = (HighByte << 8) + LowByte;
it depends on the type of the variable you use - here it is MyResult - whether the result is 32 or 16 bit.
In any case it is recommended to post your code here also, that makes it much easier to find solutions to problems.
Just take care to use "Copy for forum" in the menue of the Arduino IDE to copy the code to the clipboard and paste it from there to the post editor. That makes sure it looks like the code snippet above ...
P.S.: If you convert float to two bytes (and later back to int16_t) you must make sure that the input value is in between -32768 and 32767. These are the limits of signed 16 bit integers!
Just a simple sketch you may play around with:
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
PrintResult(42);
PrintResult(-32768);
PrintResult(32767);
PrintResult(-50000);
PrintResult(50000);
}
void loop() {
// put your main code here, to run repeatedly:
}
void PrintResult(float value) {
uint8_t LowByte = long(value) & 0xFF;
uint8_t HighByte = (long(value) & 0xFF00) >> 8;
int16_t MyResult = (HighByte << 8) + LowByte;
Serial.print("Result: ");
Serial.print(MyResult);
Serial.print("\tInput:");
Serial.println(value);
}
Here is it on Wokwi (makes playing around easier)
https://wokwi.com/projects/336818827681595986
I agree completely with ec2021 that the best approach is to write code that works with the existing compiler flags rather than modifying the build configuration.
However, I do think the subject of how to do this is generally interesting, even if not necessary in this particular case.
The short answer to your question is that it works exactly the same in Arduino IDE 2.x as in Arduino IDE 1.8.
There is one difference between the two, which is relevant under one specific set of conditions:
Arduino IDE 1.x comes with a pre-installed copy of the "Arduino AVR Boards" platform of the Uno, Mega, Leonardo, etc. boards. That is located in the hardware/arduino/avr
subfolder of the Arduino IDE installation folder. Some ancient (written before the creation of Boards Manager) or low quality tutorials will instruct you to modify the files there without mentioning the fact that the boards platforms you install via the Arduino Boards Manager are installed somewhere else. Arduino IDE 2.x does not come with this pre-installed copy of the "Arduino AVR Boards" platform (it installs it the first time you run the IDE instead).
In addition to the platforms of other architectures (e.g., SAMD) and 3rd party platforms (e.g., MightyCore), updates to the "Arduino AVR Boards" platform are also installed to that location (after which the version included with the IDE installation will no longer be used). So, even using Arduino IDE 1.8.16, you still really do need to know about this other location.
The Boards Manager of both Arduino IDE 1.x and 2.x use the same location to install platforms. So any good quality tutorial for modifying platforms will be applicable to either version of the IDE.
Thanks for the prompt reply. The sensor (DS18B20) provides the temperature in a 16 bit signed 2's compliment word so overflow is not an issue. I ran your sample program and printed some of the values in binary. I've not fully grasped it yet (at 68 brain is a bit slow) but can see where I have to read up some more. The "wokwi" link, very useful. Thanks again, B.
Thanks for your reply on this. I am going back to read up some more on this. I run ide 1.8.16 alongside 2.0.0-rc8 on a single pc which gives me a good platform for watching 2 "Nanos" chatting to each other. I'll go back to my code and have a rethink. Thanks again for the prompt reply. B.
I am confident that you will find out. Finally the core information is in these three lines, the first two lines remove decimal places (by casting floating point to long integer) and split the float variable "value" into a low and a high byte.
The third line just reconstructs the value of "value". However, if "value" is outside the of range of an int_16t the reconstruction will not comply with the float "value" ... The obvious reason is that in this case bits would be set to 1 which are not considered after the masking with 0xFF and 0xFF00.
uint8_t LowByte = long(value) & 0xFF;
uint8_t HighByte = (long(value) & 0xFF00) >> 8;
int16_t MyResult = (HighByte << 8) + LowByte;
Have fun!
Running the code samples has answered a lot but I needed to recover the decimal part. I've attached a snippet of my code and would ask if this is an ok way of achieving the goal? I'll accept being thrown from a balcony for some of the bad practices but these habits go back to Z80/8051 assembler days. Thanks.
// left in as I find it very useful :)
#define title "float_bytes_float_220711a"
#define board "Nano 328p, old bootloader."
#include "LibPrintf.h"
//-----------------------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
printf("\n Title: %s\n board: %s\n \n",title,board);
/*
tempC is derived from DS18B20 example code which gives a float of 4 bytes with byte 2 as MSB and 3 as LSB.
To save posting all the code I use a test temperature thus...
*/
float tempC = 23.45; // Test figure between -55 and +125 C
//Convert to bytes.
int16_t tempK=(tempC+273.15)*100; //Remove decimal places.
uint8_t hibyte=(tempK >> 8);
uint8_t lobyte=(tempK & 0xFF);
// Convert back to temp c.
float totalt=(hibyte << 8) + lobyte;
totalt=(totalt-27315)/100; // Recover decimal
printf("Temp c = %.0f Tempk = %d hibyte = %02X lobyte = %02X totalt = %.02f \n",tempC,tempK,hibyte,lobyte,totalt);
Serial.println(totalt);
}
//-----------------------------------------------------------------------------------------------
void loop() {
}
//-----------------------------------------------------------------------------------------------
I haven't checked your code properly, but on a first glance it looks ok.
If you need the decimals to be transported via integers it is a proper way to multiply by 10, 100 or whatever you require.
You must just take care that the result of your multiplication is inside the range of int16_t...
Or you might have to extend the number of transportation bytes ...
Another possibility is to generally change the unit from Celsius to tenths or hundreds of Celsius and only convert to float where required...
Nice to read that you remember the Z80/8051 assembler days. I started with 6502 assembler
(on paper) and Algol60 by punished cards on a Univac 1100...
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.