Arduino - Writing int array with for loop doesn't work - arduino

I am a college student so I'm still learning a lot. I ran into something interesting while making a project. I have this segment of code that works when it isn't placed in a for loop, but doesn't work when it is. I just want to understand why. Here is my code:
void setup() {
Serial.begin(9600);
int a[8];
for(int i=0;i<8;i++) {
a[i]=pow(2,i);
}
for(int i=0;i<8;i++) {
Serial.print(a[i]);
}
}
void loop() {
}
Here is the same code written without the first for loop (where the data gets written into the array):
void setup() {
Serial.begin(9600);
int a[8];
a[0]=pow(2,0);
a[1]=pow(2,1);
a[2]=pow(2,2);
a[3]=pow(2,3);
a[4]=pow(2,4);
a[5]=pow(2,5);
a[6]=pow(2,6);
a[7]=pow(2,7);
for(int i=0;i<8;i++) {
Serial.print(a[i]);
}
}
void loop() {
}
The first code outputs:
1
2
3
7
15
31
63
127
While the second code outputs:
1
2
4
8
16
32
64
128
Does anybody know? I really want to know why.

You are experiencing floating point round off. 2^4 will actually give you a value closer to 15.9999 and when this is assigned to an int, it truncates the decimal to 15. I would suggest doing bit shift operations when using powers of 2, such that:
for(int i=0;i<8;i++)
{
a[i]=(1 << i);
}
If you want to read up on bit shifting, look here.
If you want to know more about the floating point round off, look here.
Additionally if you wanted just a quick fix to your code closer to what you have, I believe this will also work:
for(int i=0;i<8;i++)
{
a[i]= (int) round( pow(2, i) );
}
This will round the floating result properly before casting it to an int.

Related

How to convert the ANALOG value of a pulse sensor to BPM?

