I'm making a project where you have a led matrix, LCD and 7 different sensors. Each sensor will display a different message to the matrix and has it's own sub menu on the LCD.
I've created a menu for switching between the sensors. A rotary encoder is used to navigate in the menu. (A pin has an interrupt on change for executing only the void loop when the pin has changed) Also a select button is used to run the program of a menu item.( a sensor program like prg_Pong(), prg_Flame());
I want to display the menu items on the LCD (no I2C). Now my problem is my main menu items are displayed correctly (with no flicker because of the interrupt on the rotary encoder pin)
but when I press the button to run for ex prg_Pong() the lcd prints message running pong but the message is flickering and the message on the led matrix is good (static).
I've found a solution by adding a delay(300); in the loop after drawMenu but when I do that and enter prg_Pong();
the message on the led matrix starts to flicker at the delay time and the menu item "running pong" doesn't flicker anymore.
The functions displayData(),mtx_clear() are used for the led matrix and they are working. Because when I remove the functionality of the LCD the message is displayed correctly.
void loop() {
drawMenu(RotaryEncoder());
delay(300);
}
void drawMenu(byte rotaryPosition) {
stateBtnSelect = digitalRead(BTN_SELECT);
if (stateBtnSelect == HIGH && previousBtnState == LOW && millis() - time > debounce) {
if (state == HIGH)
state = LOW;
else
state = HIGH;
time = millis();
}
previousBtnState = stateBtnSelect;
//Switch the value of the rotary encoder
switch (rotaryPosition) {
case 0:
displayMenuItem(0);
if (state == HIGH) {
prg_Pong();
}
break;
case 1:
displayMenuItem(1);
if (state == HIGH) {
prg_Ascroll();
}
break;
case 2:
displayMenuItem(2);
if (state == HIGH) {
prg_Clock();
}
break;
case 3:
displayMenuItem(3);
if (state == HIGH) {
prg_Flame();
}
break;
case 4:
displayMenuItem(4);
if (state == HIGH) {
prg_Heartbeat();
}
break;
default:
break;
}
}
//Display a menu item
void displayMenuItem(byte item)
{
//menu strings (mai n categories)
String menuItems[MAX_MENU_ITEMS] = {"Pong game", "A. Scroller", "Clock", "Flame/temp", "Heartbeat"};
lcd.clear();
lcd.setCursor(6, 0);
lcd.print("Menu");
lcd.setCursor(0, 1);
lcd.write(byte(0));
lcd.print(menuItems[item]);
}
void prg_Pong()
{
lcd.clear();
delay(100);
lcd.print("run pong");
mtx_clear();
//print cirlce once
MessageEncoder(ReturnLetter(24), BitStream);
for (int j = 0; j < 8; j++)
{
displayData(~BitStream[j] & Kolommen[j]);
}
}
you are clearing the lcd display frequently, so it will look like flickering.
i used a two more variables to find, Is there any need to update the display? change the display contents only if required, I didn't compile code but hope this will solve your problem, i use similar technique in my projects.
prev_menu=-1;
update_display=true;
void loop() {
drawMenu(RotaryEncoder());
delay(300);
}
void drawMenu(byte rotaryPosition) {
stateBtnSelect = digitalRead(BTN_SELECT);
if (stateBtnSelect == HIGH && previousBtnState == LOW && millis() - time > debounce) {
if (state == HIGH)
state = LOW;
else
state = HIGH;
time = millis();
update_display=true;
}
previousBtnState = stateBtnSelect;
if(prev_menu != rotaryPosition)
update_display=true;
else
update_display=false;
//Switch the value of the rotary encoder
switch (rotaryPosition) {
case 0:
displayMenuItem(0);
if (state == HIGH) {
prg_Pong();
}
break;
case 1:
displayMenuItem(1);
if (state == HIGH) {
prg_Ascroll();
}
break;
case 2:
displayMenuItem(2);
if (state == HIGH) {
prg_Clock();
}
break;
case 3:
displayMenuItem(3);
if (state == HIGH) {
prg_Flame();
}
break;
case 4:
displayMenuItem(4);
if (state == HIGH) {
prg_Heartbeat();
}
break;
default:
break;
}
}
//Display a menu item
void displayMenuItem(byte item)
{
//menu strings (mai n categories)
if( update_display){
String menuItems[MAX_MENU_ITEMS] = {"Pong game", "A. Scroller", "Clock", "Flame/temp", "Heartbeat"};
lcd.clear();
lcd.setCursor(6, 0);
lcd.print("Menu");
lcd.setCursor(0, 1);
lcd.write(byte(0));
lcd.print(menuItems[item]);
prev_menu=item;
}
}
void prg_Pong()
{
if(update_display){
lcd.clear();
delay(100);
lcd.print("run pong");
update_display=false;
}
mtx_clear();
//print cirlce once
MessageEncoder(ReturnLetter(24), BitStream);
for (int j = 0; j < 8; j++)
{
displayData(~BitStream[j] & Kolommen[j]);
}
}
Related
OK so firstly, I am VERY new to C++ as you will undoubtedly see from the code. I have been struggling with this to get it to where it is now so my apologies if it's not very good.
Ok here is what SHOULD happen.
Button 2 (downButton) is pressed to select the correct program (that works).
Button 3 (startButton) is used to start the timer for that program (that works)
The timer is supposed to count down (also working) and when it reaches zero, display a message and then go back to the start of the switch case.
This is what I am struggling with. Firstly the timer gets to zero and does nothing else. I have tried return, goto etc without any success.
I commented out the end message function, which does work BUT still gets stuck there without exiting the function.
What I am looking for is a little help to make the timer jump back to where it was called from.
Here is the code:
#include <Wire.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int hours = 0; // start hours
int minutes = 00; //start min
int seconds = 05; //start seconds
const int buttonPin = 9; // the pin that the pushbutton is attached to
const int ledPin = A0; // the pin that the LED is attached to
const int ledPin2 = A1; // the pin that the LED is attached to
int buttonState = 0; // current state of the button
int WhichScreen = 0; // This variable stores the current Screen number
boolean hasChanged = true;
const int upButton = 8; // the number of the select pin
const int downButton = 10; // the number of the select pin
const int startButton = 9; // the number of the start pin
int selectState; // the current reading from the select pin
int lastSelectState = LOW; // the previous reading from the select pin
int backState; // the current reading from the select pin
int lastBackState = LOW; // the previous reading from the select pin
int startState; // the current reading from the start pin
int lastStartState = LOW; // the previous reading from the start pin
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers
void setup()
{
Serial.begin(9600);
pinMode(buttonPin, INPUT); // initialize the button pin as a input
pinMode(ledPin, OUTPUT); // initialize the button pin as a output
pinMode(ledPin2, OUTPUT); // initialize the button pin as a output
lcd.begin(16, 2);
pinMode(upButton, INPUT);
pinMode(startButton, INPUT);
pinMode(downButton, INPUT);
pinMode(A0, OUTPUT); // LED 1
pinMode(A1, OUTPUT); // LED 2
pinMode(A2, OUTPUT); // LED 3
}
void loop()
{
start:
// Read the SELECT pin
if (WhichScreen == 1) {
buttonState = digitalRead(buttonPin);
if (buttonState == HIGH) {
digitalWrite(ledPin, buttonState);
delay(2000);
digitalWrite(ledPin, LOW);
}
}
if (WhichScreen == 4) {
buttonState = digitalRead(buttonPin);
if (buttonState == HIGH) {
// digitalWrite(ledPin2, buttonState);
// delay(5000);
// digitalWrite(ledPin2, LOW);
lcd.begin(16, 2);
minutes = 00; //start min
seconds = 05; //start seconds
digitalWrite(ledPin, buttonState);
lcd.print("P1-AMBER");
timer();
// Function SHOULD return to here when reaches zero
digitalWrite(ledPin, LOW);
minutes = 00; //start min
seconds = 05; //start seconds
lcd.print("P1-DARK RED");
timer();
}
}
if (hasChanged == true) {
switch (WhichScreen) {
case 0:
{
lcd.clear();
lcd.setCursor(2, 0); // Column, line
lcd.print("LIGHT THERAPY");
lcd.setCursor(2, 1);
lcd.print("PANEL V1.01");
delay(2500); // will be removed once relays are installed
lcd.clear();
lcd.setCursor(4, 0); // Column, line
lcd.print("WELCOME");
lcd.setCursor(1, 1);
lcd.print("GARY & TRACEY");
delay(2500); // will be removed once relays are installed
WhichScreen++;
program1();
}
break;
case 1:
{
program1();
}
break;
case 2:
{
program2();
}
break;
case 3:
{
program3();
}
break;
case 4:
{
program4();
}
break;
case 5:
{
program5();
}
break;
case 6:
{
program6();
}
break;
case 7:
{
program7();
}
break;
case 8:
{
program8();
}
break;
case 9:
{
program9();
}
break;
}
}
//-------------------------------
// BEGIN of the switch debouncing code
int reading = digitalRead(upButton);
if (reading != lastSelectState) {
// reset the debouncing timer
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
// whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current state:
// if the button state has changed:
if (reading != selectState) {
selectState = reading;
// only toggle the LED if the new button state is HIGH
if (selectState == HIGH) {
hasChanged = true;
WhichScreen++;
}
} else {
hasChanged = false;
}
}
lastSelectState = reading;
// END of the switch Debouncing code
// --------------------------------------
if (WhichScreen > 9) {
WhichScreen = 1;
}
}
void program1()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 1 (P1)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program2()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 2 (P2)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program3()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 3 (P3)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program4()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 4 (P4)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program5()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 5 (P5)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program6()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 6 (P6)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program7()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 7 (P7)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program8()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 8 (P8)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void program9()
{
lcd.clear();
lcd.setCursor(1, 0); // Column, line
lcd.print("PROGRAM 9 (P9)");
lcd.setCursor(3, 1);
lcd.print("14 Minutes");
}
void stepDown() {
if (seconds > 0) {
seconds -= 1;
} else {
if (minutes > 0) {
seconds = 59;
minutes -= 1;
} else {
if (hours > 0) {
seconds = 59;
minutes = 59;
hours -= 1;
} else {
//trigger();
}
}
}
}
void timer() {
delay(150);
while (hours > 0 || minutes > 0 || seconds >= 0) {
lcd.setCursor(0, 2);
//(hours < 10) ? lcd.print("0") : NULL;
// lcd.print(hours);
//lcd.print(":");
lcd.print("TIME LEFT: ");
(minutes < 10) ? lcd.print("0") : NULL;
lcd.print(minutes);
lcd.print(":");
(seconds < 10) ? lcd.print("0") : NULL;
lcd.print(seconds);
lcd.display();
stepDown();
delay(1000);
}
}
void trigger() {
lcd.clear(); // clears the screen and buffer
lcd.setCursor(6, 0); // set timer position on lcd for end.
lcd.print("TIMES UP");
lcd.setCursor(6, 1); // set timer position on lcd for end.
lcd.print("NUMBER 4");
delay(1000);
lcd.display();
}
Sorry it's a bit messy and all over the place, I'm also sure it's not flowing very well either but it works mostly lol.
Any and all help much appreciated, and thank you in advance.
In your timer function. You have a loop condition hours > 0 || minutes > 0 || seconds >= 0. Since your seconds is always greater than 0, you have a infinite loop in timer.
As you can see, seconds does not decrease seconds == 0.
void stepDown() {
if (seconds > 0) {
seconds -= 1;
} else {
if (minutes > 0) {
seconds = 59;
minutes -= 1;
} else {
if (hours > 0) {
seconds = 59;
minutes = 59;
hours -= 1;
} else {
//trigger();
}
}
}
}
I'm currently hitting a brick wall while trying to edit some code. I'm trying to input a pause/resume piece into the code, so that when the electrode is touched - if playing it will be paused and then consequently be able to be resumed by touching it again.
With my current edit of the code, the track isn't resuming once paused.
I'm learning as I go along so apologies, if it is an glaringly obvious fix. Any help will be greatly appreciated!
Link to library using is here:
https://github.com/mpflaga/Sparkfun-MP3-Player-Shield-Arduino-Library
See the code I'm working with below!
// compiler error handling
#include "Compiler_Errors.h"
// touch includes
#include <MPR121.h>
#include <Wire.h>
#define MPR121_ADDR 0x5C
#define MPR121_INT 4
// mp3 includes
#include <SPI.h>
#include <SdFat.h>
#include <FreeStack.h>
#include <SFEMP3Shield.h>
// mp3 variables
SFEMP3Shield MP3player;
byte result;
int lastPlayed = 0;
// mp3 behaviour defines
#define REPLAY_MODE FALSE // By default, touching an electrode repeatedly will
// play the track again from the start each time.
//
// If you set this to FALSE, repeatedly touching an
// electrode will stop the track if it is already
// playing, or play it from the start if it is not.
// touch behaviour definitions
#define firstPin 0
#define lastPin 11
// sd card instantiation
SdFat sd;
void setup(){
Serial.begin(57600);
pinMode(LED_BUILTIN, OUTPUT);
//while (!Serial) ; {} //uncomment when using the serial monitor
Serial.println("Bare Conductive Touch MP3 player");
if(!sd.begin(SD_SEL, SPI_HALF_SPEED)) sd.initErrorHalt();
if(!MPR121.begin(MPR121_ADDR)) Serial.println("error setting up MPR121");
MPR121.setInterruptPin(MPR121_INT);
MPR121.setTouchThreshold(5);
MPR121.setReleaseThreshold(5);
result = MP3player.begin();
MP3player.setVolume(10,10);
if(result != 0) {
Serial.print("Error code: ");
Serial.print(result);
Serial.println(" when trying to start MP3 player");
}
}
void loop(){
readTouchInputs();
}
void readTouchInputs(){
if (MPR121.touchStatusChanged()) {
MPR121.updateTouchData();
// only make an action if we have one or fewer pins touched
// ignore multiple touches
if (MPR121.getNumTouches() <= 1) {
for (int i = 0; i < 12; i++) { // Check which electrodes were pressed
if (MPR121.isNewTouch(i)) {
//pin i was just touched
Serial.print("pin ");
Serial.print(i);
Serial.println(" was just touched");
digitalWrite(LED_BUILTIN, HIGH);
if (i <= lastPin && i >= firstPin) {
if (MP3player.isPlaying()) {
if (lastPlayed == i && !REPLAY_MODE) {
// if we're already playing the requested track, stop it
// (but only if we're not in REPLAY_MODE)
MP3player.pauseMusic();
Serial.print("paused_playback");
Serial.println(MP3player.getState());
}
else {
// if the track is already paused, resume the track
if (MP3player.getState() == paused_playback) {
if (lastPlayed == i) {
MP3player.resumeMusic();
Serial.print("resuming");
Serial.println(i-firstPin);
} else {
// if we're already playing a different track (or we're in
// REPLAY_MODE), stop and play the newly requested one
MP3player.stopTrack();
MP3player.playTrack(i-firstPin);
Serial.print("playing track ");
Serial.println(i-firstPin);
// don't forget to update lastPlayed - without it we don't
// have a history
lastPlayed = i;
}
}
}
} else {
// if we're playing nothing, play the requested track
// and update lastplayed
MP3player.playTrack(i-firstPin);
Serial.print("playing track ");
Serial.println(i-firstPin);
lastPlayed = i;
}
}
} else {
if (MPR121.isNewRelease(i)) {
Serial.print("pin ");
Serial.print(i);
Serial.println(" is no longer being touched");
digitalWrite(LED_BUILTIN, LOW);
}
}
}
}
}
}
Thanks Again!
edited code with last played included
// compiler error handling
#include "Compiler_Errors.h"
// touch includes
#include <MPR121.h>
#include <Wire.h>
#define MPR121_ADDR 0x5C
#define MPR121_INT 4
// mp3 includes
#include <SPI.h>
#include <SdFat.h>
#include <FreeStack.h>
#include <SFEMP3Shield.h>
// mp3 variables
SFEMP3Shield MP3player;
byte result;
int lastPlayed = 0;
// mp3 behaviour defines
#define REPLAY_MODE FALSE // By default, touching an electrode repeatedly will
// play the track again from the start each time.
//
// If you set this to FALSE, repeatedly touching an
// electrode will stop the track if it is already
// playing, or play it from the start if it is not.
// touch behaviour definitions
#define firstPin 0
#define lastPin 11
enum Action {
DO_NOTHING,
PLAY,
PAUSE,
RESUME,
STOP_THEN_PLAY
};
Action nextAction(state_m state, int i) {
if (state == paused_playback && lastPlayed == i) {
return RESUME;
}
if (state != playback) {
return PLAY;
}
if (state == playback && lastPlayed == i){
return PAUSE;
}
if (state == playback) {
return STOP_THEN_PLAY;
}
return DO_NOTHING;
}
// sd card instantiation
SdFat sd;
void setup(){
Serial.begin(57600);
pinMode(LED_BUILTIN, OUTPUT);
//while (!Serial) ; {} //uncomment when using the serial monitor
Serial.println("Bare Conductive Touch MP3 player");
if(!sd.begin(SD_SEL, SPI_HALF_SPEED)) sd.initErrorHalt();
if(!MPR121.begin(MPR121_ADDR)) Serial.println("error setting up MPR121");
MPR121.setInterruptPin(MPR121_INT);
MPR121.setTouchThreshold(5);
MPR121.setReleaseThreshold(5);
result = MP3player.begin();
MP3player.setVolume(10,10);
if(result != 0) {
Serial.print("Error code: ");
Serial.print(result);
Serial.println(" when trying to start MP3 player");
}
}
void loop(){
readTouchInputs();
}
void readTouchInputs(){
if(MPR121.touchStatusChanged()){
MPR121.updateTouchData();
// only make an action if we have one or fewer pins touched
// ignore multiple touches
if(MPR121.getNumTouches()<=1){
for (int i = 0; i < 12; i++) { // Check which electrodes were pressed
if (MPR121.isNewTouch(i)) {
state = MP3player.getState();
Action action = nextAction(state, i); // find what to do next
switch (action) {
case PLAY:
Serial.println("play");
MP3player.playTrack(i-firstPin);
lastPlayed = i;
state = playback;
break;
case PAUSE:
Serial.println("pause");
MP3player.pauseMusic();
state = paused_playback;
lastPlayed = i;
break;
case RESUME:
Serial.println("resume");
MP3player.resumeMusic();
state = playback;
break;
case STOP_THEN_PLAY:
Serial.println("stop then play");
MP3player.stopTrack();
MP3player.playTrack(i-firstPin);
state = playback;
lastPlayed = i;
break;
default:
break;
}
}
}
}
}
}
Deeply nested if statements are very difficult to follow. I understand that you want to trigger one of four actions (play, pause, resume and stop/play) based on the state of MP3 player, last played track and electrode ID. I suggest you define a function like this to determine an action.
enum Action {
DO_NOTHING,
PLAY,
PAUSE,
RESUME,
STOP_THEN_PLAY
};
Action nextAction(state_m state, int i) {
if (state == paused_playback && lastPlayed == i) {
return RESUME;
}
if (state != playback) {
return PLAY;
}
if (state == playback && lastPlayed == i){
return PAUSE;
}
if (state == playback) {
return STOP_THEN_PLAY;
}
return DO_NOTHING;
}
Pass the state of MP3 player and the electrode's id to the function. You can then trigger an appropriate action based on the value of action.
for (int i = 0; i < 12; i++) { // Check which electrodes were pressed
if (MPR121.isNewTouch(i)) {
state_m state = MP3player.getState();
Action action = nextAction(state, i); // find what to do next
switch (action) {
case PLAY:
Serial.println("play");
MP3player.playTrack(i-firstPin);
lastPlayed = i;
state = playback;
break;
case PAUSE:
Serial.println("pause");
MP3player.pauseMusic();
state = paused_playback;
lastPlayed = i;
break;
case RESUME:
Serial.println("resume");
MP3player.resumeMusic();
state = playback;
break;
case STOP_THEN_PLAY:
Serial.println("stop then play");
MP3player.stopTrack();
MP3player.playTrack(i-firstPin);
state = playback;
lastPlayed = i;
break;
default:
break;
}
}
}
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.
I am using keypad 4*4 with an Arduino Mega, I have sensors connected to the Arduino I want to press a key from the keypad and have response in the same time, I don't want to wait till the loop continue, I want to save up to 10 numbers entered from the keypad, so is there a way to make the keypad as an interrupt for my code?
void loop()
{
char key = kpd.getKey();
if (key != NO_KEY) {
if (key == 'C') //clear the array
{
index = 0;
numbers[index] = '\0';
}
else if (key == '.') {
numbers[index++] = '.';
numbers[index] = '\0';
}
else if (key >= '0' && key <= '9') {
for (int z = 0; z == 10; z++) {
numbers[index++] = key;
numbers[index] = '\0';
}
}
else if (key == 'A') {
float len = atof(numbers); //conversion from string to numeric
if (fona.callPhone(numbers)) {
Serial.println(F("RING!"));
Serial.print(F("Phone Number: "));
Serial.println(numbers); //print out on serial monitor
}
}
else if (key == 'D') {
if (fona.pickUp()) {
Serial.println(F("Failed"));
}
}
else if (key == '#') {
if (fona.hangUp()) {
Serial.println(F("Failed"));
}
}
}
gas = analogRead(gasPin);
dtostrf(gas, 4, 2, gass);
Serial.println(gas);
delay(1000); // Print value every 1 sec.
val = digitalRead(inputPin); // read input value
if (val == HIGH) { // check if the input is HIGH
digitalWrite(ledPin, HIGH); // turn LED ON
delay(150);
if (pirState == LOW) {
// we have just turned on
Serial.println("Motion detected!");
// We only want to print on the output change, not state
pirState = HIGH;
}
}
else {
digitalWrite(ledPin, LOW); // turn LED OFF
delay(300);
if (pirState == HIGH) {
// we have just turned off
Serial.println("Motion ended!");
// We only want to print on the output change, not state
pirState = LOW;
}
}
int ldrStatus = analogRead(ldrPin);
if (ldrStatus <= 550) {
digitalWrite(led, HIGH);
Serial.println("LDR is DARK, LED is ON");
}
else {
digitalWrite(led, LOW);
}
valee = analogRead(tempPin);
float cel = (valee * 0.48828125) / 2.12304;
dtostrf(cel, 4, 2, temp);
Serial.print("TEMPRATURE = ");
Serial.print(cel);
Serial.print("*C");
Serial.println();
delay(1000);
Need to break the Stepper motor operation within "for loop". But the code that I have written breaks the operation after the completion of the loop, it does not break the operation between the loop. Please chek the code and tell me any possible way to stop the loop in between..
Code:
#include <Stepper.h>
int in1Pin = 8;
int in2Pin = 9;
int in3Pin = 10;
int in4Pin = 12;
bool entry = false;
int j;
Stepper motor(200, in1Pin, in2Pin, in3Pin, in4Pin);
void setup() {
pinMode(in1Pin, OUTPUT);
pinMode(in2Pin, OUTPUT);
pinMode(in3Pin, OUTPUT);
pinMode(in4Pin, OUTPUT);
while (!Serial);
Serial.begin(9600);
motor.setSpeed(300);
Serial.println("Type in your selection");
entry = false;
}
void loop() {
if (Serial.available() > 0){
switch(Serial.read()){
case 'a':
entry = true;
break;
case 'b':
entry = false;
break;
default:break;
}
}
if(entry == true){
for(j = -20; j <= 20; j++){
motor.step(j/0.176);
}
}
else if(entry == false){
digitalWrite(in1Pin,LOW);
digitalWrite(in2Pin,LOW);
digitalWrite(in3Pin,LOW);
digitalWrite(in4Pin,LOW);
}
}
From Comments: when i send 'a' through serial monitor, the stepper starts rotating, when i send 'b' through serial it should break stepper motor rotation, but its breaking only after the completion of loop (not within the loop)
If you don't give a chance to Arduino to parse the serial input in-between the execution of the for loop, then obviously is not going to work as you want.
You should do something similar to this:
...
void loop() {
static int j;
if (Serial.available() > 0){
switch(Serial.read()){
case 'a':
entry = true;
/**
* replace next 3 lines with `j = -20` if you want to start
* the loop from scratch each time `a` is received,
* instead of resuming from where you stopped.
*/
if (j >= 20) {
j = -20;
}
break;
case 'b':
entry = false;
break;
default:break;
}
}
if (entry) {
if (j <= 20) {
motor.step(j/0.176);
++j;
}
} else {
digitalWrite(in1Pin, LOW);
digitalWrite(in2Pin, LOW);
digitalWrite(in3Pin, LOW);
digitalWrite(in4Pin, LOW);
}
}
In this code, I let Arduino parse a new character after each call to motor.step(). Note that this approach introduces a tiny delay among sub-sequent calls to motor.step() wrt. your original solution, but it should be negligible in practice.
If you send multiple a in a row to Arduino using this code, but Arduino did not complete the for loop yet, then with this code the additional a will be ignored. If you want to handle them as additional for request, then you should add a counter to keep track of the number of pending (full) iterations of the for loop that you want to perform.