Arduino Asked on November 25, 2021
Totally new to Arduino, 3 weeks old!
I am doing a project with 2 inputs (2 x ultrasonic sensors) and 2 outputs (buzzer and send SMS)
The code to run the buzzer and sms works perfectly individually. For the buzzer, it will stop buzzing after approx 5 seconds.
I would love them to run concurrently but it didn’t work as planned when I upload the code on the board.
I’ve read on forums to use millis / blinkwithoutdelay but am unsure how I can incorporate those functions on my mode as the examples posted online are different from my project.
The following is the code I have created.
Would appreciate if I can get some help from the experienced folks here.
#define TRIG A0 //Module pins
#define ECHO A1
#define TRIG1 A2 //Module pins
#define ECHO1 A3
#define Buzzerpin 13
int ctn = 0;
void setup() {
Serial.begin(9600); // Serial monitoring
pinMode(TRIG, OUTPUT); // Initializing Trigger Output and Echo Input
pinMode(ECHO, INPUT_PULLUP);
pinMode(TRIG1, OUTPUT); // Initializing Trigger Output and Echo Input
pinMode(ECHO1, INPUT_PULLUP);
pinMode(Buzzerpin, OUTPUT);
}
void loop() {
digitalWrite(TRIG, LOW); // Set the trigger pin to low for 2uS
delayMicroseconds(2);
digitalWrite(TRIG, HIGH); // Send a 10uS high to trigger ranging according to specs
delayMicroseconds(20);
digitalWrite(TRIG, LOW); // Send pin low again
delayMicroseconds(2);
digitalWrite(TRIG1, LOW); // Set the trigger pin to low for 2uS. Give a short LOW pulse beforehand to ensure a clean HIGH pulse
delayMicroseconds(2);
digitalWrite(TRIG1, HIGH); // Send a 10uS high to trigger ranging according to specs
delayMicroseconds(20);
digitalWrite(TRIG1, LOW); // Send pin low again
delayMicroseconds(2);
int distance = pulseIn(ECHO, HIGH, 26000); // Read in times pulse
int distance1 = pulseIn(ECHO1, HIGH, 26000); // Read in times pulse
distance = distance/58;
distance1 = distance1/58;
if (distance < 25) {
Serial.print("r");
delay(1000);
Serial.print("AT+CMGF=1r");
delay(1000);
/*Replace XXXXXXXXXX to 10 digit mobile number & ZZ to 2 digit country code*/
Serial.print("AT+CMGS="+YYXXXX"r"); // YY is the country code XXX is the number
delay(1000); //
//The text of the message to be sent.
Serial.print("HELLO There");
delay(1000);
Serial.write(0x1A);
delay(1000);
}
else {}
if (distance1 < 25 && ctn < 150) {
ctn += 1;
digitalWrite(Buzzerpin, HIGH);
}
else {
digitalWrite(Buzzerpin, LOW);
}
if (distance1 > 25 && ctn >= 150) {
ctn = 0;
digitalWrite(Buzzerpin, HIGH);
}
}
Using the blinkwithoutdelay approach is not incorporating functions, it is a coding style.
The idea is that you write your loop code to pass through without any delays, or with only very tiny delays. On each pass through the loop, you see how much time has passed, and if it's been 5 seconds since you started the buzzer, you turn off the buzzer.
You can probably get away with the microsecond delays that you use in your ultrasonic sensor code. Those delays are so small that they will only cause very short pauses in your app.
The delay(1000)
calls in your loop are not going to work. Each one of those causes EVERYTHING to stop for a full second. You won't check the ultrasonic sensor, you won't change the buzzer state, you won't do ANYTHING, for 5 seconds, once you enter the body of that if
statement. Those delay calls have to go.
Answered by Duncan C on November 25, 2021
First thing: you should call pulseIn()
right after sending the TRIG
pulse. If you wait too much, you will miss the start of the echo pulse.
For instance, here:
int distance = pulseIn(ECHO, HIGH, 26000);
int distance1 = pulseIn(ECHO1, HIGH, 26000);
the second pulseIn()
is starting too late, because of the time taken
by the first one.
Next, the general approach for doing things is a non-blocking fashion (and thus being able to handle concurrent tasks) is to program each task in the form of a finite state machine. Combine this with the timing technique used in the “Blink without delay” Arduino tutorial if you need time-triggered actions.
Here I would use one state machine for sending the SMS and another one for the buzzer. The first one is the most complex:
Edit: I expanded on how I designed this state machine.
In a canonical finite state machine implementation, each action is associated with a state transition. We then have a transition for each command sent to the GSM module, and we can label the states according to which commands have already been sent, like this:
SENT_NOTHING → SENT_CR → SENT_CMGF → SENT_CMGS → SENT_MSG → SENT_ALL.
All of the above transitions send a string to the GSM module. The first
one (SENT_NOTHING → SENT_CR) is triggered by the distance detected
being less than 25 cm. The following ones are time-triggered one
second after the previous one. We have to add one final transition
(SENT_ALL → SENT_NOTHING) that performs no action but lets the system
“forget” it has sent an SMS and thus makes it ready to send a new one.
This transition would also be time-triggered, after a potentially long
delay (how often would the user like to receive those reminders?). In
the code below, this last delay is one second, which I believe is too
short, but matches the last delay(1000)
in the code you posted. The
implementation would be like this:
static enum {
SENT_NOTHING, SENT_CR, SENT_CMGF, SENT_CMGS, SENT_MSG, SENT_ALL
} sms_state = SENT_NOTHING;
static uint32_t time_last_command_sent;
uint32_t now = millis();
switch (sms_state) {
case SENT_NOTHING:
if (distance < 25) {
Serial.print("r");
time_last_command_sent = now;
sms_state = SENT_CR;
}
break;
case SENT_CR:
if (now - time_last_command_sent >= 1000) {
Serial.print("AT+CMGF=1r");
time_last_command_sent = now;
sms_state = SENT_CMGF;
}
break;
case SENT_CMGF:
if (now - time_last_command_sent >= 1000) {
Serial.print("AT+CMGS="+YYXXXX"r");
time_last_command_sent = now;
sms_state = SENT_CMGS;
}
break;
// and so on for the cases SENT_CMGS and SENT_MSG...
case SENT_ALL:
if (now - time_last_command_sent >= 1000) {
sms_state = SENT_NOTHING; // forget we sent an SMS
}
break;
}
I have an issue with this implementation though: it is overly
repetitive, as the cases SENT_CR, SENT_CMGF, SENT_CMGS and SENT_MSG
are essentially copies of the same code. In order to make the code
dryer, I prefer merging them into a single state, and splitting
the state information into two variables: sms_state
and
commands_sent
. The new possible values for sms_state
are now:
The mapping to the “full” machine states is then:
full state │ sms_state commands_sent
───────────────┼───────────────────────────
SENT_NOTHING │ SMS_READY 0
SENT_CR │ SMS_SENDING 1
SENT_CMGF │ SMS_SENDING 2
SENT_CMGS │ SMS_SENDING 3
SENT_MSG │ SMS_SENDING 4
SENT_ALL │ SMS_DONE 5
It could be argued that sms_state
is redundant, as all the state
information is contained in commands_sent
. I still wanted to keep
sms_state
in order to easily switch
on it. This is not the only
possible approach though, and it would be perfectly sensible to write
something like
if (commands_sent == 0) {
// handle the case SMS_READY
} else if (commands_sent < command_count) {
// handle the case SMS_SENDING
} else {
// handle the case SMS_DONE
}
For the buzzer, there are only two states: BUZZER_OFF and BUZZER_ON. The transitions are conditioned by the measured distance and, for the ON → OFF transition, also by the time it has been ON.
Here is my tentative, untested implementation of this approach:
#define TRIG A0 //Module pins
#define ECHO A1
#define TRIG1 A2 //Module pins
#define ECHO1 A3
#define Buzzerpin 13
// Commands to be sent to the GSM module.
const int command_count = 5;
const char * const commands[command_count] = {
"r",
"AT+CMGF=1r",
"AT+CMGS="+YYXXXX"r",
"HELLO There",
"x1a"
};
void setup() {
Serial.begin(9600);
pinMode(TRIG, OUTPUT);
pinMode(ECHO, INPUT_PULLUP);
pinMode(TRIG1, OUTPUT);
pinMode(ECHO1, INPUT_PULLUP);
pinMode(Buzzerpin, OUTPUT);
}
void loop() {
// Measure both distances.
digitalWrite(TRIG, LOW);
digitalWrite(TRIG, HIGH);
delayMicroseconds(20);
digitalWrite(TRIG, LOW);
int distance = pulseIn(ECHO, HIGH, 26000) / 58;
digitalWrite(TRIG1, HIGH);
delayMicroseconds(20);
digitalWrite(TRIG1, LOW);
int distance1 = pulseIn(ECHO1, HIGH, 26000) / 58;
// Timing for both state machines.
uint32_t now = millis();
// Send the SMS.
static enum {SMS_READY, SMS_SENDING, SMS_DONE} sms_state;
static uint32_t time_last_command_sent;
static int commands_sent = 0;
switch (sms_state) {
case SMS_READY:
if (distance < 25) {
Serial.print(commands[commands_sent++]);
time_last_command_sent = now;
sms_state = SMS_SENDING;
}
break;
case SMS_SENDING:
if (now - time_last_command_sent >= 1000) {
Serial.print(commands[commands_sent++]);
time_last_command_sent = now;
if (commands_sent >= command_count) {
sms_state = SMS_DONE;
}
}
break;
case SMS_DONE:
if (now - time_last_command_sent >= 1000) {
commands_sent = 0;
sms_state = SMS_READY;
}
break;
}
// Drive the buzzer.
static enum {BUZZER_OFF, BUZZER_ON} buzzer_state;
static uint32_t time_buzzer_started;
if (buzzer_state == BUZZER_OFF && distance1 < 25) {
digitalWrite(Buzzerpin, HIGH);
buzzer_state = BUZZER_ON;
time_buzzer_started = now;
} else if (buzzer_state == BUZZER_ON && distance1 >= 25
&& now - time_buzzer_started >= 2000) {
digitalWrite(Buzzerpin, LOW);
buzzer_state = BUZZER_OFF;
}
}
Answered by Edgar Bonet on November 25, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP