Multiplying within #define gives strange values - arduino

I write a code for Arduino Nano and I experience this weird behaviour:
#define GREAT (60 * 60000)
#define STRANGE (60 * 6000)
#define ZERO_X (60 * 1000)
void setup() {
Serial.begin(115200);
Serial.println(GREAT); // Prints 3600000, that's correct
Serial.println(STRANGE); // Prints 32320, thats wrong
long zerox = ZERO_X;
Serial.println(zerox); // Prints -5536, thats also wrong, obviously
}
void loop() {}
What is going on?
I use MSVS2019 Comunity with vMicro

You use integer literals to define your values, and as described in documentation type of literal depends on where it can fit. According to specs
On the Arduino Uno (and other ATmega based boards) an int stores a 16-bit (2-byte) value.
(emphasis is mine) Arduino Nano has CPU with 2 bytes int - 60, 6000 and 1000 fit in signed integer and such type is used. Though neither values of 60 * 6000 nor 60 * 1000 can fit in 2 bytes int so you get integer overflow with UB and unexpected values.
On another side 60000 does not fit into signed int of 2 bytes, so it gets type long with 4 bytes and 60000 * 60 fits there so you get expected result. To fix your problem you can just specify suffixes:
#define GREAT (60 * 60000L)
#define STRANGE (60 * 6000L)
#define ZERO_X (60 * 1000L)
and force them all to be type long. It is not necessary to do it for 60000, but it is better to have it for consistency.
For your code change:
long zerox = ZERO_X;
this line does not solve the issue as after macro substitution it is equal to:
long zerox = (60 * 1000);
and it does not help, as first calculations with type int are done on the right side of initialization, overflow happens and then int is promoted to long. To fix it you need to convert to long one of the arguments:
long zerox = 60 * static_cast<long>(1000);
or use suffix as suggested before.

Related

Variable runover when not reaching the limit yet

