switching between functions using an IR remote - arduino

I have bought a WS2812B Led Strip. I'm trying to controll it with a IR remote. it is all controlled by a arduino uno.
I know the leds work and i know the remote works. I'm trying to pre-program a few animations on the remote.
The code below is as far as i got. I can show one animation, but i have to wait until it end to change it to onother one.
Is it possible to interupt this (becouse some animations are infinite) when i push a button to choose another animation?
#include <IRremote.h>
#include "FastLED.h"
#define NUM_LEDS 232
CRGB leds[NUM_LEDS];
#define PIN 7
const int RECV_PIN = 6;
IRrecv irrecv(RECV_PIN);
decode_results results;
unsigned long key_value = 0;
void setup(){
FastLED.addLeds<WS2812B, PIN, RGB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
Serial.begin(9600);
irrecv.enableIRIn();
irrecv.blink13(true);
}
//switch case for remote
void loop(){
if (irrecv.decode(&results)){
if (results.value == 0XFFFFFFFF)
results.value = key_value;
switch(results.value){
case 0xFF30CF:
Serial.println("1");
RGBLoop();
break ;
case 0xFF18E7:
Serial.println("2");
red();
break ;
case 0xFF7A85:
Serial.println("3");
break ;
}
key_value = results.value;
irrecv.resume();
}
}
void RGBLoop(){
while(key_value==key_value){
for(int j = 0; j < 6; j++ ) {
// Fade IN
for(int k = 0; k < 256; k++) {
switch(j) {
case 0: setAll(k,0,0); break;
case 2: setAll(k,k,0); break;
case 3: setAll(0,k,0); break;
case 4: setAll(0,k,k); break;
case 5: setAll(0,0,k); break;
}
showStrip();
delay(3);
}
// Fade OUT
for(int k = 255; k >= 0; k--) {
switch(j) {
case 0: setAll(k,0,0); break;
case 2: setAll(k,k,0); break;
case 3: setAll(0,k,0); break;
case 4: setAll(0,k,k); break;
case 5: setAll(0,0,k); break;
}
showStrip();
delay(3);
}
}
}
}
void red(){
irrecv.resume();
setAll(0,255,255);
showStrip();
}
void setPixel(int Pixel, byte red, byte green, byte blue) {
#ifdef ADAFRUIT_NEOPIXEL_H
// NeoPixel
strip.setPixelColor(Pixel, strip.Color(red, green, blue));
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
// FastLED
leds[Pixel].r = red;
leds[Pixel].g = green;
leds[Pixel].b = blue;
#endif
}
void setAll(byte red, byte green, byte blue) {
for(int i = 0; i < NUM_LEDS; i++ ) {
setPixel(i, red, green, blue);
}
showStrip();
}
void showStrip() {
#ifdef ADAFRUIT_NEOPIXEL_H
// NeoPixel
strip.show();
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
// FastLED
FastLED.show();
#endif
}
void fadeall() { for(int i = 0; i < NUM_LEDS; i++) { leds[i].nscale8(250); } }
being honnest most of this I got online. it is my first time programming an arduino. but so far Im liking it

Instead of using infinite loops, you can just check for if any other command is received.
E.g:
instead of
while ( key_value == key_value ) //This is a weird infinite loop condition but whatever
{
// do stuff
}
you can have
while ( decode() == condition_for_this_loop )
{
// do stuff
}
or
for(;;) //This also is just an infinite loop, but looks nicer.
{
// do stuff
if ( decode() != condition_for_this_loop ) { break; }
}
Note that this is a pseudo code. You need to properly implement it. And you may need to alter the logic of your program a bit.
Since you have defined irrecv object globally, it will be visible for other functions below, this won't be a problem. Your code may even work just by replacing the loop condition. But if you get errors, you need to deal with them, I am just pointing to the logic. In the end, you can have something like this:
irrecv.decode( &results );
switch ( results )
{
case CONDITION_1:
inf_loop1();
break;
case CONDITION_2:
int_loop2();
break;
}
...
void inf_loop1()
{
for(;;)
{
// do stuff
irrecv.decode( &results );
if ( results != CONDITION_1 ) { break; }
}
}
void inf_loop2()
{
for(;;)
{
// do stuff
irrecv.decode( &results );
if ( results != CONDITION_2 ) { break; }
}
}
By the way, it's not a good idea to start with relatively big projects. By your question, I am assuming this is not only your first program for Arduino, but your first ever program. You don't run before you walk. Start slowly, blink some LEDs, implement some fun algorithms with LEDs, i don't know, have multiple of them and light them in different sequences, have some buttons and combine them with LEDs. Just play with LEDs. As you get more experience, you won't be asking questions like this. And if you start with some big project, chances are, you won't be able to do it, and got disappointed, or you will be just followed some online tutorials and copy-paste code, hence it won't feel like you did it.

