Arduino Buzzer for Knock Knock project is not working - c++

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)

Related

How to read a rotary encoder with interrupts on ESP32 and global flags

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;

Issues creating light sequence with pause button ARDUINO

So I am trying to create a program that runs various light sequences using an Arduino board. I am not very familiar with C++ so I feel that my lack of syntax knowledge is preventing me from accomplishing my goal.
My idea is to have various light sequences that are played depending on what button is pressed on an infrared remote. The amount of time that a light is on or off may or may not be the same as that of another light. Unfortunately, the sequences do not seem to be working which indicates that the issue is likely with the toggleLight function.
I also wanted there to be a button that allows the user to pause the sequence (no matter where along the sequence it is). And once the button is pressed again, then the sequence continues where it left off. I read something about an interrupter and sleep mode using C++ but I am unfamiliar with this so it is not included in my code.
The rest of my goals I have included within my code. I created an object "light" because I was trying to create the sequence without using the delay function as the interrupt does not work well with the delay function (as far as I am aware). Then I am able to call the function toggleLight on the specific light object. However, as I previously stated the sequences are not working.
I am more familiar with OOP languages so perhaps creating an object is not the best for Arduino. If anyone has any ideas for how I can accomplish my goal it would be much appreciated.
unsigned long previousMillis = 0;
unsigned long currentMillis = millis();
//sets up the Infrared receiver for the remote controller
const int RECV_PIN = 1;
IRrecv irrecv(RECV_PIN);
decode_results results;
//Sets up the LED's pin number and light status
class light{
private:
int _pinNumber;
byte _Lstatus;
public:
//function that creates the objects with the parameters
light(int pinNum, byte Lstat){
_pinNumber = pinNum;
_Lstatus = Lstat;}
void toggleLight(long interval){
//ensures that the "interval" amount of time has passed between the last LED blink
if ((currentMillis - previousMillis) >= interval) {
// if the LED is off turn it on and vice-versa:
if (_Lstatus == LOW) {
_Lstatus = HIGH;
} else if (_Lstatus == HIGH) {
_Lstatus = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(_pinNumber, _Lstatus);
// save the last time you blinked the LED
previousMillis = currentMillis;
}
}
};
//creating four light objects with the pins and initial status of off.
light J1(13, LOW);
light J2(11, LOW);
light P1(12, LOW);
light P2(10, LOW);
void setup()
{
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
}
//Sequence 1 is a mock sequence. It will probably be longer and the intervals would not all be 1000
void seq1(){
currentMillis = millis();
J1.toggleLight(1000);
P1.toggleLight(1000);
J1.toggleLight(1000);
P1.toggleLight(1000);
J2.toggleLight(1000);
P2.toggleLight(1000);
}
void seq2(){
currentMillis = millis(); //I found that placing currentMillis = millis() here allows the lights to at least turn on
J1.toggleLight(2000);
J1.toggleLight(10);
P1.toggleLight(1000);
P1.toggleLight(0);
J2.toggleLight(1500);
J2.toggleLight(1);
P2.toggleLight(2000);
P2.toggleLight(1);
}
void loop()
{
/* So once i get the sequences working, my idea is to have different sequences play
depending on what button on the remote is pressed. However, first I want to get the sequences and
interrupter
For now I have just tried running sequence #2 but it does not work.. Neither does Sequence #1. */
seq2();
//I MADE THESE LINES OF CODE COMMENTS FOR NOW. UNTIL I GET THE REST OF THE PROGRAM WORKING
/*if (irrecv.decode(&results))
{
switch (results.value) {
case : 0xf00000 // When button #1 sequence one plays
seq1();
break;
case : 0xf00000 // when button #2 is pressed, sequence two plays
seq2();
break;
*/
}
I can see that you haven't yet got around to understanding the flow of code inside the Arduino. Here's a quick tutorial to get you up and running.
Basically, your code is not being processed sequentially. So when the processor is moving through seq() and gets to the first if statement in 'J1.toggleLight(2000);' and finds that the condition is not met (i.e. the 2000 milliseconds have not yet passed,) it immediately moves on to the second line in seq(). The issue with this is that when it does meet the condition at the second line it will reset the 'previousMillis' value which all the lights depend on.
Thus you should dedicate a clock for each light by moving in 'previousMillis' into the class as a member.
class light{
private:
int _pinNumber;
byte _Lstatus;
unsigned long previousMillis = 0;
This way each light will be able to keep track of its own time without conflicting with the other lights. This will lead to your lights flickering at a frequency of 1/interval which I doubt is what you want.(it wont even do that with each light getting toggled twice in the seq() functions. Try removing the second calls and see hat happens) This brings me to the second issue.
The second issue is with the way you set up your sequences. Since you want to the lights to operate sequentially you'll need to find the accumulated time which crosses out the chance of using a class, unless you're willing to implement a scheduling mechanism that takes account the interval and arrangement of each instance. Seems a bit impractical imo.
I think you should ditch the class and use a simple function that that accepts the eight values that each sequence requires. Then you could utilize if statements to manipulate the states of the lights.
I hope the answer made some sense.
Good luck.

C++ buzzer to play piano notes for an Arduino

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.

Arduino C++ calculating trigger points

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.
}

For loop with if statement for ardiuno powered humidity control

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.