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.
Related
When using interrupts, any kind of digitalRead should be obsolete, or so i think. Let me explain my understanding of this to show my problem:
Say, a quadrature incremental rotary encoder (RE) is pulled up on both signal pins and is therefore idle HIGH. If i configure an interrupt service routine (ISR) on the falling edge on both pins and have the ISR for pin B do nothing more than set the flag "flag_B" to false (representing the logic state at the pin) and the ISR for pin A sets or resets a flag "flag_clockwise" depending on the state of flag_B during A's ISR, then i should get the right direction for every turning step, right? My only task is to then set the state of pin B to high again after the ISRs and my code should be ready for the next interrupt. But that doesn't work, with many random cases of the wrong direction being displayed or showing counter clockwise on the first half of the turning step when you can feel a little "bump" while turning the knob and then going clockwise on the second half, when the shaft has completed a single step, basically performing two opposite actions while only turning one step. Is my logic flawed?
All signals are debounced in hardware and look like expected on the oscilloscope.
ISRs:
//re-CLK signal
void IRAM_ATTR reCLK_ISR(void){
if(myFlags->pinBstate){
//this means the first interrupt of a turning step comes from the clock, not data
myFlags->direction = CCW;
}
else{
//this means the interrupt of a turning step occured from data first
myFlags->direction = CW;
}
}
//re-DATA signal
void IRAM_ATTR reDATA_ISR(void){
myFlags->pinDATAstate = false;
}
further processing:
//some function
//scroll = CW / CCW
if((myFlags->direction) != STOP){
if(myFlags->direction == CW){
//DEBUG
digitalWrite(PIN_LED_DEBUG, LOW);
}
}
if(myFlags->direction == CCW){
//DEBUG
digitalWrite(PIN_LED_DEBUG, HIGH);
}
myFlags->pinDATAstate = true;
myFlags->direction = STOP; //direction has 3 states: CW, CCW and STOP
}
the last 2 statements should restore all affected flags to the state before the encoder was touched, meaning ready for the next turn. The LED is just a replacement for different calculations which should be performed depending on the turning direction.
Even if the pin is assigned as interrupt, you can still use digitalRead() to determine the direction. For example:
const uint8_t pinA = 25;
const uint8_t pinB = 26;
volatile int counter;
void IRAM_ATTR isr(){
if(digitalRead(pinA) == digitalRead(pinB)) {
//Clockwise
counter++;
} else {
//Counter Clockwise
counter--;
}
}
You don't even need another ISR to determine the direction. I used this method to determine both the speed and the direction;
It is a homework and I have completely NO idea, my teacher says you need just while, analogWrite and a counter. I have a DC motor, a transistor and a 9V battery.
I know my code does NOTHING, but just as example.
int analogPin = 3;
int count = 0;
void setup()
{
pinMode(analogPin, OUTPUT);
}
void loop() {
while(count<30){
analogWrite(analogPin , 255);
delay(20000);
count++;
}
}
You need to use counter value as your analogue output value:
void loop()
{
if( count < 256 )
{
analogWrite( analogPin, count ) ;
delay( 20000 );
count++ ;
}
}
Note that you do not need a while loop; the Arduino framework already calls loop() iteratively (the clue is in the name). However if your teacher thinks you need one and is expecting one, you may need to use one just for the marks. Alternatively discuss it with your teacher, and explain why it is unnecessary
In fact the delay too is arguably bad practice - it is unhelpful in applications where the loop() must do other things while controlling the motor. The following allows other code to run whilst controlling the motor:
unsigned long delay_start = 0 ;
void loop()
{
if( count < 256 &&
millis() - delay_start >= 20000ul )
{
analogWrite( analogPin, count ) ;
count++ ;
delay_start = millis() ;
}
// Do other stuff here
}
Because the loop() now never blocks on the delay() function, you can have code that does other things such as read switch inputs and it can react to them instantly, whereas as in your solution, such inputs could be ignored for up-to 20 seconds!
A typical DC motor will not start moving at very low values - you may want to start count somewhat higher than zero to account for the "dead-band". Analogue signals are also generally a poor way to drive a DC motor and varying speed; a PWM is generally a more efficient method, and will allow the motor to run at lower speeds. With an analogue signal at low levels (lower than for PWM), your motor will not move and will just get warm and drain your battery.
For test purposes, reduce the delay time; you don't want to sit there for an hour and 25 minutes just to find the code does not work! Set it to say 500ms, then start it, time how long it takes before the motor starts to move. If that is say 30 seconds, then yu know the motor starts to move when count is about 60; in which case that is a better starting value that zero. Then you can increase your delay back to 20 seconds if you wish - though a DC power supply might be better than a battery - I'm not sure it will last that long.
Im trying to generate a knock knock detector using Arduino and a Piezo Buzzer. The one used in this proyect is the same as in this picture
Using this code
const int outputPin = 8; // led indicator connected to digital pin
const int knockSensor = A0; // the piezo is connected to an analog pin
const int thresholdHIGH =150; // threshold value to decide when the detected knock is hard (HIGH)
const int thresholdLOW = 120; // threshold value to decide when the detected knock is gentle (LOW)
const int secretKnockLength = 4; //How many knocks are in your secret knock
/* This is the secret knock sequence
* 0 represents a LOW or quiet knock
* 1 represents a HIGH or loud knock
* The sequence can be as long as you like, but longer codes increase the difficulty of matching */
const int secretKnock[secretKnockLength] = {0, 0, 1, 0};
int secretCounter = 0; //this tracks the correct knocks and allows you to move through the sequence
int sensorReading = 0; // variable to store the value read from the sensor pin
void setup() {
//Set the output pin as an OUTPUT
pinMode(outputPin, OUTPUT);
//analogWrite(knockSensor, LOW);
//Begin Serial Communication.
Serial.begin(9600);
}
void loop() {
// read the piezo sensor and store the value in the variable sensorReading:
sensorReading = analogRead(knockSensor);
Serial.print ("Valor del Sensor: ");
Serial.println(sensorReading);
// First determine is knock if Hard (HIGH) or Gentle (LOW)
//Hard knock (HIGH) is detected
if (sensorReading >= thresholdHIGH) {
//Check to see if a Hard Knock matches the Secret Knock in the correct sequence.
if (secretKnock[secretCounter] == 1) {
//The Knock was correct, iterate the counter.
secretCounter++;
Serial.println("Correct");
} else {
//The Knock was incorrect, reset the counter
secretCounter = 0;
Serial.println("Fail");
digitalWrite(outputPin, LOW);
}//close if
//Allow some time to pass before sampling again to ensure a clear signal.
delay(100);
//Gentle knock (LOW) is detected
} else if (sensorReading >= thresholdLOW) {
//Check to see if a Gentle Knock matches the Secret Knock in the correct sequence.
if (secretKnock[secretCounter] == 0) {
//The Knock was correct, iterate the counter.
secretCounter++;
Serial.println("Correct");
} else {
//The Knock was incorrect, reset the counter.
secretCounter = 0;
Serial.println("Fail");
}//close if
//Allow some time to pass before sampling again to ensure a clear signal.
delay(100);
}//close if else
//Check for successful entry of the code, by seeing if the entire array has been walked through.
if (secretCounter == (secretKnockLength) ) {
Serial.println("Welcome");
//if the sececret knock is correct, illuminate the LED for a couple seconds
digitalWrite(outputPin, HIGH);
//Reset the secret counter to 0.
secretCounter = 0;
}//close success check
}//close loop".
My problem is the buzzer is detecting nothing. I dont know if this is because the buzzer is not the correct one or something else.
Any idea?
If the problem is that the buzzer isn't making any noise, then you should be using the tone() function in your code. (https://www.arduino.cc/reference/en/language/functions/advanced-io/tone/)
Your code compiles successfully, so if that's not your issue, then there's probably something wrong with your project's wiring.
To detect knocking, you need to use piezo without driver circuit.
according to Arduino tutorial said that avoid piezo with plastic housing.
You should use piezo that look like metal disc like this
(image from sparkfun)
Note : piezo with plastic housing might contain drive circuit or not piezo at all (it might be magnetic buzzer)
unsigned long t;
boolean isHigh;
#define BUZZER_PIN 3
void setup() {
// put your setup code here, to run once:
pinMode(BUZZER_PIN, OUTPUT);
isHigh = false;
t = micros();
}
void loop() {
playNote('c');
}
void playNote(char note) {
unsigned long timeToWait;
unsigned long timeToPlayTheNote = millis();
while (timeToPlayTheNote - millis() < 1000) {
if (note == 'c') {
timeToWait = 1911;
}
if (micros() - t > timeToWait) {
if (!isHigh) {
digitalWrite(BUZZER_PIN, HIGH);
isHigh = true;
} else {
digitalWrite(BUZZER_PIN, LOW);
isHigh = false;
}
t = micros();
}
}
}
I don't know why this won't work. I used to play a frequency every 1,000 microseconds but is there any way to make this simpler as well? Also, with this method I have to do (1/f)/2 and then convert that value from seconds to microseconds and use that as the value for timeToWait.
Initialization of ˋtimeToWait` should obviously be outside of the loop.
An array could be used for timing data.
ˋt` should probably be initialized inside ˋplayNoteˋ
Alternatively, you might use an enum for delay associated to a note.
enum class notes
{
C = 1911
};
Well, all suggestion assume that you don't want to compensate for drifting offsets.
Buzzers have a fixed frequency. They don't work like speakers at all. You will get better results with a real speaker. Don't forget to put a capacitor in series with it so the speaker sees an AC signal, you can fry a speaker quite easily if you feed it a DC signal..
For best results, you should use 2 x 47uF to 100uF electrolytic capacitors back to back, with the negative poles joined together, one positive to the 'duino and the other positive pole connected to the speaker. With higher capacitance, you'll get more bass.
Why don't you use a PWM at 50% (128) and change the PWM frequency to generate the sound? You could use the Timer1 or Timer3 library for that. Letting the hardware do the work would be more presise and would free your application for other tasks, such as reading a keyboard.
https://playground.arduino.cc/Code/Timer1
Setting the PWM at 0% with an analogWrite() would cut the sound.
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.
}