For my project I am trying to messure the distance between my sensor and an object to trigger an alarm. If the alarm goes off, a message will be sent to my Telegram account with a library (UniversalTelegramBot). For messuring th distance, I'm using a loop to the distance between the sensor and the object, but to check if I get any messages through I also need to use a loop. I want to make it so that with the comment "/off" the distance get messured again, but when you type something else it waits till the comment "/off" gets used again. While the alarm is on and it's checking for messages, I want to use "/start" as comment to see which other comments to use and how to use it, without going to the messure loop again.
My main problem is that when the alarm goes off and I type anything, it doesn't matter what, the loop starts messuring again, but I only want it to go back to messuring when I type "/off" in telegram.
My code:
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>
UniversalTelegramBot bot(TELEGRAM_BOT_TOKEN, client);
int delayBetweenChecks = 250;
unsigned long lastTimeChecked;
void handleNewMessages(int numNewMessages) {
for (int i = 0; i < numNewMessages; i++) {
String chat_id = String(bot.messages[i].chat_id);
String text = bot.messages[i].text;
String from_name = bot.messages[i].from_name;
if (from_name == "")
from_name = "Gast";
Serial.print("De volgende knop is ingedrukt: ");
Serial.println(text);
if (text == F("/off")) {
digitalWrite(LED_PIN, LOW);
alarmStatus = 0;
noTone(buzzer);
}
if (text == "/start") {
String welcome = "Welkom bij jouw alarmsysteem, " + from_name + ".\n";
welcome += "/off : om het alarm uit te zetten\n";
bot.sendMessage(chat_id, welcome, "Markdown");
}
}
}
void loop() {
digitalWrite(trigPin, LOW);
delayMicroseconds(5);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
pinMode(echoPin, INPUT);
duration = pulseIn(echoPin, HIGH);
// Convert the time into a distance
cm = (duration / 2) / 29.1; // Divide by 29.1 or multiply by 0.0343
inches = (duration / 2) / 74; // Divide by 74 or multiply by 0.0135
Serial.print(inches);
Serial.print("in, ");
Serial.print(cm);
Serial.print("cm");
Serial.println();
while (cm <= 9) {
Serial.println("ALARM!");
digitalWrite(LED_PIN, HIGH);
tone(buzzer, 250);
alarmStatus = 1;
if (alarmStatus = 1) {
if (millis() > lastTimeChecked + delayBetweenChecks) {
int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
handleNewMess ages(numNewMessages);
if (numNewMessages) {
Serial.println("got response");
handleNewMessages(numNewMessages);
}
lastTimeChecked = millis();
}
}
break;
}
delay(250);
}
If I understand it correctly, you could keep two separate state variables. One for turning the alarm on and off when you leave/return home and one for when triggering the alarm.
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>
const int delayBetweenChecks = 250;
bool alarmStatus = false; // off by default
bool alarmTriggered = false; // off by default
UniversalTelegramBot bot(TELEGRAM_BOT_TOKEN, client);
double measure_distance_in_cm() {
digitalWrite(trigPin, LOW);
delayMicroseconds(5);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
pinMode(echoPin, INPUT);
unsigned long duration = pulseIn(echoPin, HIGH);
// Convert the time into a distance
return static_cast<double>(duration) / (29.1 * 2.);
}
void trigger_on() {
digitalWrite(LED_PIN, HIGH);
alarmTriggered = true;
}
void trigger_off() {
digitalWrite(LED_PIN, LOW);
alarmTriggered = false;
}
void handleNewMessages() {
int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
for (int i = 0; i < numNewMessages; i++) {
String text = bot.messages[i].text;
Serial.println(text);
if (text == "/start") { // when you're leaving home
alarmStatus = true;
trigger_off();
}
else if (text == "/stop") { // when you're returning home
alarmStatus = false;
trigger_off();
}
else if (text == "/off") { // turn off a triggered alarm
trigger_off();
}
else if (test == "/on") { // force triggering of the alarm
trigger_on();
}
}
}
void loop() {
static unsigned long lastTimeChecked = millis();
handleNewMessages();
if(alarmStatus) { // you only need to do this if the alarm is on
if(alarmTriggered == false) { // only measure if the alarm isn't already triggered
double cm = measure_distance_in_cm();
if(cm <= 9.) {
Serial.println("ALARM!");
trigger_on();
}
}
// If it's triggered: Sound the alarm in 200ms bursts until /off or /stop is received
if(alarmTriggered) tone(buzzer, delayBetweenChecks-50);
}
// try to keep the polling at a steady pace
lastTimeChecked += delayBetweenChecks;
unsigned long now = millis();
if(lastTimeChecked > now) delay(lastTimeChecked - now);
}
Disclaimer: I don't have an Arduino so I can't test it myself.
Related
I am working on two way communication between arduino and android phone. Currently everything is working, however I have couple of issues I have been trying to solve recently.
How I can ignite ignition for 5 seconds? I mean if IgnitionPin is on HIGH, run it for 5 seconds then automatically turn off? There is an easy way with delay, but it will not work in my case as don't want any other delays to slow up my script.
I am using Arduino Uno. I want to start my Arduino with pin in OFF position. Why pin 10 always turns ON then shuts down, even with digitalWrite(IgnitionPin, HIGH); I have tried other pins and they work fine -> turned OFF on start.
SoftwareSerial BTserial(12,13);
char choice;
const int loopDelay = 50;
int IgnitionPin = 10;
const long ignitionInterval = 5000;
int ignitionState = HIGH;
unsigned long previousMillis = 0;
void setup()
{
BTserial.begin(115200);
digitalWrite(IgnitionPin, HIGH);
pinMode(IgnitionPin, OUTPUT);
}
void loop()
{
if (BTserial.available())
{
choice = BTserial.read();
}
if( choice == 'm' )
{
ignitionState = HIGH;
digitalWrite(IgnitionPin, ignitionState);
ignitionCountTime = millis();
}
if (ignitionCountTime - previousMillis >= ignitionInterval) {
previousMillis = ignitionCountTime;
if (ignitionState == HIGH)
{
ignitionState = LOW;
}
digitalWrite(IgnitionPin, ignitionState);
}
delay(loopDelay);
}
EDIT:
SoftwareSerial BTserial(12,13);
char choice;
const int loopDelay = 50;
int IgnitionPin = 10;
unsigned long startTime;
unsigned long ignitionInterval = 30000;
unsigned long ignitionCountTime = 0;
void setup()
{
BTserial.begin(115200);
digitalWrite(IgnitionPin, HIGH);
pinMode(IgnitionPin, OUTPUT);
}
void loop()
{
if (BTserial.available())
{
choice = BTserial.read();
}
if( choice == 'm' )
{
digitalWrite(IgnitionPin, HIGH);
ignitionCountTime = millis();
}
if (ignitionCountTime - startTime >= ignitionInterval)
{
digitalWrite(IgnitionPin, LOW);
}
delay(loopDelay);
}
#1
Use the TimerOne library or setup an ISR.
Run the ISR at, 5 times per second.
uint32_t timeout = 5 * 60;
uint8_t flag = 1;
digitalWrite (myPin, HIGH);
if (timeout && flag) {
timeout--;
} else {
digitalWrite (myPin, LOW);
flag = 0;
}
OR
by checking time elapsed since some specific point in time.
unsigned long startTime;
unsigned long interval = 60000;
const byte aPin = 13;
void setup()
{
pinMode(aPin, OUTPUT);
digitalWrite(aPin, HIGH);
}
void loop()
{
if (millis() - startTime >= interval)
{
digitalWrite(aPin, LOW);
}
}
EDIT
Arduino is a microcontroller, it can do only one thing at once.
SoftwareSerial BTserial(12,13);
char choice;
const int loopDelay = 50;
int IgnitionPin = 10;
uint32_t timeout = 5 * 60;
uint8_t flag = 0;
void setup()
{
BTserial.begin(115200);
pinMode(IgnitionPin, OUTPUT);
digitalWrite(IgnitionPin, LOW);
}
void loop()
{
if (BTserial.available())
{
choice = BTserial.read();
}
if (choice == "m")
{
timeout = 5 * 60; //modify this timeout.
flag = 1;
digitalWrite(IgnitionPin, HIGH);
}
else if ((timeout > 0) && (flag == 1))
{
timeout--;
}
else
{
digitalWrite(IgnitionPin, LOW);
flag = 0;
}
delay(loopDelay);
}
#2 - In setup you are running 'digitalWrite(IgnitionPin, HIGH);' this will make it high
just use pinMode(IgnitionPin, OUTPUT); for setting pin as output pin
void setup()
{
Serial.begin(115200);
Serial.println("Enter AT commands:");
BTserial.begin(115200);
sensors.begin();
// Set Pin as an output pin
pinMode(IgnitionPin, OUTPUT);
digitalWrite(IgnitionPin, LOW);
}
If you want IgnitionPin as LOW at each restart - use 'digitalWrite(IgnitionPin, LOW);' in setup() after pinMode call.
Hello i have problem :
state sensor = sensor steps "IF" anyone "steps now = impulse " = all code run"perfect good work im happy" but if sensor LOW a did not detect any motion while loading the code = broke stop all , even if it is at the end close or halfway, it will stop when there is no traffic "" (stage_sensor == HIGH)"", i dont have idea how to fix this//
Please help or sugestion
EDIT : There may be syntax errors (is not consistent), but just wanted to present a problem, the system only works under "if" and this is problem, i dont know how to fix
#include <AccelStepper.h> //accelstepper library
// #define light
// #define move_sensor
// #define pump_water
// #define fan_blowing
// #define fan_extractor
// #define blue
// #define red
// #define green
const byte limitSwitch_1 = 26;
const byte limitSwitch_2 = 25;
bool switchFlipped = false;
bool previousFlip = true;
int switchCounter;
int newSpeed;
int state_sensor = 0; // <--- stage sensor //
int light = 2;
int blue = 3;
int red = 4;
int green = 5;
int fan_blowing = 6;
int water_pump = 8;
int move_sensor = 9;
int fan_extractor = 10;
AccelStepper stepper(1, 22, 23);
void setup()
{
pinMode(water_pump, OUTPUT);
pinMode(light, OUTPUT);
pinMode(fan_blowing, OUTPUT);
pinMode(fan_extractor, OUTPUT);
pinMode(blue, OUTPUT);
pinMode(red, OUTPUT);
pinMode(green, OUTPUT);
pinMode(move_sensor, INPUT);
pinMode(limitSwitch_1, INPUT_PULLUP); //pin 1 engine (IF touch)
pinMode(limitSwitch_2, INPUT_PULLUP); //pin 2 engine (IF touch)
Serial.begin(9600);
stepper.setMaxSpeed(1000);
stepper.setAcceleration(100);
stepper.setSpeed(1000);
delay(500);
}
void loop()
{
digitalWrite(light, HIGH);
digitalWrite(blue, HIGH);
state_sensor = digitalRead(sensor_move);
if (state_sensor == HIGH) // <--- stage sensor IF anyone move = all code run but if sensor LOW did not detect movement all code broke stop all //
{
digitalWrite(blue, LOW);
digitalWrite(red, HIGH);
stepper.runSpeed();
engine();
}
}
void engine()
{
if(digitalRead(limitSwitch_1) == LOW)
{
switchCounter++;
delay(1000);
newSpeed = -1 * (1000 + (switchCounter * 200));
stepper.setSpeed(newSpeed);
}
if(digitalRead(limitSwitch_2) == LOW)
{
switchCounter++;
delay(1000);
newSpeed = -1 * (1000 + (switchCounter * 200));
stepper.stop();
fans();
}
}
void fans()
{
digitalWrite(red, HIGH);
{
digitalWrite(fan_blowing, HIGH);
digitalWrite(fan_extractor, HIGH);
delay(1000);
digitalWrite(water_pump, HIGH);
}
delay(1000);
digitalWrite(red, LOW);
digitalWrite(water_pump, LOW);
digitalWrite(green, HIGH);
delay(1000);
digitalWrite(fan_blowing, LOW);
digitalWrite(fan_extractor, LOW);
digitalWrite(green, LOW);
digitalWrite(blue, HIGH); //this blue RGB light "ON" but if sensor_steps "ON" = active cycle , blue light led off
delay(1000);
}
What you could do is set up a global "stage" variable:
#include <AccelStepper.h> //accelstepper library
const byte limitSwitch_1 = 26;
const byte limitSwitch_2 = 25;
bool switchFlipped = false;
bool previousFlip = true;
int switchCounter;
int newSpeed;
int stage;
AccelStepper stepper(1, 22, 23);
void setup()
{
pinMode(limitSwitch_1, INPUT_PULLUP);
pinMode(limitSwitch_2, INPUT_PULLUP);
Serial.begin(9600);
stepper.setMaxSpeed(1000);
stepper.setAcceleration(100);
stepper.setSpeed(1000);
delay(500);
stage = 0;
}
void loop()
{
stepper.runSpeed();
runner();
}
void runner()
{
if(stage == 0 && digitalRead(limitSwitch_1) == LOW)
{
switchCounter++;
delay(5000);
newSpeed = -1 * (1000 + (switchCounter * 200));
stepper.setSpeed(newSpeed);
++stage;
}
if(stage == 1 && digitalRead(limitSwitch_2) == LOW)
{
stepper.setSpeed(1000);
delay(1000);
stepper.setSpeed(0);
++stage;
}
if (stage == 2) {
// do next thing, then ++stage;, etc.
}
}
I recently bought an ELEGO Mega 2560, or in other words an Arduino Mega. I bought a bmp180 sensor as well. I connected the bmp in this fashion, VCC - 3.3v, GND - GND, SCL - 21, SDA - 20. I uploaded a simple code which just displayes altitude. When I go to the Serial Monitor to view the results, nothing pops up. It is suppose to say BMP init success if it connects, and fail if it doesn't. When I go to the monitor, it just doens't say anything. When I disconnect the sensor, it says fail. It appears as if the Serial Monitor just freezes. Also a headsup, my code is very messy, I'm sorry if it's hard to keep up.
#include <Wire.h>
#include <SFE_BMP180.h>
SFE_BMP180 bmp180;
float Po = 1014.9;
#define ledPin 7
#define TransmitPin 5
//int Altitude = 5;
int sendValue;
String incomingString;
unsigned long lastTransmission;
const int interval = 1000;
void setup() {
Wire.begin();
pinMode(ledPin, OUTPUT);
pinMode(2, INPUT);
pinMode(10, OUTPUT);
pinMode(TransmitPin, OUTPUT);
bool success = bmp180.begin();
Serial.begin(115200);
if (success) {
Serial.println("BMP180 init success");
}
else
Serial.println("fail");
}
void loop() {
sendValue = digitalRead(29);
if (sendValue == HIGH) {
if (millis() > lastTransmission + interval) {
Serial.println("AT+SEND=1,8,Return");
digitalWrite(TransmitPin, HIGH);
delay(100);
digitalWrite(TransmitPin, LOW);
lastTransmission = millis();
}
}
if (Serial.available()) {
incomingString = Serial.readString();
if (incomingString.indexOf("Testing!") > 0) {
digitalWrite(10, HIGH);
delay(100);
digitalWrite(10, LOW);
}
}
char status;
double T, P, alt;
bool success = false;
status = bmp180.startTemperature();
if (status != 0) {
delay(1000);
status = bmp180.getTemperature(T);
if (status != 0) {
status = bmp180.startPressure(3);
if (status != 0) {
delay(status);
status = bmp180.getPressure(P, T);
if (status != 0) {
if (millis() > lastTransmission + interval) {
alt = bmp180.altitude(P, Po);
Serial.print("AT+SEND=1,8,");
int altAsFoot = alt * 3.281;
Serial.println(altAsFoot);
digitalWrite(TransmitPin, HIGH);
delay(100);
digitalWrite(TransmitPin, LOW);
}
for (int i = 0; i < 1800; i++) {
delay(1);
if (Serial.available()) {
incomingString = Serial.readString();
if (incomingString.indexOf("+OK") > 0) {
digitalWrite(ledPin, HIGH);
delay(100);
digitalWrite(ledPin, LOW);
}
if (incomingString.indexOf("Testing!") > 0) {
digitalWrite(10, HIGH);
delay(100);
digitalWrite(10, LOW);
}
}
}
}
}
}
}
}
Turns out it was a hardware issue. I had ground shorted to SDA. I'm assuming the same will happen if it's shorted to SCL. Make sure both SDA and SCL aren't shorted to each other or ground.
So i have been experimenting with TinkerCad, waiting for my arduino to arrive. Currently I have a loop of ledlights and i want to start and stop the loop by pressing a button.
Currently i am able to start my loop via the button, but not able to stop the loop with the same button press. Does this have something to do with the debouncing?
const int button = 10;
const int led1 = 8;
const int led2 = 4;
const int led3 = 3;
const int timedelay = 250;
boolean buttonstate = false;
void setup()
{
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT);
pinMode(button, INPUT);
}
void loop() {
if(digitalRead(button)==HIGH) // check if button is pushed
buttonstate = !buttonstate; //reverse buttonstate value
if(buttonstate==true)
{
digitalWrite(led1, HIGH);
delay(timedelay);
digitalWrite(led1, LOW);
delay(timedelay);
digitalWrite(led2, HIGH);
delay(timedelay);
digitalWrite(led2, LOW);
delay(timedelay);
digitalWrite(led3, HIGH);
delay(timedelay);
digitalWrite(led2, HIGH);
delay(timedelay);
digitalWrite(led1, HIGH);
delay(timedelay);
digitalWrite(led3, LOW);
delay(timedelay);
digitalWrite(led2, LOW);
delay(timedelay);
digitalWrite(led1, LOW);
delay(timedelay);
digitalWrite(led1, HIGH); }
else {
digitalWrite(led1, HIGH);
}
}
My circuit setup:
EDIT:
I have adjusted my code, replaced the delay with millis and looking for a change in button state. Still looking for a way to adjust interval_led1 at the end of the loop to make sick ledlight sequences.
const int led1 = 13;
const int led2 = 8;
const int led3 = 5;
const int button = 10;
int ledState_led1 = LOW; // ledState used to set the LED
int ledState_led2 = LOW;
int ledState_led3 = LOW;
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis_led1 = 0; // will store last time LED was updated
unsigned long previousMillis_led2 = 0;
unsigned long previousMillis_led3 = 0;
long interval_led1 = 500; // interval at which to blink (milliseconds)
long interval_led2 = 600;
long interval_led3 = 700;
boolean buttonstate = false;
void setup() {
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT);
pinMode(button, INPUT);
}
void loop() {
// check to see if it's time to blink the LED; that is, if the difference
// between the current time and last time you blinked the LED is bigger than
// the interval at which you want to blink the LED.
unsigned long currentMillis_led1 = millis();
unsigned long currentMillis_led2 = millis();
unsigned long currentMillis_led3 = millis();
bool current_state = digitalRead(button);
bool prev_buttonstate= false;
if(current_state==HIGH && current_state != prev_buttonstate)
{
buttonstate = !buttonstate; //reverse buttonstate value
}
prev_buttonstate = current_state;
if(buttonstate==true)
if (currentMillis_led1 - previousMillis_led1 >= interval_led1) {
previousMillis_led1 = currentMillis_led1;
if (ledState_led1 == LOW) {
ledState_led1 = HIGH;
} else {
ledState_led1 = LOW;
}
digitalWrite(led1, ledState_led1);
}
if(buttonstate==true)
if (currentMillis_led2 - previousMillis_led2 >= interval_led2) {
previousMillis_led2 = currentMillis_led2;
if (ledState_led2 == LOW) {
ledState_led2 = HIGH;
} else {
ledState_led2 = LOW;
}
digitalWrite(led2, ledState_led2);
}
if(buttonstate==true)
if (currentMillis_led3 - previousMillis_led3 >= interval_led3) {
previousMillis_led3 = currentMillis_led3;
if (ledState_led3 == LOW) {
ledState_led3 = HIGH;
} else {
ledState_led3 = LOW;
}
digitalWrite(led3, ledState_led3);
}
}
Here your two cases are very different in terms of delay:
if(buttonstate==true) is very long to execute because of the multiple delay instructions in it,
else is very fast because there is no delay in it.
When buttonstate==True and you press the button (as Delta_G said, the delay() prevent the test to happen most of the time and you should use millis() for instance to do the timing, but let say you are lucky and you pass your first if statement), so buttonstate will flip to false.
As there is no delay in your else instruction, the board will come back in no time to your initial if, which, unfortunately will still be true as you are not fast enough to press this button for only a few microseconds. So buttonstate will flip again and your code will fall in your if(buttonstate==true) which is very long, allowing you to release the button in time before the if(digitalRead(button)==HIGH) is reevaluated.
The solution (apart from timing issues raised by #Delta_G, and hardware issues raised by #TomServo) is to seek for changes of the button state. You thus have to compare to the previous value it had. You can declare another boolean boolean prev_buttonstate = false; and could do something like:
bool current_state = digitalRead(button);
if(current_state==HIGH && current_state != prev_buttonstate)
{
buttonstate = !buttonstate; //reverse buttonstate value
}
prev_buttonstate = current_state;
Hope it helps!
Your circuit is correct. If you keep pressing the button little longer, the condition will continue to hold good and the state falsely resets again.
To simulate the toggling effect, use a bool variable like so:. You reset the variable when the signal goes low.
void loop() {
static bool ready = true;
if(digitalRead(button)==HIGH && ready)
{
ready = false;
buttonstate = !buttonstate; //reverse buttonstate value
if(buttonstate){
digitalWrite(led1, HIGH);
delay(timedelay);
digitalWrite(led1, LOW);
delay(timedelay);
/* Etc*/ }
else {
digitalWrite(led1, HIGH);
}
}
else
if(digitalRead(button)==LOW && !ready)
{
ready = true;
}
}
I'm trying to blink a LED according to press of toggle button. If I press the first toggle switch the first time, LED blinks at 5 Hz, when I press the toggle button for the second time, LED blink at 6 Hz and when I press the third time, LED turns off.
I tried using the program below, but It's not working as I wanted.
// constants won't change. They're used here to set pin numbers:
const int buttonPin = 7; // the number of the pushbutton pin
const int ledPin = 6; // the number of the LED pin
// variables will change:
int buttonState = 0;
// variable for reading the pushbutton status
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
Serial.begin(9600);
}
void loop() {
int x=0;
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);
Serial.print(x);
// check if the pushbutton is pressed. If it is, the buttonState is HIGH:
if (buttonState == HIGH && x==0) {
// turn LED on:
digitalWrite(ledPin, HIGH);
delay(1000);
digitalWrite(ledPin, LOW);
delay(1000);
Serial.print(x);
} else {
// turn LED off:
x = x+1;
}
if (buttonState == HIGH && x==1) {
// turn LED on:
digitalWrite(ledPin, HIGH);
delay(2000);
digitalWrite(ledPin, LOW);
delay(2000);
Serial.print(x);
} else {
// turn LED off:
digitalWrite(ledPin, LOW);
x = x+1;
}
if (buttonState == HIGH && x==2) {
// turn LED on:
digitalWrite(ledPin, HIGH);
delay(3000);
digitalWrite(ledPin, LOW);
delay(3000);
Serial.print(x);
} else {
// turn LED off:
digitalWrite(ledPin, LOW);
x = x+1;
}
if (buttonState == HIGH && x==3) {
// turn LED off:
digitalWrite(ledPin, LOW);
x = 0;
}
}
When I use this code it works for first case that is LED blinks at 1000 ms delay, but if I toggle switch it again works for first condition. How can I make it to execute second condition i.e. to blink at delay of 2000 ms?
Firstly this is your circuit. I tried this circuit and code and worked for me. I used interrupt for checking button state. And millis calculation is simple.
Frequency = 1 / Period
Period = Ton + Toff
6Hz = 1000 millis / T => T = 166 millis
166 = Ton + Toff (for %50 duty cycle Ton=Toff) => Ton = Toff = 83 millis
enter image description here
const int ledPin = 13;
const int buttonPin = 2;
int state = -1;
bool willLightOn = false;
unsigned long currentDelay = 0;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(buttonPin), changeState, FALLING);
}
void loop() {
if(state % 3 == 0) { //6Hz
currentDelay = 83;
willLightOn = true;
} else if (state % 3 == 1) { //5Hz
currentDelay = 100;
willLightOn = true;
} else if (state % 3 == 2) { //LED off
currentDelay = 0;
willLightOn = false;
digitalWrite(ledPin, LOW);
}
currentMillis = millis();
if (currentMillis - previousMillis >= currentDelay && willLightOn) {
previousMillis = currentMillis;
digitalWrite(ledPin, !digitalRead(ledPin));
}
}
void changeState() {
state++;
}
Right now your logic checks 3 times for the value of x in a single loop.
Below code toggles light whenever x is greater than zero. x's value is changed when button is pressed.
But there is a big problem here: If button is pressed when there's something else going on in the processor or it is sleeping (like the long delays you want to use), it may be ignored. So you better study interrupts and implement this behavior using them.
if (x > 0)
{
digitalWrite(ledPin, HIGH);
delay(1000 * x);
digitalWrite(ledPin, LOW);
}
if (buttonState == HIGH)
{
x++;
if (x > 3)
x = 0;
}
You should create a global state of the application. This state is where you remember if you are blinking at 50hz/60hz/off. Then you can use a switch to do the right thing.
Then you check if the button is pressed and change the application state.
See my example below:
// constants won't change. They're used here to set pin numbers:
const int buttonPin = 7; // the number of the pushbutton pin
const int ledPin = 6; // the number of the LED pin
// variables will change:
int applicationState = 0;
bool lightOn = true;
int currentDelay = 1000;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
// variable for reading the pushbutton status
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
}
void loop() {
if (digitalRead(buttonPin) == HIGH) {
applicationState++;
if(applicationState >= 3) {
applicationState = 0;
}
delay(100);
}
switch(applicationState){
case 0:
currentDelay = 1000;
lightOn = true;
break;
case 1:
currentDelay = 2000;
lightOn = true;
break;
case 2:
digitalWrite(ledPin, LOW);
lightOn = false;
break;
}
currentMillis = millis();
if (currentMillis - previousMillis >= currentDelay && lightOn) {
previousMillis = currentMillis;
digitalWrite(ledPin, !digitalRead(ledPin));
}
}
I hope you understand what I try to say and demo with the example code.
Your code can not work:
You do need to check if the button state changes, detect when there is a edge. And make sure you detect a single edge only once.
You must repeat the blinking it in a loop till the button is pressed, then you can change the frequency.
You must check the button while you sleep, otherwise your program do not recognize when you press the button.
To make it work, you must change the complete program.
#define BLINK_SLEEP_TIME <some value> // insert value for 16.6666ms
//return 1 after a positive edge
bool button_read(void)
{
static bool lastState=1; //set this to 1, so that a pressed button at startup does not trigger a instant reaction
bool state = digitalRead(buttonPin);
if(state != lastState)
{
state=lastState;
return state;
}
return 0;
}
//Blink the LED with a given period, till button is pressed
//Times are in x*16.666ms or x/60Hz
//At least one time should be more than 0
void blink(uint8_t ontime, uint8_t offtime)
{
while(1)
{
for(uint8_t i=0;i<ontime;i++)
{
led_setOn();
delay(BLINK_SLEEP_TIME);
if(button_read())
{
return;
}
}
for(uint8_t i=0;i<offtime;i++)
{
led_setOff();
delay(BLINK_SLEEP_TIME);
if(button_read())
{
return;
}
}
}
}
const uint8_t time_table[][]=
{
{0,50},//LED is off
{6,6}, //LED blinks with 5Hz, 60Hz/2/6=5Hz
{5,5}, //LED blinks with 6Hz, 60Hz/2/5=6Hz
}
void endless(void)
{
uint8_t i=0;
for(;;)
{
i++;
if(i>2)
{
i=0;
}
blink(time_table[i][0],time_table[i][1]);
}
}
A better approach would be to use a hardware PWM-Module and change the values after a edge on the button.