I am having trouble getting this loop to only run once. I have a flag set and my understanding is it will run through once, change flag = 1, then not run through again but when I execute it, the loop runs over and over again. Any help is appreciated.
EDIT: The issue I'm finding is even when my voltage satisfies the if statement, the loop continues to run.
voltage = analogRead(A0); //reads in voltage from pin A0
Serial.println(voltage);
//Calibration routine
do {
if ((voltage >= 1) && (voltage <= 10)) {
//while the voltage is between 4.88 and 48.8 mV the calibration light will flash once
//this ensures the voltage is above 0 and lower than the threshold for the max voltage routine
digitalWrite(calibrationLED, HIGH);
delay(2000);
digitalWrite(calibrationLED, LOW);
delay(1000);
digitalWrite(calibrationLED, HIGH);
delay(2000);
Serial.println("Calibrated");
delay(5000);
voltageInitial = analogRead(A0);
//stores the initial voltage to a separate variable, does not change over the course of the crimp
Serial.println("Initial Voltage: ");
Serial.println(voltageInitial);
flag = 1;
}
} while (flag == 0);
The flag variable will only get set to 1 if the if condition is true. This happens when voltage has a value from 1 to 10.
If the value of voltage is not in the range 1 - 10, flag will not be set. And since voltage is never modified inside of the loop, you have an infinite loop.
"the loop runs over and over again. "
It smells the loop never enter inside if ((voltage >= 1) && (voltage <= 10)) ,
thus never set flag = 1;
So naturally it keep running.
This is an infinite loop actually until the value of the voltage comes between 1 to 10. so flag=1 should be out of the if condition. Otherwise you can add a break after if condition is finished. It will execute exactly once after the condition.
You are waiting for the voltage variable to change, without actually changing it (reading from analog pin).
You need to add voltage = analogRead(A0); into your loop.
do
{
voltage = analogRead(A0);
if ((voltage >= 1) && (voltage <= 10)) //while the voltage is between 4.88 and 48.8 mV the calibration light will flash once
{ //this ensures the voltage is above 0 and lower than the threshold for the max voltage routine
...
flag = 1;
}
} while (flag == 0);
Related
I have a quick question and I think that a more experienced firmware developer will be able to help me out with. So the main goal I have with this code is:
Run a loop (detect) that will "listen" for something to happen and then increment a variable (voltscount in this case) once that threshold is hit. Once voltscount is hit twice within a specified time, the 1st while loop inside of the sampling loop will listen more closely (via a FFT).
I want to then break out of the while loop (stop listening with the FFT) when the threshold in the detect loop isn't hit in a certain time period (3 secs in this example).
Number 1 is achieve but I'm having trouble with number 2. The code seems to remain in the 1st while loop inside of sampling (the serial window keeps displaying the FFT values instead of 0,1 or 2 -voltscount value) well after 3 sec of that volts threshold remaining below 4.8.
I didn't include my void setup and all that jazz, let me know if that is needed in order to answer my question.
void loop(){
detect();
sampling();
}
void detect(){
unsigned long startMillis= millis(); // Start of sample window
unsigned int peakToPeak = 0; // peak-to-peak level
unsigned int signalMax = 0;
unsigned int signalMin = 1024;
// collect data for 50 mS
while (millis() - startMillis < sampleWindow)
{
sample = analogRead(0);
if (sample < 1024) // toss out spurious readings
{
if (sample > signalMax)
{
signalMax = sample; // save just the max levels
}
else if (sample < signalMin)
{
signalMin = sample; // save just the min levels
}
}
}
peakToPeak = signalMax - signalMin; // max - min = peak-peak amplitude
volts = (peakToPeak * 5.0) / 1024; // convert to volts
Serial.println(voltscount);
}
void sampling(){
unsigned long currentMillis = millis();
if(volts >=4.8){
voltscount++;
previousMillis = millis();
}
while(voltscount >= 2){
/////SAMPLING
for(int i=0; i<samples; i++)
{
microseconds = micros(); /* Overflows after around 70 minutes! */
vReal[i] = analogRead(0);
vImag[i] = 0;
while(micros() < (microseconds + sampling_period_us)){
}
}
double x;
double v;
FFT.MajorPeak(vReal, samples, samplingFrequency, &x, &v);
Serial.print(x, 0);
Serial.print(", ");
Serial.println(v, 0);
delay(10); /* Repeat after delay */
/*
// 3 tests for smoke detector chirps and code to light up the LEDs
if (x > 4000 and x < 4900 and v > 80) {
digitalWrite(blueLEDpin, HIGH);
digitalWrite(uvLEDpin, HIGH);
delay(blueLEDdelay);
digitalWrite(blueLEDpin, LOW);
delay(uvLEDdelay);
digitalWrite(uvLEDpin, LOW);
}
if (x > 1700 and x < 1800 and v > 40) {
digitalWrite(blueLEDpin, HIGH);
digitalWrite(uvLEDpin, HIGH);
delay(blueLEDdelay);
digitalWrite(blueLEDpin, LOW);
delay(uvLEDdelay);
digitalWrite(uvLEDpin, LOW);
}
*/
if (v > 1400) {
digitalWrite(blueLEDpin, HIGH);
digitalWrite(uvLEDpin, HIGH);
delay(blueLEDdelay);
digitalWrite(blueLEDpin, LOW);
delay(uvLEDdelay);
digitalWrite(uvLEDpin, LOW);
}
if (currentMillis - previousMillis > 3000){
voltscount = 0;
break;
}
}
}
Assume the state where voltscount is 1. You enter sampling(). You first set the current mills to the current time. Assume it's 1000 (Hypothetical since I don't know what that function returns). Then since your volts is greater than 4.8 you increment voltscount to 2. And you set the previousMillis to something like 1500 because millis() is called for the second time. Then you enter the first while loop and at the end of it, you check if currentMillis - previousMillis > 3000. But this is never true since previouslMillis have a greater value than currentMillis. I guess what you could do is to read the currentmillis right before you check if the difference is greater than 3000.
if (millis() - previousMillis > 3000){
voltscount = 0;
break;
}
In Arduino programming, most beginner's while loops are simply wrong.
Especially, if you feel the need to break them somehow.
The Arduino environment provides a loop() function prototype, which you should use properly.
Typically, any while() {...} can be replaced by an if() {...}, which is called again in the next loop() run. Sometimes, you need a few more state variables, but the important change is to follow the paradigm that loop() should not take noticeable time to run and should not describe sequential actions by listing them one after the other in loop().
The basic BlinkWithoutDelay sample applies everywhere, not only to blinking. The important part is not only calling millis(), but keeping the state of your sketch in appropriate variables (like those previousmillis)
Can someone explain how to modify this program to "ReadAnalogVoltage" once every 60 seconds, forever.
Right now it reads the voltage every millisecond (I think), forever.
Too much information is being generated.
Any suggestions would be appreciated.
Rob.
> /*
ReadAnalogVoltage
Reads an analog input on pin 0, converts it to voltage, and prints
the result to the serial monitor.
Attach the center pin of a potentiometer to pin A0, and the outside
pins to +5V and ground.
This example code is in the public domain.
*/
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}
// the loop routine runs over and over again forever:
void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(A0);
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
float voltage = sensorValue * (5.0 / 1023.0);
// print out the value you read:
Serial.println(voltage);
}
There are several ways to lower the sampling rate. Place a counter inside your loop and only print when the counter reaches a certain value. Be sure to reset the value after printing.
int counter = 0;
void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(A0);
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
float voltage = sensorValue * (5.0 / 1023.0);
// print out the value you read:
if(counter >= 1000000){
Serial.println(voltage);
counter = 0;
}
}
The best way to solve this is with an time interrupt. The actual mechanics of setting the interrupt will depend on what hardware you are using.
Really simple question but I'm not entirely sure how to incorporate a for loop in the if statement I have. Context: I have a humidifier I am trying to automate based on the humidity of the room. I'm using an ardiuno, dht11 humidity sensor and a servo. The humidifier knob has three settings (high low off) and so the servo has three positions. I have the code running so the servo turns appropriately according to the humidity level. The issue is that it fluctuates very easily. To correct that I'm looking to incorporate a for loop so that after let say 60 one second iterations of the humidity being greater than 55 the servo moves. I tried to add a for loop but it doesn't seem to be working.
But this is only my solution based on the little programming I know. If there is a better solution or even an equally viable alternative I'd love to know. I'm currently studying mechanical engineering but I'm finding that to really make something one needs a background in electronics and code. I'm trying to learn both independently through a series of projects and so I'm quite eager to learn. Hopefully this helps explain why I'm asking such a simple questions to begin with.
#include <dht.h>
#include <Servo.h>
Servo myservo;//create servo object to control a servo
dht DHT;
#define DHT11_PIN 7 // pin for humidity sensor ( also measure temp)
void setup() {
myservo.attach(9);//attachs the servo on pin 9 to servo object
myservo.write(0);//statting off position at 0 degrees
delay(1000);//wait for a second
Serial.begin(9600);
}
void loop() {
int chk = DHT.read11(DHT11_PIN); // the follow is just so that I can see the readings come out properly
Serial.print("Temperature = ");
Serial.println(DHT.temperature);
Serial.print("Humidity = ");
Serial.println(DHT.humidity);
delay(500);
if (DHT.humidity > 55) // here is where my code really begins
{
for (int i=0; i>60; i++); // my goal is to execute the follow code after the statement above has been true for 60 one second iterations
{
myservo.write(0);//goes to off position
delay(1000);//wait for a second
}
} else if (DHT.humidity > 40 ) {
for (int i=0; i>60; i++); // same thing here
myservo.write(90);//goes to low position
delay(1000);//wait for a second
}
else
{
for (int i=0; i>60; i++);
myservo.write(180);//goes to high position
delay(1000);
}
} // end of void loop()
Just addressing your question, the following line is incorrect:
for (int i=0; i>60; i++);
Two things:
1) The second statement in the for loop describes the conditions on which it executes. The way it is written, it will only execute when i>60 (not what you want according to the comments).
2) The semicolon after the for statement makes the next block unassociated.
Correct that line to the following:
for (int i=0; i<60; i++)
See the following for more information:
https://www.tutorialspoint.com/cprogramming/c_for_loop.htm
It would probably be helpful to examine your compiler warnings, and/or set a higher warning level, to catch these type of things early (this is, of course, somewhat compiler dependent).
I guess you trying kind of de-bouncing at you need humid level stay in same range for some period.
First, I define conversion function to map humid level to state
#define HUMID_OFF 1
#define HUMID_LOW 2
#define HUMID_HIGH 3
byte state_conv (float humid_level){
if (humid_level > 55) return HUMID_OFF ;
else if (humid_level > 40 ) return HUMID_LOW ;
else return HUMID_HIGH ;
}
Second I will check changing of state and use millis() to count time while current state is steady. if counting time are longer than threshold then change the actual state.
/*Global variable*/
byte actual_state;
byte flag_state;
void setup (){
// Do things that necessary
float humid = dht.readHumidity();
/*Initialize value*/
actual_state = state_conv(humid);
flag_state= state_conv(humid);
}
void loop(){
static unsigned long timer = millis();
float humid = dht.readHumidity();
byte crr_state = state_conv(humid);
if (crr_state != actual_state ){// if state is changing
if (flag_state != crr_state){
/if crr_state change form last iteration then reset timer
flag_state = crr_state;/
timer = millis();
}
else if (millis() - timer > 10000){
//if crr_state not change for 10000 ms (10 second)
actual_state = crr_state; // update actual state to crr_state
}
}
// After this use actual_state to control servo
if (actual_state == HUMID_OFF ){
myservo.write(0);//goes to off position
}
else if (actual_state == HUMID_LOW ){
myservo.write(90);//goes to low position
}
else if (actual_state == HUMID_HIGH ){
myservo.write(180);//goes to high position
}
}
DHT.humidity returns a float, so if you want to compare, then first store this in an int, and then compare.
Inside this function, I want the user to able to choose how many times he/her wants to repeat a test. I translate the ASCII dec (from serial.read), with char(incomingByte), but as soon as I enter the for-loop the number is changed back to its dec value... Can you explain why?
Serial.println("Choose number of times (max 10) to repeat test : ");
while(Serial.available() == 0) {
delay(10);
}
int incomingByte = Serial.read();
// Number of times to repeat test chosen by user.
nRepeat = char(incomingByte);
Serial.print("You chose : ");
Serial.println(char(nRepeat));
for(int i=0; i<nRepeat; i++) {
randomSeed(A1);
// Assigning a random seed for the random function.
timer = random(2000, 5000);
// Sets the random timer to vary between 2000 and 5000 ms
delay(timer);
// The delay is now random between 2000 and 5000 ms
digitalWrite(LED, HIGH);
// Turn on the LED (pin 13)
startTid = millis();
// Saves the current time the Arduino has been powered.
while(digitalRead(Buttom) == HIGH) {
// Loop until buttom is pressed
}
stopTid = millis();
// Saves current time since arduino got powered
digitalWrite(LED, LOW);
// Turns LED off
Serial.print("Your time was: ");
Serial.print(stopTid-startTid);
// Prints the time between the exercise started and finished
Serial.println(" milli seconds");
person[cc].reacTime[i] = stopTid-startTid;
Serial.print(i);
Serial.print(" out of ");
Serial.println(nRepeat);
delay(1000);
}
As #iharob has pointed out, char() in C is not a function for translation. The easiest way in C to do the ASCII transformation you are looking for is to subtract '0' from the entered value. This relies on the fact that the numbers are in order in the ASCII table. However, you will want to ensure that the user's input is actually a digit. Your code will also only allow repeats from 0-9.
nRepeat = incomingByte - '0';
if (nRepeat >= 0 && nRepeat <= 9) {
// Valid digit entered, proceed
//...
}
When you Serial.read() you get the ASCII value of the number written by the user, i.e. 0 (zero) = 49.
If you want to get the int value of the numbers written, you can use Serial.parseInt(). You can then make a if statement to make sure the number is between 0 and 10.
More documentation about Serial.parseInt() : http://arduino.cc/en/Reference/ParseInt
Can you give some examples of situations where a while loop and a if loop would be appropriate?
I am working on this project where an Arduino reads an analog input from a variable resistor.
This is how I have it read the raw input:
int intputValue = analogRead(A0);
Then I convert the raw input into a number between 0 and 100 for percentage:
double percentValue = inputValue * (1.0/10.23);
So then I use this percentValue to determine whether the Arduino needs to send signal(s) through several of its digital pins. I have signals going to a 4 channel relay module. Basically my idea is that if the percentValue is between 0-25, one of the relays would turn on, hence only one digital pin would need to be activated. Between 26-50, two pins, 51-75, three pins, and 76-100, four pins.
Here's my question: Should I use a if statement:
if(percentValue >=0 && percentValue <=25){
digitalWrite(pin1, HIGH); //turns on pin 1
}
Or use a while loop:
while(percentValue >= 0 && percentValue <=25){
digitalWrite(pin1, HIGH); //turns on pin 1
}
Then I'm going to do a similar thing for the rest of the percentValue ranges.
Is there a difference between using "if" and "while" in this case?
Thanks for any help.
There should be a setup and loop function in your code, you can put if in your loop function.
void setup() {
// put your setup code here, to run once:
int intputValue = analogRead(A0);
}
void loop() {
// put your main code here, to run repeatedly:
double percentValue = inputValue * (1.0/10.23);
if(percentValue >= 0 && percentValue <= 25){
digitalWrite(pin1, HIGH); //turns on pin 1
}
}
While loops are used to run a specific code block as long as certain parameters are met. An if statement is similar but it will only run the said code block once but a while statement will run until told otherwise.
So effectively:
while(1 == 1)
{
System.out.println("Hello World");
}
Will print Hello World indefinitely. On the other hand:
if(1 == 1)
{
System.out.println("Hello World");
}
Will print Hello World once.
Just for fun since your understanding of loops is shady; a for loop will run a specified number of times:
for(int i = 0; i < 3; i++)
{
System.out.println("Hello World");
}
Would print Hello World 3 times.
refer to:
While loop
For loop
If statement
General Java Tutorials
"Then I'm going to do a similar thing for the rest of the percentValue ranges."
This implies you should use an if statement, and not a while loop, especially if you want to do anything else with the device.
Presumably, this code will be placed in the Arduino loop() function, which is called repeatedly, giving you a loop. You don't want the Arduino to get stuck in a while loop of your own.
It appears that you want to light up different LEDs depending on the reading. You will want to turn off the other LEDs in the body of your if statements as well. Otherwise, the Arduino will just eventually have all 4 LEDs lit up.
if(percentValue >=0 && percentValue <=25){
digitalWrite(pin1, HIGH); //turns on pin 1
digitalWrite(pin2, LOW);
digitalWrite(pin3, LOW);
digitalWrite(pin4, LOW);
}
// etc.