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)
Related
Before I get started I want to make it clear I am still a beginner in both C++ and Arduino electronics, so the answer may be painfully obvious to someone with even a week more experience than me.
But to me, it is a total mystery. Thank you for any help.
To begin this is the code:
int potPosition;
int delayTime;
void setup() {
Serial.begin(9600);
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
pinMode(9, OUTPUT);
}
void loop() {
potPosition = analogRead(A0);
Serial.println(potPosition);
if (potPosition < 16) {
delayTime == 16;
} else if (potPosition >= 16){
delayTime == analogRead(A0);
}
digitalWrite(13, HIGH);
digitalWrite(12, HIGH);
digitalWrite(11, HIGH);
digitalWrite(10, HIGH);
digitalWrite(9, HIGH);
delay(delayTime);
digitalWrite(13, LOW);
digitalWrite(12, LOW);
digitalWrite(11, LOW);
digitalWrite(10, LOW);
digitalWrite(9, LOW);
delay(delayTime);
}
This is all written in the Arduino Genius IDE and I am outputting it to a Sparkfun electronics Arduino with 5 LEDs connected to it controlled by a potentiometer, which sets the on and then off delay by outputting a value I take as milliseconds of delay to make the LEDs flash. All the electronic wiring is correct, as when I remove the if statement and just run it as normal, everything works as I'd expect.
So you may be wondering, what's the issue? Well, currently when the potPosition exceeds a certain value - a delay value in ms - which is too low, the LEDs no longer appear to flash, as the delay is far to small for the flash to be visible. This is an expected outcome, as a delay of nearing 0ms is extremely small.
What I am trying to do with my if statement:
if (potPosition < 16) {
delayTime == 16;
} else if (potPosition >= 16){
delayTime == analogRead(A0);
}
is if the potPosition - the value of the dial which is the ms delay - goes below a certain number (the last value which is a long enough delay for a flashing effect to still be visible) set my delayTime back to that value and if the potPosition is that value or anything higher, then set the delayTime to whatever delay the potentiometer is outputting.
However, currently, when I download this code onto my Arduino all my lights appear dim (the same way they do when my delay time is extremely low) no matter what value my potentiometer is outputting, which is not what I want it to do, and I have no idea why my if statement is not resetting my delay time correctly.
I've never really coded C++ before starting on a Sparkfun electronics kit, and only ever used Python, so I may be missing a key aspect of programming in C++, but I have no idea what that would be.
Any help with this issue would be great, and I am more than happy to answer any questions.
This delayTime == analogRead(A0); and this delayTime == 16; are comparisons, i.e. delayTime remains uninitialised and you get undefined behaviour.
I bet you want to write a value there, as in delayTime = analogRead(A0); or maybe delayTime = potPosition; (same for the other ==). The latter seems more plausible to me, so as to only have one analog read per loop iteration.
I wanted to execute a simple loop in Arduino UNO at the same time, but I don't know what statement/code use to able to execute it at the same time.
I've tried the while loop and having a time include starting. But since the function is separated from one another in the loop. The execution of the led is the 1st function and 2nd function in different direction. But I want them to be executed at the same time.
void loop() {
// loop from the highest pin to the lowest:
for (int thisPin = 2; thisPin >= 0; thisPin--) {
// turn the pin on:
digitalWrite(ledPins[thisPin], HIGH);
delay(timer);
// turn the pin off:
digitalWrite(ledPins[thisPin], LOW);
}
// loop from the lowest pin to the highest:
for (int thisPin = 0; thisPin < pinCount; thisPin++) {
// turn the pin on:
digitalWrite(ledPins[thisPin], HIGH);
delay(timer);
// turn the pin off:
digitalWrite(ledPins[thisPin], LOW);
}
}
It turns out that the execution is executed in function from the highest pin to lowest pin. Then, the lowest pin to the highest pin. Instead of executed at the same time.
Arduino doesn't have the usual capabilities to run tasks in parallel (it doesn't have multithreading). There are however some workarounds. See https://arduino.stackexchange.com/questions/286/how-can-i-create-multiple-running-threads for more details.
Fortunately in your case you don't need to run the loops in parallel. You can rethink your algorithm in one loop by turning at the same time the pins at opposite sides. Something like this:
for (int i = 0; i < pinCount; ++i) {
// turn the pins on:
digitalWrite(ledPins[i], HIGH);
digitalWrite(ledPins[pinCount - i - 1], HIGH);
delay(timer);
// turn the pins off:
digitalWrite(ledPins[i], HIGH);
digitalWrite(ledPins[pinCount - i - 1], LOW);
}
If you want to do multiple things at the same time, this example here is very useful https://forum.arduino.cc/index.php?topic=223286.0 while they may technically be out of sync it would be on a tiny scale
Can someone please help me with code? I have a 24 teeth trigger wheel. Every tooth is registered by hall sensor and I need that Arduino simulate 36 pulse output of that corresponding 24 pulse input.
Here is my code with delayMicroseconds, but I can`t use delayMicroseconds, because Arduino doesn't understand bigger than 16k micros delay.
const int hall = 2; // hall sensor
const int ledPin = 13; // the pin that the LED is attached to
// Variables will change:
int teethCounter = 0;
int hallState = 0;
int lasthallState = 0;
long cycles=0;
boolean cycle = false;
unsigned long microsStart = 0;
unsigned long microsStop = 0;
unsigned long usElapsed = 0;
unsigned long usElapsedUp = 0;
unsigned long usInterval;
void setup() {
// initialize the button pin as a input:
pinMode(hall, INPUT);
// initialize the LED as an output:
pinMode(ledPin, OUTPUT);
// initialize serial communication:
Serial.begin(9600);
}
void loop() {
hallState = digitalRead(hall);
if(cycle==true){
microsStart=micros();
}
if(cycle==true){
usInterval = usElapsedUp/72;
for (int i=0; i <= 36; i++){
digitalWrite(13,HIGH);
delayMicroseconds(usInterval);
digitalWrite(13,LOW);
delayMicroseconds(usInterval);
cycle = false;
}
}
// compare the hallState to its previous state
if (hallState != lasthallState) {
// if the state has changed, increment the counter
if (hallState == HIGH) {
teethCounter++;
if(teethCounter==24){
cycle = true;
cycles++;
teethCounter=0;
usElapsedUp = usElapsed;
}
Serial.print("Tooth count: ");
Serial.print(teethCounter);
Serial.print(" Cycles: ");
Serial.print(cycles);
Serial.print(" Time: ");
Serial.print(usElapsedUp);
Serial.print(" Interval: ");
Serial.println(usInterval);
}
microsStop=micros();
usElapsed=microsStop-microsStart;
}
// save the current state as the last state,
//for next time through the loop
lasthallState = hallState;
}
How can I calculate and from where can I take trigger points?
If(event happens==true){
digitalWrite(13,HIGH);
}
If(event happens==false){
digitalWrite(13,LOW);
}
If it helps to understand here is a block diagram
As long as you understand that you will never be able get 36 pulses per turn accuracy with 24 pulse/turn, you can do this, which is a common trick derived from the Bresenham algorithm. This solution assumes you are concerned about the position.
Now, this will generate pulses in real-time, as opposed to your code, which generates pulses in a blocking manner, I don't think losing pulses was your original intent.
This code will not generate pulses uniformly, 1 out of 3 readings will generate 2 pulses.
Another way would be to calculate the average speed and program a hardware timer to simulate the 36 pulses per turn, using interrupts, but going that route would likely (invariably, in my experience) end up in total loss of sync between the actual position of the wheel and what your corrected tick count reports. There are also strict speed ranges that you have to respect if going that route, also this will introduce severe latency issues to your application.
Change the increment value to 36, and the whole turn count to 24/36.
Change the step detection to a threshold of 24.
I'm trying to understand why you want to do this 36/24 thing, and can't.
So, your mileage may vary.
// compare the hall State to its previous state
// to declared outside of loop()
// int smallCounter;
// PULSE_WIDTH as small positive pulse with in us
//
if (hallState != lasthallState) {
// if the state has changed, increment the counter
smallCounter += ((hallState == HIGH) ? 36 : 0);
// ... I'm assuming that the serial prints you had here were just here for debugging.
lasthallState = hallState;
}
//
// reporting for each step below
//
if (smallCounter >= 24)
{
smallCounter -= 24;
if (++teethCounter >= 36) {
cycle = true;
cycles++;
teethCounter=0;
usElapsedUp = usElapsed;
}
digitalWrite(13,HIGH);
delayMicroseconds(PULSE_WIDTH);
digitalWrite(13,LOW);
delayMicroseconds(PULSE_WIDTH); // this is probably not needed.
}
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);
So I was building this: http://www.instructables.com/id/Automatically-water-your-small-indoor-plant-using-/?ALLSTEPS
Problem: I set the water time to 5 min and the wait time to 1 hour, the water still kept running and hasn't stop after 5 min.
Is there another way to write this program?
The program he provide was
int motorPin = A0;
int blinkPin = 13;
int watertime = 300000; // how long to water in miliseconds
int waittime = 3600000; // how long to wait between watering
void setup()
{
pinMode(motorPin, OUTPUT);
pinMode(blinkPin, OUTPUT);
}
void loop()
{
digitalWrite(motorPin, HIGH);
digitalWrite(blinkPin, HIGH);
delay(watertime);
digitalWrite(motorPin, LOW);
digitalWrite(blinkPin, LOW);
delay(waittime);
}
Arduino UNO maximum integer value is 32767 (16-bit signed integer). So both watertime and waittime are too large to store in int variables. Try slowing the timebase by using delay(1000) to control a loop that runs once every second, then express watertime and waittime using seconds instead of miliseconds.
Incidentally, there is a stackoverflow site that specializes in Arduino: https://arduino.stackexchange.com/
Arduino UNO integers are 16bit wide, just use unsigned long variables or a #define.
#define watertime 300000
#define waittime 3600000
// some code...
delay(watertime);
// more code...
delay(waittime);