Related

issue with Arduino using FastLed with IRremote change mode

Hi I am trying to build a arduino project to control led strip and change the light pattern when pressed different button
the code i am using is
#include <IRremote.h> //include the library
#include <FastLED.h>
// How many leds in your strip?
#define NUM_LEDS 70
#define DATA_PIN 3
#define CLOCK_PIN 13
#define Button_0 0xFF9867
#define Button_1 0xFFA25D
#define Button_2 0xFF629D
#define Button_3 0xFFE21D
#define Button_4 0xFF22DD
#define Button_5 0xFF02FD
#define Button_6 0xFFC23D
#define Button_7 0xFFE01F
#define Button_8 0xFFA857
#define Button_9 0xFF906F
int receiver = 7; //initialize pin 13 as recevier pin.
IRrecv irrecv(receiver); //create a new instance of receiver
decode_results results;
CRGB leds[NUM_LEDS];
void setup() {
Serial.begin(9600);
LEDS.addLeds<WS2812,DATA_PIN,RGB>(leds,NUM_LEDS);
LEDS.setBrightness(84);
irrecv.enableIRIn(); //start the receiver
}
void loop() {
if (irrecv.decode(&results)) { //if we have received an IR signal
Serial.println (results.value, HEX); //display HEX results
irrecv.resume(); //next value
if (results.value == Button_0) {
RunningLightSlow();
}
else if(results.value == Button_1) {
RainbowLights();
}
// switch (results.value) {
//
// case Button_0: RunningLightSlow(); break;
// case Button_1: RainbowLights(); break;
// }
}
}
// LIGHTSHOW 0 starts --------------------------------------------------------------------------------------------------
void fadeall() { for(int i = 0; i < NUM_LEDS; i++) { leds[i].nscale8(250); } }
void RunningLightSlow() {
while(1) {
static uint8_t hue = 0;
Serial.print("x");
// First slide the led in one direction
for(int i = 0; i < NUM_LEDS; i++) {
// Set the i'th led to red
leds[i] = CHSV(hue++, 255, 255);
// Show the leds
FastLED.show();
// now that we've shown the leds, reset the i'th led to black
// leds[i] = CRGB::Black;
fadeall();
// Wait a little bit before we loop around and do it again
delay(10);
}
Serial.print("x");
// Now go in the other direction.
for(int i = (NUM_LEDS)-1; i >= 0; i--) {
// Set the i'th led to red
leds[i] = CHSV(hue++, 255, 255);
// Show the leds
FastLED.show();
// now that we've shown the leds, reset the i'th led to black
// leds[i] = CRGB::Black;
fadeall();
// Wait a little bit before we loop around and do it again
delay(10);
}
}
}
// LIGHTSHOW 0 ends --------------------------------------------------------------------------------------------------------
// LIGHTSHOW 1 STARTS ------------------------------------------------------------------------------------------------------
void RainbowLights() {
randomSeed(millis());
int wait=random(10,30);
int dim=random(4,6);
int max_cycles=8;
int cycles=random(1,max_cycles+1);
while(1) {
rainbowCycle(wait, cycles, dim);
}
}
void rainbowCycle(int wait, int cycles, int dim)
{
Serial.println("Let's make a rainbow.");
//loop several times with same configurations and same delay
for(int cycle=0; cycle < cycles; cycle++)
{
byte dir=random(0,2);
int k=255;
//loop through all colors in the wheel
for (int j=0; j < 256; j++,k--)
{
if(k<0)
{
k=255;
}
//Set RGB color of each LED
for(int i=0; i<NUM_LEDS; i++)
{
CRGB ledColor = wheel(((i * 256 / NUM_LEDS) + (dir==0?j:k)) % 256,dim);
leds[i]=ledColor;
}
FastLED.show();
FastLED.delay(wait);
}
}
}
CRGB wheel(int WheelPos, int dim)
{
CRGB color;
if (85 > WheelPos)
{
color.r=0;
color.g=WheelPos * 3/dim;
color.b=(255 - WheelPos * 3)/dim;;
}
else if (170 > WheelPos)
{
color.r=WheelPos * 3/dim;
color.g=(255 - WheelPos * 3)/dim;
color.b=0;
}
else
{
color.r=(255 - WheelPos * 3)/dim;
color.g=0;
color.b=WheelPos * 3/dim;
}
return color;
}
Now the issue i am facing is when i press a button (for eg 0) it runs the function RunningLightSlow(); i have written the infinite loop code because i want infinite light show and when i press button 1 it does not change the light pattern ,
Root cause would be once the function goes into infinite loop it then never receives the ir signal and hence never change the light pattern
Remove all infinite loops from your program and create a simple structure in loop() where you check for IR signal and update a "state" variable with the last button pressed.
void loop() {
// create a function or add here whatever is needed to read the IR code
irCode = readIrCode();
// check if the new code is identical to a previous code
if (lastIrCode != irCode) {
// memorize the current code
lastIrCode = irCode;
/ act according to user selection
switch (irCode) {
case Button_0:
doThis(); // this could be your RunningLightSlow()
break;
case Button_1:
doThat(); // maybe RainbowLights()
break;
default:
// for cases you have not handled above
doWhatever();
}
}
}
Do not forget to remove the infinite loops from the functions that run the lights and obviously you would need to declare the variables and set the correct calls.