first of all I would like to apologise for not speaking English very well.
My problem is the following: I have an arduino sensor (the iDuino SE049). I need to transform the analog value into BPM value.
So first I have to eliminate the drift (slide, (I don't know the precise word)) that the sensor can encounter. To do this I am advised to :
❶Work on an array of X values
❷Calculate the average
❸Subtract the average from each of the values
After I need to find the optimal value for X with the curve on Arduino IDE (I work on 1.8.19 version).
(Start with X = 50 for the size of the array, my teacher told me that)
Once I have this average, I need to make the system robust to light changes.
Problem: The amplitude varies so we can't work with a fixed threshold.
The heart rate is hidden in temporal information. You just need to be able to measure the characteristic time.
Solution : I can work with a curve that always has maximum amplitude of 1, then find an ideal threshold.
❶Work on the table of X values after eliminating the drift.
❷Look for the maximum
❸Divide all values by this maximum
❹Split by a threshold value
And then I have to do the study to find the threshold value
I tried to do a code that didn't worked well for the drift/slide, and average of the signal. But it didn't worked well because I can't get the value of the average outside of my loop to substract it to the analog value readed by the sensor.
I want to get all the value readed by the sensor during a time of 20 milliseconds, thats why I use the function millis(). And all this values needs to be stored in my array.
#include <Arduino.h>
int Sensor = 0;
//#define start_read 0
float tab[20]={0};
float sum = 0;
int i;
void setup() {
Serial.begin(9600);
}
void loop ()
{
unsigned long currentTime=0;
while (millis() - currentTime > 20)
{
tab[i] = analogRead(Sensor);
i++;
sum += tab[i];
float average = sum/20;
Serial.println(average);
}
currentTime = millis();
i = 0;
}
I have another code to test, I should work but it's not I don't know why : The variable names are in French but you can copy it and change the name to work on it if it's clearer, I'll understand the changes.
#include <Arduino.h>
int sensor=A0, comp=1, var=0;
float bpm=0;
double temps;
const int nbr=50;
float tab[nbr]={0}, moyenne=0, maximum=0;
void setup() {
Serial.begin(9600);
pinMode(sensor, INPUT);
}
void loop() {
maximum=0;
moyenne=0;
for (int i = 0; i < nbr; i++)
{
tab[i]=analogRead(sensor);
moyenne=moyenne+tab[i];
delay(40);
}
moyenne=moyenne/nbr;
for (int i = 0; i < nbr; i++)
{
tab[i]=tab[i]-moyenne;
}
maximum=tab[0];
for (int i = 0; i < nbr; i++)
{
if (tab[i]>maximum)
{
maximum=tab[i];
}
}
for (int i = 0; i < nbr; i++)
{
tab[i]=tab[i]/maximum;
}
for (int i = 0; i < nbr; i++)
{
if (tab[i]>(0.950))
{
bpm++;
}
}
temps=(millis()/1000);
var=15*comp;
if (temps/(var)>=1)
{
bpm=bpm*4;
//Serial.setCursor(0,10);
Serial.print("BPM : ");
Serial.print(bpm);
Serial.println("♥️");
//Serial.display();
//Serial.clearDisplay();
comp=comp+1;
bpm=0;
}
}
I can't get the value of the average outside of my loop
average is declared inside the loop so when an iteration ends it goes out of scope. You should declare it outside the while loop.
I want to get all the value readed by the sensor during a time of 20 milliseconds
You probably want to get something like 1 value per millisecond because all the values readed in 20 milliseconds are not 20 also you may not be able to know the exact number of values as it depends on the sensor's sample rate among other things.
If you need to reed those values to get a fixed threshold (i.e. use the code to study some behavior and get a const that will be used in the second code), put the code in void setup() instead of void loop() otherwise it will loop forever.
void loop ()
{
unsigned long currentTime=0;
while (millis() - currentTime > 20)
{
tab[i] = analogRead(Sensor);
i++;
sum += tab[i];
float average = sum/20;
Serial.println(average);
}
currentTime = millis();
i = 0;
}
Here there are some problems. Every time void loop() ends and executes again currentTime its going to be deleted and declared again holding the value 0. This makes the while loop execute for an infinite amount of time causing i to increment over tab's length leading to memory errors.
Also analogRead() does not return a float (returns an integer between 0 and 1023).
That's what I found about the first code, but about the second one I don't understand what is the problem. It is a compiling error? You don't get the expected output in the serial monitor? If it's the last thing, what is the output?

Robotic arm homing

I'm making a humanoid robotic arm and so far i've designed the shoulder. for rotation I've been using a high torque dc motor with an encoder that has a hall sensor and uses 17 pulses for one complete rotation along with a gear box that has a gear ratio of 1:625 making total pulses to (625x17). Now the robotic arm shoulder should only moves around 90 degrees making the pulses (625x17x0.25 = 6256.25). I'm controlling this dc motor with an Arduino and an L298 H-bridge module for controlling the motor. I've written a code using the Arduino IDE and I am still a beginner at this.
The main premise here is to do the homing of the shoulder. Basically when the robot turns on first it moves in the counter clockwise direction looking for the limit switch and then from their start counting the pulses to reach 90 degrees (6256.25 pulses) clock wise.
The main problem here is that after pressing the limit switch the motor starts to rotate in the clockwise direction after hitting the limit switch but it does not stop after reaching 90 degrees.
Any help is highly appreciated. Im new to coding as well as stack overflow.
#define Encoder_output_A 2
#define Encoder_output_B 3
int y;
int pos = 0;
int motorpin1=5;
int motorpin2=4;
float Count_pulses = 0;
float Count_rounds = 0;
int Limswitch = 7;
void setup() {
Serial.begin(9600); // activates the serial communication
pinMode(Encoder_output_A,INPUT); // sets the Encoder_output_A pin as the input
pinMode(Encoder_output_B,INPUT); // sets the Encoder_output_B pin as the input
attachInterrupt(digitalPinToInterrupt(Encoder_output_A),DC_Motor_Encoder,RISING);
pinMode(motorpin1,OUTPUT);
pinMode(motorpin2,OUTPUT);
pinMode(Limswitch,INPUT);
homing();
}
void loop() {
Serial.print("Result: ");
Serial.println(Count_pulses);
Serial.println(Count_rounds);
delay(100);
y=digitalRead(Limswitch);
if(y==LOW ||Count_pulses>=2656.25)// 17 x 625 x 0.25 = 2656.25
{digitalWrite(motorpin1,LOW);
digitalWrite(motorpin2,LOW);
} else{
digitalWrite(motorpin1,HIGH);
digitalWrite(motorpin2,LOW);
}
}
//The purpose of this code is to count the ouput pulses or the encoder outputs as you rotate the Motor shaft.
void DC_Motor_Encoder(){
int b = digitalRead(Encoder_output_B);
if(b > 0){
Count_pulses++;
Count_rounds = Count_pulses/17;
}
else{
Count_pulses--;
}
}
void homing() {
y=digitalRead(Limswitch);
if(y==HIGH && Count_pulses==0)
{while(y==HIGH){digitalWrite(motorpin1,HIGH);
digitalWrite(motorpin2,LOW);
y=digitalRead(Limswitch);
}
}
else if(y==LOW){
while(Count_pulses < 2657){
digitalWrite(motorpin1,LOW);
digitalWrite(motorpin2,HIGH);
}
}
if(Count_pulses>=2656.25)
digitalWrite(motorpin1,LOW);
digitalWrite(motorpin2,LOW);
}
Your code is a bit hard to read the way you have it formatted, I suggest you follow a more typical standard. It will make pulling out issues easier. Here's how I would format your homing() function:
void homing() {
y=digitalRead(Limswitch);
if(y==HIGH && Count_pulses==0) {
while(y==HIGH) {
digitalWrite(motorpin1,HIGH);
digitalWrite(motorpin2,LOW);
y=digitalRead(Limswitch);
}
} else if(y==LOW){
while(Count_pulses < 2657) {
digitalWrite(motorpin1,LOW);
digitalWrite(motorpin2,HIGH);
}
}
if(Count_pulses>=2656.25) {
digitalWrite(motorpin1,LOW);
digitalWrite(motorpin2,LOW);
}
}
The issue may be that you are missing a curly bracket after the last if statement. I've added it above.
This: if(Count_pulses>=2656.25) {
Vice your original: if(Count_pulses>=2656.25)

Parsing a hex nr byte by byte

I'm trying to parse a hex number byte by byte, and concatenate to a string the representation of each byte, in the order they're stored in memory. (for a little test on endianness, but that's not important I guess).
Here is the code (please ignore the glaring unit-test issues with it :D; also, some of the code might look weird since initially the display_bytes method took in a char* not an int8_t*, but I thought using an int8_t might make it more obvious to me, what the issue is)
TEST_CLASS(My001littlebigendian)
{
public:
TEST_METHOD(TestMethod1)
{
int i = 0x12345678;
display_bytes((int8_t*)&i, sizeof(i));
}
void display_bytes(int8_t* b, int length)
{
std::stringstream ss;
for (int i = 0; i < length; ++i)
{
int8_t signedCharRepresentation = *(b + i); //signed char has 1 byte
int8_t signed8ByteInt = (int8_t)signedCharRepresentation; //this is not ok
int32_t signed32ByteInt = (int32_t)signedCharRepresentation; //this is ok. why?
//ss << std::hex << signed8ByteInt; //this is not ok. why?
ss << std::hex << signed32ByteInt; //this is ok
}
std::string stringRepresentation = ss.str();
if (stringRepresentation.compare("78563412") == 0)
{
Assert::IsTrue(true, L"machine is little-endian");
}
else if(stringRepresentation.compare("01234567") == 0)
{
Assert::IsTrue(true, L"machine is big-endian");
}
else
{
Assert::IsTrue(true, L"machine is other-endian");
}
}
};
Now, what I don't understand (as hopefull the comments make clear) is why does this only work when I cast each byte to a 4 byte int, and not an 1 byte int. Since I am working with chunks of 1 byte. Intuitively it would make me think doing it like this should cause some sort of overflow? But it seems not.
I've not dug deeper into why this is the issue yet, since I was hoping to not need to. And maybe if someone with more knowledge in this area can give me nudge in the right direction, or maybe even an outright answer if I'm missing something very obvious. (which I do feel I might be, since I'm not used to working at this low level).

send a list of data from python to arduino using pyserial

I want to turn ON and OFF, three LEDS connected to arduino, using a python GUI, thus I use pyserial. The state of the LEDs is described by a tuple
RGB = (Red On/Off, Green On/Off, Blue On/Off), e.g. (1,0,1)
Since pyserial.write() works only with strings, let's say that RGB = "101"
I want to sent to arduino the RGB string, split it to three values 1, 0 and 1, and set LOW or HIGH three output pins, depending on the incoming values.
The python code works fine:
import serial
ser = serial.Serial('/dev/ttyACM4', 9600)
ser.write('101')
This is the arduino code:
void setup() {
Serial.begin(9600);
Serial.println("Ready");
}
void loop() {
char input_data = ' ';
if(Serial.available()){
char input_data = {Serial.read()};
Serial.println(input_data);
}
delay(100);
}
The print line is only for inspection purpose.
Can I somehow split the input_data string, and retrieve its values, like:
int R = input_data[0];
int G = input_data[1];
int B = input_data[2];
Any suggestion would be appreciated.
OK, so one quick clarification here (as verified by your comment): input_data is not a string, it is a single char (one byte). Also, serial.read() returns a single byte (the next in the serial stream) and does so as an int, so you're implicitly casting int to char in your code. This is fine, just wanted to make that clear.
What you can do is make input_data an array of chars, and store each new byte in a new location in the array. Then you'll have all three numbers you're looking for. Then, all that's left is to turn input_data[i] into a LOW or HIGH value to be passed to each of your LED pins. What I would do is within your for loop, test each value read from the serial.read() function to see if it's value is '0' or '1' and then storing true or false in another array of bools. Finally, you can test that array and set the corresponding pin 'HIGH' or 'LOW' as necessary. Please keep in mind what I mentioned earlier in the comments, that you're going to want to test the received data as the characters 1 and 0 and not the numbers 1 and 0.
Here's a quick snippet as an example:
int[3] pins; //Initialize this to contain the numbers of the R, G, and B pins.
void setup() {
Serial.begin(9600);
Serial.println("Ready");
}
void loop() {
char[3] input_data;
bool[3] low_high;
if(Serial.available() >= 3){
for (int i=0; i<3; i++) {
input_data[i] = Serial.read();
if(input_data[i] == 48) //ASCII for 0
low_high[i] = false;
if(input_data[i] == 49) //ASCII for 1
low_high[i] = true;
//Serial.println(input_data);
}
}
for (int j=0; j<3; j++) {
if(low_high[j]) {
digital_write(pins[j],'HIGH');
} else {
digital_write(pins[j], 'LOW');
}
}
delay(100);
}
That should get you up and running. Please keep in mind that this example has very limited input checking, so you'll need to beef it up a bit (i.e. checking for more than 3 bytes ready, making sure you're getting char's and not gibberish, etc.).

while loop in arduino refuse to work (simple prog)

Im writing a basic program that take 20 mms and control the on and off of the small light in the motherboard.
But the it always shows me an error
Can please someone help me fix it?
void setup() {
pinMode(13, OUTPUT);
}
int cycle = 1;
int time_t=20;
int time_on = 0;
int time_off= (time_t-time_on);
int big=0.05;
while (cycle<=100) {
if (cycle%10==0) {
time_on=time_t*big;
time_off= time_t-time_on;
big=big+0.05;
}
digitalWrite(13, HIGH);
delay(time_on);
digitalWrite(13, LOW);
delay(time_off));
cycle++;
}
The arduino program syntax specifies that a program consists of two functions setup() and loop(). Hence the part to be repeated again and again must be written inside the loop function. So your code Will go inside a void function named loop. And it becomes :
void loop(){
while (cycle<=100) {
if (cycle%10==0) {
time_on=time_t*big;
time_off= time_t-time_on;
big=big+0.05;
}
digitalWrite(13, HIGH);
delay(time_on);
digitalWrite(13, LOW);
delay(time_off));
cycle++;
}
Arduino reference :
http://arduino.cc/en/Reference/Loop
i've not used this language yet but i see an issue with "int big=0.05;" you're trying to assign a floating point value to an integer. should it be a double, float or similar data type? and then inside the while loop you have
time_on=time_t * big;
once again you are trying to multiply an integer with a floating point value and assign that back to an integer. the result being the value of time_on will be some rounded version of time_t times big. the value of time_on will only change as the value of big approaches or equals 1. and the value of big will always equal 0. you need to change your big and time_on variables to floating point data types.

Resources