My variable had a Runover even before it reached its range.
Currently, I am working with timer 2 with a frequency of 90MHz. When I retrieved the current time by __HAL_TIM_GET_COUNTER() and printed it by printf("receive_time: %lu\n", (unsigned long)receive_time), the maximum the receive_time get is 16 bits although I declared it as 32 bits. I observed the printed value and it never exceeded 2^16 -1.
I also tried to print with printf("%" PRIu32 "\n",a); but it also returned the same.
I had another 32-bit variable send_time which is used later to subtract with receive_time. However, it gave me something in the order of 2*10^9 (2147482170 for example) besides some common values.
I want to ask what happen and if I do something wrong. If everything is correct, is there any way to handle the rollover besides having 2 timers starting at different times?
The code is here:
Radio::radio.tx_buf[0] = seq++; /* set payload */
printf("send a ready message\r\n");
Radio::Send(1, 0, 0, 0); /* begin transmission */
send_time = __HAL_TIM_GET_COUNTER(&htim2);
.
.
.
if (Radio::radio.rx_buf[0] == 100)
{
receive_time = __HAL_TIM_GET_COUNTER(&htim2);
printf("receive_time: %lu\n", (unsigned long)receive_time);
printf("send_time: %lu\n", (unsigned long)send_time);
time_difference = (receive_time - send_time)/2;
printf("time_difference: %lu\n", (unsigned long)time_difference);
uint32_t distance = time_difference * 10 / 3;
printf("Distance: %lu\n", (unsigned long)distance);
}```
Thank you in advanced,
Huy Nguyen.

How can i determine duration of wav file

I'm working with .wav files and I need to get their duration in seconds.
So far I've been determining it with:
File size / byte_rate
Byte_rate being (Sample Rate * BitsPerSample * Channels) / 8.
And it works, with smaller files, when I try to parse bigger files, I get more seconds than the actual duration.
Example:
Size(bytes): 45207622 Byte_rate: 176400 Duration: 256
(45207622 / 176400)
but the actual duration is 250...
FYI: I've double checked the size and byte_rate, they are correct.
Without a sample RIFF header or your code, it would be difficult to answer the specifics in your question. (i.e. Why your math isn't coming to your expected result.)
However, since you've specified that you're working in C in the comments, might I suggest using the sox library instead of parsing the headers with newly written code? In addition to catching a fair number of edge cases, this allows you to support any format sox supports reading without having to write any of the reading code yourself. (Though anyone inclined to do so should probably take a look at Can someone explain .wav(WAVE) file headers? and RIFF WAVE format specifications. The process should be roughly the method described in the question, at least in most cases. [Edit: That is chunk data length divided by the header's byte rate.])
Example code:
#include <sox.h>
#include <stdio.h>
int main(int argc, char **argv) {
sox_format_t *fmt;
if(argc < 2) {
printf("Please provide audio file.\n");
return 1;
}
fmt = sox_open_read(argv[1], NULL, NULL, NULL);
__uint64_t ws = fmt->signal.length / fmt->signal.channels;
if(fmt->signal.length) {
printf("%0.2f seconds long\n", (double)ws / fmt->signal.rate);
} else {
printf("Cannot determine duration from header.\n");
}
}
For anyone curious, I largely derived this from the sox command line tool's source code.
Thank you EPR for giving me the fix to timing in my program. I'm not using libsox, I've set up a struct trying to match the original at http://www.lightlink.com/tjweber/StripWav/Canon.html This is NOT the correct way to do it but it works for simple files. Another useful reference is at http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
Anyway I assume the header is 44 bytes and read() it into memory at the location of the struct. Then I can access fields of the struct, malloc space for the pcm data and read() it into the pcm space from where the file pointer was left. I'm just writing an audiogram program so it needs to be close to correct for the WAV files I generate with arecord, sox, Audacity. Always 2 channels, 44100 sample rate. My struct:
struct wavhdr { // defined by Microsoft, needs to match
char riff[4]; // should be "RIFF"
uint32_t len8; // file length - 8
char wave[4]; // should be "WAVE"
char fmt[4]; // should be "fmt "
uint32_t fdatalen; // should be 16 (0x10)
uint16_t ftag; // format tag, 1 = pcm
uint16_t channels; // 2 for stereo
uint32_t sps; // samples/sec
uint32_t srate; // sample rate in bytes/sec (block align)
uint16_t chan8; // channels * bits/sample / 8
uint16_t bps; // bits/sample
char data[4]; // should be "data"
uint32_t datlen; // length of data block
// pcm data follows this
} hdr;
I was trying to use the measured file size - header length / samples/sec, that didn't work, I was off by a factor of 6.

LM35 decreases when near heat

My LM35 connected to arduino decreases temperature value in celsius when near heat and increases value when far from heat. Could any one help or know why it's working other way round.
void setup() {
// put your setup code here, to run once:
//Start the serial connection with the computer
//to view the result open the serial monitor
// 9600 is the “baud rate”, or communications speed.
Serial.begin(9600);
}
void loop() {
delay(2000);
float tempValue = analogRead(A2);
// converting that reading to voltage
float tempVoltage = (tempValue/1024.0)*5.0;
float tempDegrees = (tempVoltage - 0.5) * 100.0 ;
//Multiplying tempDegrees by -1 to make it positive
tempDegrees =(tempDegrees * -1);
Serial.println("............................................");
Serial.println("Degrees");
Serial.println(tempDegrees);
delay(2000);
}
just randomly came accross your question. and it has been 6 years since I touched an LM35 :d
but I think you have a problem in that -0.5 thing. I did not really get that!
LM35's function as far as I remember was :
T = V/ 10mV
you might want to check the datasheet but I'm pretty positive this is the equation. when you get the voltage from ADC you have to put it in this equation and get the result.
be careful : you have to also attribute for the temperature error as well as ADC noise if temperature precision is important for you.
If you are using a 5 volts power supply to your arduino:
5 Volts in the Arduino are directly converted to 1023 in the output of the ADC
ADC_Outpput * 5000 / 1024, where 5000 is coming from 5volts as millivolts, 1024 is the 10 bist resolution
LM35 resolution is linearly generated with a rate of + 10-mV/°C
so the analogVolt = ADC_Outpput * 5000 / 1024
FinalTemperature = (analogVolt - 500) / 10

Arduino Variable Size and Fixed Point

I am working on a project where I need to use Fixed Point math, I am not able to figure out why the numbers are "Rolling Over", I was able to get a large enough number when I changed the shift amount from 16 to 8 and finally to 4. Here is the code I am using at present:
#define SHIFT_AMOUNT 8
#define SHIFT_MASK ((1 << SHIFT_AMOUNT) - 1)
#define FIXED_ONE (1 << SHIFT_AMOUNT)
#define INT2FIXED(x) ((x) << SHIFT_AMOUNT)
#define FLOAT2FIXED(x) ((int)((x) * (1 << SHIFT_AMOUNT)))
#define FIXED2INT(x) ((x) >> SHIFT_AMOUNT)
#define FIXED2FLOAT(x) (((float)(x)) / (1 << SHIFT_AMOUNT))
int32_t test = FLOAT2FIXED(1.00);
void setup()
{
Serial.begin(57600);
}
void loop(){
test += FLOAT2FIXED(1.00);
Serial.println(FIXED2FLOAT(test));
}
And the output:
1
2
3
...
127
-128
-127
-126
When SHIFT_AMOUNT = 8 I am only able to store variables from -128 to 128 but since I am using a 32 bit variable shouldn't a 16 bit shift move the decimal point to the "Middle" leaving 2 16 bit sections, one for the Whole Number and the other for the decimals? Shouldn't the whole range of the int32_t be −2,147,483,648 to 2,147,483,647 with the shift at 16? Is there a setting that I am missing or am I just way off with how this works?
If SHIFT_AMOUNT = 4 I get a range that I need but this doesn't seem right since all the other examples that I have seen online use the 16 bit shift.
Here is a link showing what I am looking to do
EDIT
If I have this correctly, when shifting 8 bits when using a 16 bit wide type that leaves 8bits for the whole and 8 for the fractal leaving a range of -128 to 128. Hence the need for using the 4bit shift increasing the range of the whole to -32,768 to 32,767 is this correct? If that is right then is the int32_t not a true 32 bit wide?
EDIT2
Patrick Trentin pointed out where I was going wrong. Everything was correct except for the part I copied from the linked question. I was casting to a int not a int32_t. The int type is 16bits wide, hence having to use 4 to get the range I needed.
Change this:
#define FLOAT2FIXED(x) ((int)((x) * (1 << SHIFT_AMOUNT)))
into this:
#define FLOAT2FIXED(x) ((int32_t)((x) * (1 << SHIFT_AMOUNT)))
Rationale: the size of int is 16-bit on an Arduino Uno (see the documentation), this caps the size of the values that you are storing within your int32_t variable to 16 bits.
EDIT:
The fact that int16_t is an alias of signed int, which is an alias for int, can be corroborated by either looking at the online documentation or at the content of the file
arduino-version/hardware/tools/avr/lib/avr/include/stdint.h
among the Arduino Uno sources:
/** \ingroup avr_stdint
16-bit signed type. */
typedef signed int int16_t;

Arduino multiplication error

Arduino not is able to multiply numbers from 40 onwards by 1000 for example
void setup() {
Serial.begin(9600);
}
void loop() {
float a = 60 * 1000;
Serial.print(a);
}
the result is -5536 .-. ??? what ??
I need to convert seconds to milliseconds, but I do not know alternatives to multiplication by 1000
The problem is that you are
taking a (signed) int and setting it to 60
taking a (signed) int and setting it to 1000
multiplying them, obtaining a signed int. This generates an overflow, so the result is -5536
converting this number in a float; -5536 -> -5536.0
The solution? Since you want to deal with floats... Operate with floats!
float a = ((float)60) * 1000;
float a = 60.0 * 1000;
The two solutions are the same; the first converts (int)60 in a float, then multiplies it by (int)1000, which gives you (float)60000.
The second tells the compiler that 60.0 is a float.
In both cases a float multiplied by an int gives you a float, so... No overflow!
The problem is that Serial.print converts a to signed integer. Try this:
Serial.print((float)a);
or this:
#include "floatToString.h"
char buffer[25];
Serial.print(floatToString(buffer, a, 5));

Resources