ESP32 handle webserver and neopixels (with delay) in two cores

I'm searching the answer for a while but haven't found how to fix this problem. I have a lot of neopixel animations (currently 50 of them), these are written with delays which keeps the animations simple and readable. disadvantage is the delays as you all know. 50 animations to rewrite is a hell of a job so I want to avoid this. What I did is bought an ESP32 with two cores, so I thought I could run the animations on one core and the webserver on one core. previously I wanted to do I with an interrupt on a ESP8266 but I red and tested that I was not possible to do communication things or run a webserver in an interrupt for example. Is it possible to do that with two cores on an ESP32 or am I running in to the same problem as earlier? current output of the program: My webserver starts and the neopixel animation is showed when pushing the button, however my webserver continuously restarts over and over again. Any help appreciated, thanks in advance!
ps: In the worst case scenario I have to rewrite my animations, anyone an idea how to replace 3 for loops in to non blocking code?
code running on ESP32:
TaskHandle_t Task1;
TaskHandle_t Task2;
#include <Adafruit_NeoPixel.h>
#define PIN 6
#define NUM_LEDS 24
// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);
void colorWipe(byte red, byte green, byte blue, int SpeedDelay) {
for(uint16_t i=0; i<NUM_LEDS; i++) {
setPixel(i, red, green, blue);
showStrip();
delay(SpeedDelay);
}
}
// *** REPLACE TO HERE ***
void showStrip() {
#ifdef ADAFRUIT_NEOPIXEL_H
// NeoPixel
strip.show();
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
// FastLED
FastLED.show();
#endif
}
void setPixel(int Pixel, byte red, byte green, byte blue) {
#ifdef ADAFRUIT_NEOPIXEL_H
// NeoPixel
strip.setPixelColor(Pixel, strip.Color(red, green, blue));
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
// FastLED
leds[Pixel].r = red;
leds[Pixel].g = green;
leds[Pixel].b = blue;
#endif
}
void setAll(byte red, byte green, byte blue) {
for(int i = 0; i < NUM_LEDS; i++ ) {
setPixel(i, red, green, blue);
}
showStrip();
}
#include <WiFi.h>
const char* WIFI_NAME= "xxxxxxxxxx";
const char* WIFI_PASSWORD = "xxxxxxxxxx";
WiFiServer server(80);
String header;
// Auxiliary variables to store the current output state
String output5State = "off";
String output4State = "off";
// Assign output variables to GPIO pins
const int output5 = 2;
const int output4 = 4;
// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;
String ledState = "off";
void setup() {
//create a task that will be executed in the Task1code() function, with priority 1 and executed on core 0
xTaskCreatePinnedToCore(
Task1code, /* Task function. */
"Task1", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task1, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
delay(500);
//create a task that will be executed in the Task2code() function, with priority 1 and executed on core 1
xTaskCreatePinnedToCore(
Task2code, /* Task function. */
"Task2", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task2, /* Task handle to keep track of created task */
1); /* pin task to core 1 */
delay(500);
Serial.begin(115200);
// Initialize the output variables as outputs
pinMode(output5, OUTPUT);
pinMode(output4, OUTPUT);
// Set outputs to LOW
digitalWrite(output5, LOW);
digitalWrite(output4, LOW);
}
//Task1code: blinks an LED every 1000 ms
void Task1code( void * pvParameters ){
Serial.print("Task1 running on core ");
Serial.println(xPortGetCoreID());
strip.begin();
strip.show(); // Initialize all pixels to 'off'
for(;;){
/*
digitalWrite(output5, HIGH);
delay(1000);
digitalWrite(output5, LOW);
delay(1000);*/
//Serial.print("state:" + ledState);
if(ledState == "on") {
//digitalWrite(output4, HIGH);
colorWipe(0x00,0xff,0x00, 50);
colorWipe(0x00,0x00,0x00, 50);
} else {
strip.clear();
strip.show();
}
}
}
//Task2code: blinks an LED every 700 ms
void Task2code( void * pvParameters ){
Serial.print("Connecting to ");
Serial.println(WIFI_NAME);
WiFi.begin(WIFI_NAME, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print("Trying to connect to Wifi Network");
}
Serial.println("");
Serial.println("Successfully connected to WiFi network");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
for(;;){
WiFiClient client = server.available();
if (client) {
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
currentTime = millis();
previousTime = currentTime;
while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
currentTime = millis();
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
// turns the GPIOs on and off
if (header.indexOf("GET /5/on") >= 0) {
Serial.println("GPIO 5 on");
output5State = "on";
//digitalWrite(output5, HIGH);
} else if (header.indexOf("GET /5/off") >= 0) {
Serial.println("GPIO 5 off");
output5State = "off";
//digitalWrite(output5, LOW);
} else if (header.indexOf("GET /4/on") >= 0) {
Serial.println("GPIO 4 on");
output4State = "on";
ledState = "on";
//digitalWrite(output4, HIGH);
} else if (header.indexOf("GET /4/off") >= 0) {
Serial.println("GPIO 4 off");
output4State = "off";
ledState = "off";
//digitalWrite(output4, LOW);
}
// Display the HTML web page
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS to style the on/off buttons
// Feel free to change the background-color and font-size attributes to fit your preferences
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button { background-color: #195B6A; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
client.println(".button2 {background-color: #77878A;}</style></head>");
// Web Page Heading
client.println("<body><h1>ESP8266 Web Server</h1>");
// Display current state, and ON/OFF buttons for GPIO 5
client.println("<p>GPIO 5 - State " + output5State + "</p>");
// If the output5State is off, it displays the ON button
if (output5State=="off") {
client.println("<p><button class=\"button\">ON</button></p>");
} else {
client.println("<p><button class=\"button button2\">OFF</button></p>");
}
// Display current state, and ON/OFF buttons for GPIO 4
client.println("<p>GPIO 4 - State " + output4State + "</p>");
// If the output4State is off, it displays the ON button
if (output4State=="off") {
client.println("<p><button class=\"button\">ON</button></p>");
} else {
client.println("<p><button class=\"button button2\">OFF</button></p>");
}
client.println("</body></html>");
// The HTTP response ends with another blank line
client.println();
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
}
}
void loop(){
}
animation with three for loops:
void theaterChaseRainbow(int SpeedDelay) { /* for wheel watch higher in the code */
byte *c;
for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel
for (int q=0; q < 3; q++) {
for (int i=0; i < NUM_LEDS; i=i+3) {
c = Wheels( (i+j) % 255);
setPixel(i+q, *c, *(c+1), *(c+2)); //turn every third pixel on
}
showStrip();
delay(SpeedDelay);
for (int i=0; i < NUM_LEDS; i=i+3) {
setPixel(i+q, 0,0,0); //turn every third pixel off
}
}
}
}
You're 95% of the way there. ESP32 runs FreeRTOS with preemptive multi-threading. Your code is quite reasonably divided into two threads (Task1 and Task2) which run on different cores. In fact, the number of cores doesn't really matter much as long as you're not running out of CPU cycles or violating the real-time deadlines of your animations. You could probably run both tasks concurrently on a single core without the ESP32 breaking a sweat.
As for the HTTP server restarting - that's the intended behaviour of your code, is it not? The (rather horribly indented) function Task2code runs a never-ending loop which starts the HTTP server, serves a page and the stops. Is this loop really necessary?

Stop a looping RGB LED with a 4x4 keypad arduino

I am using an RGB LED with a keypad. Pressing '1' turns on a light while pressing '2' turns off the light. After I press '3' I want the LED to loop through colors and only if a different button is pressed is when the code leaves the loop. My problem is while looping the keypads state of HIGH or LOW is not changed therefore the key that is saved as pressed cannot change. I need some way to get out of this loop without stopping the loop.
#include <Keypad.h>
const int GreenLED=9;
const int BlueLED=10;
const int RedLED=11;
const byte numRows=4;
const byte numCols=4;
char keymap[numRows][numCols] =
{
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
};
byte rowPins[numRows] = {5,4,3,2};
byte colPins[numCols] = {13,8,7,6};
char keypressed;
boolean ledPin_stateGreen;
boolean ledPin_stateRed;
boolean ledPin_stateBlue;
Keypad myKeypad = Keypad(makeKeymap(keymap), rowPins, colPins, numRows,
numCols);
void setup() {
pinMode(GreenLED, OUTPUT);
pinMode(BlueLED, OUTPUT);
pinMode(RedLED, OUTPUT);
ledPin_stateGreen = digitalRead(GreenLED);
ledPin_stateRed = digitalRead(RedLED);
ledPin_stateBlue = digitalRead(BlueLED);
Serial.begin(9600);
}
void loop() {
char key = myKeypad.getKey();
if(key != NO_KEY)
{
Serial.println(key);
}
//Serial.println(myKeypad.getState());
keypadEvent(key);
}
void setColor(int red, int green, int blue)
{
#ifdef COMMON_ANODE
red = 255 - red;
green = 255 - green;
blue = 255 - blue;
#endif
analogWrite(RedLED, red);
analogWrite(GreenLED, green);
analogWrite(BlueLED, blue);
}
void keypadEvent (KeypadEvent key)
{
switch (myKeypad.getState())
{
case PRESSED:
if (key == '1')
{
digitalWrite(GreenLED, HIGH);
digitalWrite(BlueLED, HIGH);
digitalWrite(RedLED, HIGH);
}
if (key == '2')
{
digitalWrite(GreenLED, LOW);
digitalWrite(BlueLED, LOW);
digitalWrite(RedLED, LOW);
}
if (key == '3')
{
int previousState= myKeypad.getState();
while(key == '3')
{
key = myKeypad.getKey();
setColor(255, 0, 0); // red
delay(200);
Serial.println(myKeypad.getState());
setColor (50,50,50); //white
delay (200);
setColor (255,40,0);
delay(200);
setColor(0, 255, 0); // green
delay(200);
setColor(0, 0, 255); // blue
delay(200);
setColor(255, 255, 0); // yellow
delay(200);
setColor(80, 0, 80); // purple
delay(200);
setColor(0, 255, 255); // aqua
delay(200);
Serial.println(myKeypad.getState());
}
}
}
}
In your setup, you have GreenLED, BlueLED, RedLED set to OUTPUT, but then you try to digitalRead() from them...
void setup() {
pinMode(GreenLED, OUTPUT);
pinMode(BlueLED, OUTPUT);
pinMode(RedLED, OUTPUT);
ledPin_stateGreen = digitalRead(GreenLED);
ledPin_stateRed = digitalRead(RedLED);
ledPin_stateBlue = digitalRead(BlueLED);
Serial.begin(9600);
}
Your loop is preventing the keypad from being read for about 1.6 seconds. Then you have a very very small window to have the key pressed to pickup the key. Also, as you have stated, once you are in the loop you can't exit out of it due to the fact that you don't check for key presses. The tutorial on Arduino Playground states:
Consider, though, when you are writing your code that every delay()
you use will take processing time away from the keypad. Something as
short as delay(250) can make the keypad seem very unresponsive. And
the same thing will happen if you sprinkle a bunch of delay(10)'s all
through your code.
One way to solve this problem is to remove the delays from your loop and turn your program into a state machine which continuously polls the keypad for key presses. Now there are many ways to do this, of which I have chosen only one, and actually I really have turned it into 2 state machines. The top level one is the overall state of your program (i.e. what is our state based on the key pressed). The second one is a state machine to represent your loop which changes the colors of your LEDs. You can learn more about state machines here.
Here is your program turned into the above state machines:
#include <Keypad.h>
const int GreenLED=9;
const int BlueLED=10;
const int RedLED=11;
const byte numRows=4;
const byte numCols=4;
char keymap[numRows][numCols] =
{
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
};
byte rowPins[numRows] = {5,4,3,2};
byte colPins[numCols] = {13,8,7,6};
char keypressed;
boolean ledPin_stateGreen;
boolean ledPin_stateRed;
boolean ledPin_stateBlue;
enum MyState {
LIGHT_ON,
LIGHT_OFF,
LIGHT_LOOPING
};
enum LightState {
COLOR_1,
COLOR_2,
COLOR_3,
COLOR_4,
COLOR_5,
COLOR_6,
COLOR_7,
COLOR_8
};
//Our current state for lights
MyState currentState = LIGHT_LOOPING;
LightState currentLightState = COLOR_1;
//The previous time in milliseconds
unsigned long prevTimeMS = 0;
Keypad myKeypad = Keypad(makeKeymap(keymap), rowPins, colPins, numRows,
numCols);
void setup() {
pinMode(GreenLED, OUTPUT);
pinMode(BlueLED, OUTPUT);
pinMode(RedLED, OUTPUT);
ledPin_stateGreen = digitalRead(GreenLED);
ledPin_stateRed = digitalRead(RedLED);
ledPin_stateBlue = digitalRead(BlueLED);
Serial.begin(9600);
prevTimeMS = millis();
}
void loop() {
char key = myKeypad.getKey();
if(key != NO_KEY)
{
Serial.println(key);
}
//Serial.println(myKeypad.getState());
//This function is really checking to see if we need to perform a state
//transition or not for the currentState
keypadEvent(key);
//Do stuff based on the state We are in
unsigned long currentTimeMS = millis();
switch(currentState)
{
case LIGHT_ON:
//Don't really need to do anything since we perform
//the work on the state transition
break;
case LIGHT_OFF:
//Don't really need to do anything since we perform
//the work on the state transition
break;
case LIGHT_LOOPING:
//Now switch based on the current color state to see if we
//need to change to the next state
switch(currentLightState)
{
case COLOR_1:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_2);
}
break;
case COLOR_2:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_3);
}
break;
case COLOR_3:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_4);
}
break;
case COLOR_4:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_5);
}
break;
case COLOR_5:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_6);
}
break;
case COLOR_6:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_7);
}
break;
case COLOR_7:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
transitionLightState(COLOR_8);
}
break;
case COLOR_8:
if(checkDelay(currentTimeMS,prevTimeMS,200))
{
//We need to transition to the next state
//which is back to the first state so we loop
transitionLightState(COLOR_1);
}
break;
}
break;
}
}
//This will return true if the correct amount of time has passed
boolean checkDelay(unsigned long currentMS, unsigned long prevMS, unsigned long delayMS)
{
if((currentMS - prevMS) >= delayMS)
{
return true;
}
return false;
}
void transitionMyState(MyState newState)
{
switch(newState)
{
case LIGHT_ON:
digitalWrite(GreenLED, HIGH);
digitalWrite(BlueLED, HIGH);
digitalWrite(RedLED, HIGH);
break;
case LIGHT_OFF:
digitalWrite(GreenLED, LOW);
digitalWrite(BlueLED, LOW);
digitalWrite(RedLED, LOW);
break;
case LIGHT_LOOPING:
//We want to transition to the COLOR_1 state here
transitionLightState(COLOR_1);
break;
}
currentState = newState;
//need to save off a new prevTimeMS
prevTimeMS = millis();
}
void transitionLightState(LightState newState)
{
//perform the action for the state transition
switch(newState)
{
case COLOR_1:
setColor(255, 0, 0); // red
break;
case COLOR_2:
setColor (50,50,50); //white
break;
case COLOR_3:
setColor (255,40,0);
break;
case COLOR_4:
setColor(0, 255, 0); // green
break;
case COLOR_5:
setColor(0, 0, 255); // blue
break;
case COLOR_6:
setColor(255, 255, 0); // yellow
break;
case COLOR_7:
setColor(80, 0, 80); // purple
break;
case COLOR_8:
setColor(0, 255, 255); // aqua
break;
}
currentLightState = newState;
//need to save off a new prevTimeMS
prevTimeMS = millis();
}
void setColor(int red, int green, int blue)
{
#ifdef COMMON_ANODE
red = 255 - red;
green = 255 - green;
blue = 255 - blue;
#endif
analogWrite(RedLED, red);
analogWrite(GreenLED, green);
analogWrite(BlueLED, blue);
}
void keypadEvent (KeypadEvent key)
{
switch (myKeypad.getState())
{
case PRESSED:
if (key == '1')
{
transitionMyState(LIGHT_ON);
}
if (key == '2')
{
transitionMyState(LIGHT_OFF);
}
if (key == '3')
{
transitionMyState(LIGHT_LOOPING);
}
}
}
The implementation is more for readability then minimizing code by the way. I added 2 state variables which hold what state we are in for the key handler state machine and the looping state machine. The important thing to note about this implementation is that key presses are checked every iteration of the loop and there are no delays implemented. This allows for us to break out of the looping color state machine at anytime. You could also say that the looping of colors changed into a poll of "Do I need to change my color now?"
The other important thing to note is that now the time needs to be tracked so the looping state machine can determine when the right amount of time has passed in order to know when to change states. When a state transition happens then the current time is saved off. Note: The rollover of the millis counter is not taken into account in this example. This happens roughly every 50 days according to the Arduino docs. So every 50 days you will get a glitch if you are in the LIGHT_LOOPING state.

