I'm using Arduino to control a project which contains addressable LEDs and buttons.
The libraries I'm using are in the title.
For an unknown reason, when I use each code separately everything works fine, but when I combine them the FastLED library blocks the button reading and causes weird things - like giving double press, or stop doing other things.
How can I fix the issue? (I tried to eliminate the delay(), but it wasn't helpful)
Thanks in advance!
/*
Author: Yuval Kedar - KD Technology
Instagram: https://www.instagram.com/kd_technology/
Date: Oct 19
Dev board: Arduino Uno
There are two button types: Red and Blue.
Red btn = -1
Blue btn = +1
On the machine's background there is an LED matrix which shows the user's progress.
There are 4 levels (square frames) until one reaches the core and wins.
The trick? The floor, which includes the buttons, is spinning.
*/
#include <FastLED.h>
#include <ezButton.h>
#include "Arduino.h"
#define WINNING_SENSOR_PIN (12)
#define LED_DATA_PIN (6)
#define BLUE_BTN_PIN (A0)
#define RED_BTN_PIN (A3)
#define SERIAL_BAUDRATE (115200)
#define NUM_LEDS (64)
#define LED_BRIGHTNESS (200)
#define WINNING_FX_TIME (1000)
ezButton blue_btn(BLUE_BTN_PIN);
ezButton red_btn(RED_BTN_PIN);
CRGB leds[NUM_LEDS];
uint8_t score = 0;
uint8_t last_score = 0;
uint8_t level[] = {0, 28, 48, 60, 63}; //levels 0 to 4
void level_up(uint8_t led_num) {
uint8_t start_point = 0;
if (led_num == level[1]) start_point = 0; //up from level 0 to 1
if (led_num == level[2]) start_point = 28; //up from level 1 to 2
if (led_num == level[3]) start_point = 48; //up from level 2 to 3
if (led_num == level[4]) start_point = 60; //...
for (uint8_t current_pixel = start_point; current_pixel < led_num; current_pixel++) {
leds[current_pixel] = CRGB::Blue;
FastLED.show();
delay(50);
}
delay(2500); //debounce
}
void level_down(uint8_t led_num) { //clear prev level's frame and do the opposite direction effect with red color
uint8_t start_point = 0;
if (led_num == level[0]) start_point = 28; //down from level 1 to 0
if (led_num == level[1]) start_point = 48; //down from level 2 to 1
if (led_num == level[2]) start_point = 60; //down from level 3 to 2
if (led_num == level[3]) start_point = 63; //...
for (int8_t i = start_point - 1; i > led_num; i--) {
leds[i] = CRGB::Red;
FastLED.show();
delay(50);
}
for (int8_t i = start_point - 1; i > led_num; i--) {
leds[i] = CRGB::Black;
FastLED.show();
}
delay(2500); //debounce
}
void fadeall() {
for(uint8_t i = 0; i < NUM_LEDS; i++) {
leds[i].nscale8(250);
}
}
void winning() {
static uint8_t hue = 0;
for(uint8_t x = 0; x < 5; x++) {
for(int8_t i = 0; i < NUM_LEDS; i++) {
leds[i] = CHSV(hue++, 255, 255);
FastLED.show();
fadeall();
// delay(10);
}
for(int8_t i = (NUM_LEDS)-1; i >= 0; i--) {
leds[i] = CHSV(hue++, 255, 255);
FastLED.show();
fadeall();
// delay(10);
}
}
}
void reset_game() {
score = 0;
last_score = 4;
digitalWrite(WINNING_SENSOR_PIN, LOW);
FastLED.clear();
FastLED.show();}
void winning_check() {
(score == 4) ? analogWrite(WINNING_SENSOR_PIN, 175) : digitalWrite(WINNING_SENSOR_PIN, LOW);
}
void update_score() {
if (blue_btn.isPressed()) {
Serial.println("+PLUS+");
if (score++ >= 4) score = 4;
}
if (red_btn.isPressed()) {
Serial.println("-MINUS-");
if (score-- <= 0) score = 0;
}
if (score == 0){
if (last_score == 1) level_down(level[0]);
last_score = 0;
digitalWrite(WINNING_SENSOR_PIN, LOW);
}
else if (score == 1) {
if (last_score == 0) level_up(level[1]); // if last_score was 0 make the blue effect because level is up
if (last_score == 2) level_down(level[1]); // if last_score was 2 make the red effect because level is down
last_score = 1;
digitalWrite(WINNING_SENSOR_PIN, LOW);
}
else if (score == 2) {
if (last_score == 1) level_up(level[2]);
if (last_score == 3) level_down(level[2]);
last_score = 2;
digitalWrite(WINNING_SENSOR_PIN, LOW);
}
else if (score == 3) {
if (last_score == 2) level_up(level[3]);
if (last_score == 4) level_down(level[3]);
last_score = 3;
digitalWrite(WINNING_SENSOR_PIN, LOW);
}
else if (score == 4) {
winning_check();
// winning(); //this func makes issue when using ezButton.h. It calls "show" too many times.
reset_game();
}
}
void setup() {
Serial.begin(SERIAL_BAUDRATE);
pinMode(WINNING_SENSOR_PIN, OUTPUT);
digitalWrite(WINNING_SENSOR_PIN, LOW);
blue_btn.setDebounceTime(150);
red_btn.setDebounceTime(150);
FastLED.addLeds<NEOPIXEL, LED_DATA_PIN>(leds, NUM_LEDS); // GRB ordering is assumed
FastLED.setBrightness(LED_BRIGHTNESS);
FastLED.clear();
FastLED.show();
Serial.println(F(
"_______________________________\n"
"\n"
" G e a r M a c h i n e \n"
"_______________________________\n"
"\n"
" ~ Made by KD Technology ~ \n"
"\n"));
}
void loop() {
blue_btn.loop();
red_btn.loop();
Serial.println(score);
update_score();
FastLED.show();
}
The reason is that, you used the delay() function. If you use the delay, some pressed events will be missed, not only for ezButton, but also for any kind of button press implementation. The ezButton already has the internal debounce function.
Related
I'm getting :
invalid types 'int[int]' for array subscript
when I try to compile my code for my Arduino and for the life of me I can't figure out what I'm doing wrong.
Here's my code (feel free to use it although it isn't done yet).
/*
* Changelog 0.5
* pins[], pinFV[], pinV[], pinDelay[] moved
* to pins[][]
*/
int analogPins[6] = {A0, A1, A2, A3, A4, A5};
int pins[4][4];
int mode = 0;
void setup() {
int x = 6;
do {
pinMode(analogPins[x], INPUT);
x--;
} while (x > 0);
int pins[4] = {9, 5, 6, 3};
x = 4;
do {
pinMode(pins[0][x], INPUT);
x--;
} while (x > 0);
x = 4;
do{
pins[1][x] = random(5, 10);
x--;
}while(x>0);
x = 4;
do{
pins[2][x] = random(50, 75);
x--;
}while(x>0);
x = 4;
do{
pins[3][x] = 255;
x--;
}while(x>0);
//this is the section causing the problem
pins[0][0] = 8;
pins[0][1] = 9;
pins[0][2] = 10;
pins[0][3] = 11;
//end section
pinMode(8, OUTPUT);
Serial.begin(19200);
}
void readAnalogDecode() {
//analogAverage(pin);
//elseIf statements to set what mode to use
//catch anything out of bounds for mode select
if (analogRead(A5) > 800 || analogRead(A5) < 0) {
mode = 0;
}
if (analogRead(A5) > 4 && analogRead(A5) < 8) {
mode = 0;
}
if (analogRead(A5) > 8 && analogRead(A5) < 15) {
mode = 1;
}
if (analogRead(A5) > 15 && analogRead(A5) < 20) {
mode = 2;
}
if (analogRead(A5) > 20 && analogRead(A5) < 26) {
mode = 3;
}
if (analogRead(A5) > 26 && analogRead(A5) < 47) {
mode = 4;
}
if (analogRead(A5) > 47 && analogRead(A5) < 185) {
mode = 5;
}
if (analogRead(A5) > 185 && analogRead(A5) < 345) {
mode = 6;
}
}
void pinFader(int pin, int value) {
digitalWrite(8, LOW);
if (pins[3][pin] < value) {
do {
pins[3][pin] = pins[3][pin] + pins[1][pin];
if (pins[3][pin] > 255) {
pins[3][pin] = 255;
}
Serial.println(analogRead(A5));
Serial.println(" ");
Serial.println(analogRead(A4));
analogWrite(pins[0][pin], pins[3][pin]);
digitalWrite(8, LOW);
delay(pins[2][pin]);
} while (pins[3][pin] < value);
}
if (pins[3][pin] > value) {
do {
pins[3][pin] = pins[3][pin] - pins[1][pin];
if (pins[3][pin] < 0 ) {
pins[3][pin] = 0;
}
digitalWrite(8, LOW);
analogWrite(pins[0][pin], pins[3][pin]);
delay(pins[2][pin]);
} while (pins[3][pin] > value);
}
}
void loop() {
Serial.println(" " );
Serial.print(analogRead(A0));
Serial.print(" " );
Serial.print(analogRead(A1));
Serial.print(" " );
Serial.print(analogRead(A2));
Serial.print(" " );
Serial.print(analogRead(A3));
Serial.print(" " );
Serial.print(analogRead(A4));
Serial.print(" " );
Serial.print(analogRead(A5));
Serial.print(" " );
Serial.print(mode);
readAnalogDecode();
if (mode == 0) {
pinFader(1, 255); //no green
pinFader(2, 255); //no red
pinFader(1, 0); //green on
pinFader(3, 255); //no blue
pinFader(2, 0); //red on
pinFader(1, 255); //no green
pinFader(3, 0); //blue on
pinFader(1, 0); //green on
readAnalogDecode();}
if (mode == 1) {
analogWrite(9, 0);
delay(500);
analogWrite(9, 255);
/* delay(500);
analogWrite(10, 0);
delay(500);
analogWrite(10, 255);
delay(500);
analogWrite(11, 0);
delay(500);
analogWrite(11, 255);
*/delay(500);
readAnalogDecode();
}
}
Is this not how to assign a value to a 2-d array? pins[0][0] = 8;
I apologize in advance if the problem is related to syntax. I'm used to java and python.
You have 2 arrays with the same name. Inside setup the local one is the one that is referred to and it isn't a 2D array so you can't use it as such.
At global scope:
int pins[4][4];
And inside setup:
int pins[4] = {9, 5, 6, 3};
Try naming one of them something different. Having two things with the same name can be terribly confusing.
I am using the FastLED library and the Keypad library to run code on my WS2812 lights. When I used just a basic keypad layout to print out what key was being pressed, this code worked. But now that I added methods to run code from the keypad if statements, it seems as if it is not working. Nothing happens to the lights when I press a number on the keypad. Currently the LED code is in methods. I had previously just had it in the if statements for the keypad itself. I'm going to keep the code included here as concise as possible as to not flood the screen with what I think isn't relevant. If you have any questions I can provide relevant snippets of the rest of the code.
void setup() {
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(32);
Serial.begin(9600);
}
void christmas() {
for(int i = 0; i < 6; i++){
for(int i = 0; i > 299; i++) {
leds[i] = CRGB (0, 128, 0); //green
FastLED.show();
leds[i+1] = CRGB (255, 0, 0); //red
FastLED.show();
delay(20);
}
//the second loop turns the first light red and the second light green starting at the end
for (int i = 299; i <= 0; i--) {
leds[i] = CRGB (255, 0, 0); //green
FastLED.show();
leds[i + 1] = CRGB (0, 128, 0); //red
FastLED.show();
delay(20);
}
}
}
void loop() {
char key = keypad.getKey(); //determines what key is pressed
if (key == '1'){
christmas();
}
if (key == '2'){
dot();
}
if (key == '3'){
mavericks();
}
if (key == '4'){
rainbow();
}
if (key == '5'){
blueBar();
}
if (key == '6'){
purple();
}
To be clear there is no error when running the code. It runs fine. But, it does nothing when the keys are pressed on the keypad, where as it should, I believe, run the methods called and effect the lights accordingly.
Edit:
After implementing Bobby Tables suggestions, it works to an extent. Except now When I press 1, which is:
void christmas() {
delay(10);
for(int j = 0; j < 6; j++){
delay(10);
for(int i = 0; i > 299; i++) {
delay(10);
leds[i] = CRGB::Red; //green
FastLED.show();
leds[i+1] = CRGB::Green; //red
FastLED.show();
delay(20);
}
//the second loop turns the first light red and the second light green starting at the end
for (int i = 299; i >= 0; i--) {
leds[i] = CRGB::Green; //green
FastLED.show();
leds[i + 1] = CRGB::Red;
FastLED.show();
delay(20);
}
}
}
it runs instantly but if I press any other key it does not fully function. Such as when I press 5 which is:
void blueBar() {
delay(10);
fill_solid(leds, NUM_LEDS, CRGB::White);
delay(10);
for(int j = 0; j < 6; j++) {
delay(10);
for(int i = 0; i < 299; i++) {
leds[i] = CRGB::Blue;
leds[i + 1] = CRGB::Blue;
leds[i + 2] = CRGB::Blue;
leds[i + 3] = CRGB::Blue;
leds[i + 4] = CRGB::Blue;
leds[i + 5] = CRGB::Blue;
FastLED.show();
delay(200);
leds[i] = CRGB::White;
FastLED.show();
}
}
}
It does nothing. Except when I press 5 and then press 1 afterwards. It will fill the initial solid white but then start running christmas().
I now believe that your if statements are working fine, but as a result of incorrect formation of your loop conditionals, christmas() does nothing. I suspect you have similar problems in your other lighting patterns.
This is using i for both loops. Also the second loop will never run since i > 299 is immediately false:
for(int i = 0; i < 6; i++){
for(int i = 0; i > 299; i++) {
This also will never run since i is immediately greater than 0:
for (int i = 299; i <= 0; i--) {
I use Neopixels (64 LEDs), and I have a function called level_up that gets different led_num each time. Generally, it is a level bar; level[1] will light all the LEDs in a sequence from 0 to 28, level[2] all the LEDs from 29 to 48, etc.
The function I attached works fine, but I need to change the delay to millis() and not sure how. Any thoughts?
uint8_t level[] = {0, 28, 48, 60, 64}; //levels 0 to 4
void level_up(uint8_t wait, uint8_t led_num) {
uint8_t start_point;
if (led_num == level[1]) start_point = 0; //up from level 0 to 1
if (led_num == level[2]) start_point = 28; //up from level 1 to 2
if (led_num == level[3]) start_point = 48; //up from level 2 to 3
if (led_num == level[4]) start_point = 60; //...
for (uint8_t i = start_point; i < led_num; i++) {
strip.setPixelColor(i, strip.Color(0, 0, 255));
strip.show();
delay(wait); //TODO: change it to timer
}
}
void loop() {
if (plus_btn.pressed()) {
score++;
if (score >= 4) {
score = 4;
}
}
if (minus_btn.pressed()) {
score--;
if (score <= 0) {
score = 0;
}
}
switch (score) {
case 0:
if (last_score == 1) level_down(50, level[0]);
last_score = 0;
break;
case 1:
// if last_score was 0 make the blue effect because level is up
if (last_score == 0) level_up(50, level[1]);
// if last_score was 2 make the red effect because level is down
if (last_score == 2) level_down(50, level[1]);
last_score = 1;
break;
case 2:
if (last_score == 1) level_up(50, level[2]);
if (last_score == 3) level_down(50, level[2]);
last_score = 2;
break;
case 3:
if (last_score == 2) level_up(50, level[3]);
if (last_score == 4) level_down(50, level[3]);
last_score = 3;
break;
case 4:
winning_timer.start();
winning();
digitalWrite(WINNING_SENSOR_PIN, HIGH);
break;
}
Serial.println(score);
}
Using millis() will not block the for-loop like delay().
Therefore I think you will have to adapt the code that is calling your method, because at the moment it looks like your code depends on being blocked in the for-loop.
But generally you would use millis() like in the code sample below. You store the start-timestamp and then do something after your wait-period is over.
uint8_t level[] = {0, 28, 48, 60, 64}; //levels 0 to 4
uint8_t counter;
uint8_t end_point;
bool show_level;
void level_up(uint8_t wait, uint8_t led_num) {
if (led_num == level[1]) counter = 0; //up from level 0 to 1
if (led_num == level[2]) counter = 28; //up from level 1 to 2
if (led_num == level[3]) counter = 48; //up from level 2 to 3
if (led_num == level[4]) counter = 60; //...
show_level =true;
end_point = led_num;
}
bool set_pixel_color(uint8_t wait)
{
if(timestamp - millis() == wait)
{
strip.setPixelColor(counter, strip.Color(0, 0, 255));
strip.show();
timestamp = millis();
return true; // incremented
}
return false;
}
void show_level_led_strip()
{
if(show_level)
{
if(counter > end_point) // escape when the counter gets bigger then the current led_num
{
show_level = false;
}
else
{
if(set_pixel_color(50))
{
counter++;
}
}
}
}
void loop() {
if (plus_btn.pressed()) {
score++;
if (score >= 4) {
score = 4;
}
}
if (minus_btn.pressed()) {
score--;
if (score <= 0) {
score = 0;
}
}
switch (score) {
case 0:
if (last_score == 1) level_down(level[0]);
last_score = 0;
break;
case 1:
// if last_score was 0 make the blue effect because level is up
if (last_score == 0) level_up(level[1]);
// if last_score was 2 make the red effect because level is down
if (last_score == 2) level_down(level[1]);
last_score = 1;
break;
case 2:
if (last_score == 1) level_up(level[2]);
if (last_score == 3) level_down(level[2]);
last_score = 2;
break;
case 3:
if (last_score == 2) level_up(level[3]);
if (last_score == 4) level_down(level[3]);
last_score = 3;
break;
case 4:
winning_timer.start();
winning();
digitalWrite(WINNING_SENSOR_PIN, HIGH);
break;
}
show_level_led_strip();
}
Serial.println(score);
This doesn't answer your question directly, but the strategy that I use gives me any number of timed events without my program blocking in millis().
Set a deadline in the future and enclose the delayed action in an if statement that polls millis() until that deadline is reached. It's not perfect because the software timing loses time due to processing, and because of the millis() overflow and wrap-around issue (look it up on arduino.cc).
/* Global variables (constexpr creates a compile time only constant) */
constexpr uint32_t WAIT_INTERVAL = 10; // interval is 10ms
uint32_t deadline = 0; // when to run next
// inside loop()
uint32_t now = millis(); // capture the current millis() value
if(now >= deadline)
{
deadline = now + WAIT_INTERVAL; // push the next deadline into the future
// perform timed periodic operations here (call function or whatever)
}
After reading your post and all comments, I think I know what you want. You just want that the loop continues without stay in this function during the delay right?
millis() does not sleep or delay, it just gives you the time in milliseconds, since the Arduino is running.
So, you can just add this to your code and it will work:
uint8_t level[] = {0, 28, 48, 60, 64}; //levels 0 to 4
unsigned long lastTime = 0; // << add this
uint8_t start_point = 0; // << move here
void update_leds(uint16_t wait, uint8_t led_num) {
if(start_point >= led_num) return;
if(millis() - lastTime > wait) { // << add this
//uint8_t start_point;
lastTime = millis(); // << add this
//for (uint8_t i = start_point; i < led_num; i++) {
strip.setPixelColor(start_point, strip.Color(0, 0, 255));
strip.show();
//delay(wait); // << remove this
//}
start_point++;
}
}
void level_up(uint8_t led_num) {
if (led_num == level[1]) start_point = 0; //up from level 0 to 1
if (led_num == level[2]) start_point = 28; //up from level 1 to 2
if (led_num == level[3]) start_point = 48; //up from level 2 to 3
if (led_num == level[4]) start_point = 60; //...
}
change wait from uint8_t to uint16_t, since 255 could be too little.
Now, you can call this function many times, but the leds are updated only when the timeout finished.
There is only one problem: if inside your loop you have other delays, maybe the leds are updated some milliseconds later than expected... If you understand what I mean.
Edit: some times, you want also be notified if the leds was updated. So you can return a bool to say if the function updated the leds or not (maybe you need it in the loop, to check if the it "leveled up".
I'm currently trying to make a RC car on Arduino and facing the following issue, where I'm unable to reset the L298P Motor Driver (By Keyes) Shield to stop motor from moving (Using only one motor at Motor B port).
Basically, the motor sticks to one direction rotating and not stopping when JoyStick's position is resetted.
Here's the current code I'm using. (Also Includes my pathetic trials).
#include <Servo.h>
Servo myservo;
#include <LiquidCrystal.h>
LiquidCrystal lcd(2, 3, 5, 6, 7, 8);
int JoyStick_X = 0; //Analog
int JoyStick_Y = 1; //Analog
int JoyStick_Z = 1; //Digital
int SpeedPin = 11;
int Direction = 13;
void setup(void) {
myservo.attach(9);
lcd.begin(16, 2);
lcd.clear();
pinMode(JoyStick_Z, INPUT_PULLUP);
pinMode(Direction, OUTPUT);
Serial.begin(9600);
}
void loop(void) {
int x, y, z;
x = analogRead(JoyStick_X);
y = analogRead(JoyStick_Y);
z = digitalRead(JoyStick_Z);
lcd.setCursor(0, 0);
lcd.print("Car Status");
lcd.setCursor(0, 1);
lcd.print("Spd:");
lcd.print(x);
/* if (z == 0) {
lcd.print("High");
}
else if (z == 0 && x < 250) {
lcd.print("RHgh");
}
else if (x > 510 && x < 530) {
lcd.print("No ");
}
else if (x > 530 && x < 730) {
lcd.print("Low ");
}
else if (x > 730) {
lcd.print("Med ");
}
else if (x < 510 && x > 250) {
lcd.print("RLow");
}
else if (x < 250) {
lcd.print("RMed");
}*/
lcd.print(" ");
lcd.setCursor(10, 1);
lcd.print("Trn:");
if (y < 500) {
lcd.print("L ");
}
else if (525 > y && y > 500) {
lcd.print("-");
}
else if (y > 525) {
lcd.print("R ");
}
lcd.setCursor(11, 0);
lcd.print("Bst:");
if (z == 0) {
lcd.print("Y");
}
else {
lcd.print("N");
}
myservo.write(40 + y * 8.7890625 / 100);
if (x > 530) {
digitalWrite(Direction, HIGH);
}
else if (x < 510) {
digitalWrite(Direction, LOW);
}
if (x > 510 && x < 530) {
digitalWrite(0, 0);
}
//int speed = ((0.520408 * x ) - 275.816326 );
analogWrite(SpeedPin, 255);
/*int value = 255;
digitalWrite(M1,LOW);
analogWrite(E1, value);*/
/*var n;
switch (n)
{
case 1:
digitalWrite(Direction, HIGH);
break;
case 2:
digitalWrite(Direction, LOW);
break;
default:
digitalWrite(0, 0);
}*/
}
I think you're missing analogWrite(SpeedPin, 0); somewhere in your code. Since SpeedPin seems to be how you're controlling speed, you need to write a zero to it to stop.
I have an if statement inside a for loop. When the the condition in if statement is true, I want to break out of the for loop.
here is what exactly I have
if (data < voltage && data2 < voltage) {
digitalWrite(pump, HIGH);
digitalWrite(valve, HIGH);
digitalWrite(valve2, HIGH);
for (int i = 0; i < 10; i++) {
lcd.setCursor(0, 2);
lcd.print("S_1:");
if (data > 260 && data < 295)
lcd.print("MID");
else if (data < 260)
lcd.print("LOW");
else
lcd.print("HIGH");
lcd.setCursor(7, 0);
lcd.print("Pump:ON");
lcd.setCursor(0, 1);
lcd.print("V1:ON");
lcd.setCursor(9, 2);
lcd.print("S_2:");
if (data2 > 260 && data2 < 295)
lcd.print("MID");
else if (data2 < 260)
lcd.print("LOW");
else
lcd.print("HIGH");
lcd.setCursor(7, 1);
lcd.print("V2:ON");
lcd.setCursor(14, 1);
lcd.print("V3:OFF");
sum += data;
sum2 += data2;
delay(1000);
}
average = sum / 10;
average2 = sum2 / 10;
if (average > voltage || average2 > voltage) {
digitalWrite(pump, LOW);
digitalWrite(valve, LOW);
digitalWrite(valve2, LOW);
}
sum = 0;
sum2 = 0;
lcd.clear();
}
It seams not going throw the whole conditions.
You could use the break keyword.
for (int i = 0; i < 100; i++){
if (i == 2){
break;
}
}
Also, this is considered by OOD people a gotoish code, prefer inserting the break condition in the for condition :
boolean shouldBreak = false;
for (int i = 0; i < 100 && !shouldBreak; i++){
if (i == 2){
shouldBreak = true;
}
}
Use the break; keyword to do that.