Getting an Arduino function to loop within a switch case

I'm trying to get the function flash() to repeat, but it stops when clickevent() occurs again and case 0 becomes active.
I think my error is that case 0 is not becoming active because it's stuck on case 1 maybe? I can get it to start using while() statements or do while() statements but then I can't exit the loop, any help would be very appreciated.
// ======== change the lighting mode setting between flash and solid with a single click of the button ===========
int ModeSetting = 0;
void clickEvent()
{
Serial.println("Click Occured");
ModeSetting++;
if (ModeSetting > 1)
ModeSetting = 0;
ChangeMode(ModeSetting);
}
void ChangeMode(int i)
{
switch (i)
{
case 0:
strip.setBrightness(255);
Serial.println("MODE = SOLID COLOUR"); // SOLID LIGHT;
break;
case 1:
flash(); // THE FUNCTION THAT NEEDS TO LOOP pls help
break;
}
}
int flashstate = LOW;
unsigned long PrevFlashMillis = 0;
const long flashinterval = 1000;
void flash()
{
unsigned long currentFlashMillis = millis();
if (currentFlashMillis - PrevFlashMillis >= flashinterval)
{
// save the last time you blinked the LED
PrevFlashMillis = currentFlashMillis;
// if the LED is off turn it on and vice-versa:
if (flashstate == LOW)
{
strip.setBrightness(255);
Serial.println("FLASH ON");
flashstate == HIGH;
}
else
{
strip.setBrightness(0);
Serial.println("FLASH OFF");
flashstate == LOW;
}
}
}

arduino interrupts with servo motor

currently am working on project to open a door with access code using arduino UNO and a servo motor. Normal operation requires entering access code using keypad which is working fine. Another option requires pressing a button that causes an interrupt to rotate the servo motor. My problem is my interrupt only works once and never works again. Plus how do i put the for-loop to rotate the servo motor inside the interrupt function with a delay. I know that is not possible but am calling another function that has the delayMicroseconds but all this is not working. Below is my implementation please help
#include <Keypad.h>
#include <LiquidCrystal.h>
#include <Servo.h>
Servo servo;
const int openButtonPin = 2;
void setup() {
// put your setup code here, to run once:
servo.attach(5);
pinMode(openButtonPin, INPUT); //Pin 2 is input
attachInterrupt(0, enforceOpenAccess, HIGH); // PIN 2
}
void(* resetFunc)(void) = 0;
void loop()
{
//My other keypad implementations go here
}
void myDelay(int x) // function to cause delay in the interrupt
{
for(int i = 0; i<x; i++)
{
delayMicroseconds(1000);
}
}
void enforceOpenAccess() // ISR
{
for(int k =0; k<=180; k+=2)
{
servo.write(k); //rotate the servo
myDelay(30); //delay the rotation of the servo
}
}
The code above is run on arduino UNO being simulated in proteus and the interrupt button is a push button. Please if there is other ways of implementing that but with the same behaviour as I have described above help out. Thanks a lot
There are a couple of problems in the slice of code you posted. Just for completeness, you should post the loop function, since we can't guess what you wrote inside.
Just one comment: did you put a pullup? Otherwise use INPUT_PULLUP instead of INPUT for the button pinmode.
The main one is that you attached the interrupt for the HIGH mode, which will trigger the interrupt any time the pin is up, not on the rising edge. And please use the macro digitalPinToInterrupt to map to the correct pin:
attachInterrupt(digitalPinToInterrupt(openButtonPin), enforceOpenAccess, RISING);
Then.. Let's improve the code. You really should use the interrupts only when strictly necessary when you have to respond IMMEDIATELY (= less than a couple of milliseconds) to an input. Here you don't have to, so it's MUCH better to check for the button in the loop (more on turning the motor following)
uint8_t lastState;
void setup()
{
...
lastState = LOW;
}
void loop()
{
uint8_t currentState = digitalRead(openButtonPin);
if ((currentState != lastState) && (currentState == HIGH))
{
// Start turning the motor
}
lastState = currentState;
...
}
This will enable you to properly debounce the button too:
#include <Bounce2.h>
Bounce debouncer = Bounce();
void setup()
{
...
pinMode(openButtonPin, INPUT); //Pin 2 is input
debouncer.attach(openButtonPin);
debouncer.interval(5); // interval in ms
}
void loop()
{
debouncer.update();
if (debouncer.rose())
{
// Start turning the motor
}
...
}
If, on the other way, you REALLY want to use the interrupts (because waiting for a couple of milliseconds is too much for you), you should do something like this:
#include <Bounce2.h>
Bounce debouncer = Bounce();
void setup()
{
...
pinMode(openButtonPin, INPUT);
attachInterrupt(digitalPinToInterrupt(openButtonPin), enforceOpenAccess, RISING);
}
void loop()
{
...
}
void enforceOpenAccess() // ISR
{
// Start turning the motor
}
It looks like your code? No, because now we'll speak about turning the motor
You should NOT use delays to make steps, because otherwise you will wait for 30ms * 180 steps = 5.4s before being able to do anything else.
You can, however, make a sort of reduced state machine. You want your servo to move from 0 to 180 in steps of 1. So let's code the "don't move" state with any value greater than 180, and consequently we can do something like this in the loop:
unsigned long lastServoTime;
uint8_t servoPosition = 255;
const int timeBetweenSteps_in_ms = 30;
void loop()
{
...
if (servoPosition <= 180)
{ // servo should move
if ((millis() - lastServoTime) >= timeBetweenSteps_in_ms)
{
lastServoTime += timeBetweenSteps_in_ms;
servoPosition++;
if (servoPosition <= 180)
servo.write(servoPosition);
}
}
}
Then, using any of the previous examples, instead of // Start turning the motor write
lastServoTime = millis();
servoPosition = 0;
servo.write(servoPosition);
This way you won't block the main loop even when the button is pressed
This is what is in my loop()
char key = keypad.getKey();
if(key)
{
if(j < 10)
{
studentNumber[j] = key;
//holdMaskedNumber[j] = '*';
lcd.setCursor(0,2);
lcd.print(String(studentNumber));
if(j == 9)
{
studentNumber[9] = '\0';
//holdMaskedNumber[9] = 0;
lcd.clear();
//String number = String(studentNumber);
//lcd.print(number);
//delay(1000);
//lcd.clear();
lcd.print("Access Code");
}
j++;
}
else
{
if(i < 5)
{
accessCode[i] = key;
holdMaskedCode[i] = '*';
lcd.setCursor(1,2);
lcd.print(String(holdMaskedCode));
if(i == 4)
{
holdMaskedCode[5] = '\0';
accessCode[5] = '\0';
//lcd.clear();
//lcd.setCursor(0,0);
//accessCodeString = String(accessCode);
//lcd.print(accessCodeString);
//delay(1000);
lcd.clear();
for(int i =0; i<6; i++)
{
lcd.print("Please wait.");
delay(500);
lcd.clear();
lcd.print("Please wait..");
delay(500);
lcd.clear();
lcd.print("Please wait...");
delay(500);
lcd.clear();
}
digitalWrite(4, HIGH);
lcd.print("Access Granted");
for(int k =0; k<=180; k+=2)
{
servo.write(k);
delay(30);
}
resetFunc();
}
i++;
}
}
}

Resources