Fade leds with serial communication Arduino - c++

I am getting serial communication and trying to make an led fade effect ,
This is my function for leds which is facing latency issues , obviously the for loop . Can anyone suggest a better logic or solution to approach this without getting latency in leds?
void loop() {
// serial communication
while(Serial.available() > 0 ){
int inByte = Serial.read();
Serial.print(inByte);
if (inByte > 0) {
// if byte is 255, then the next 3 bytes will be new rgb value
if (inByte == 255) {
setColors = true;
colorSetCounter = 0;
} else if (setColors) {
switch (colorSetCounter) {
case 0:
redVal = inByte;
break;
case 1:
greenVal = inByte;
break;
case 2:
blueVal = inByte;
setColors = false;
receiveNotes = true;
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
break;
}
colorSetCounter++;
} else if (receiveNotes) {
controlLeds(inByte);
}
}
}
}
void controlLeds (int note) {
note -= 1;
if (!leds[note]) {
leds[note].red = redVal;
leds[note].green = greenVal;
leds[note].blue = blueVal;
}
else {
for(int i =0; i<=255; i++){
leds[note].fadeToBlackBy(i);
FastLED.show();
if(!leds[note]){
break;
}
}
}
FastLED.show();
}

You need to write non-blocking code as Scheff mentioned. Instead of a global variable, you can use a static variable in a function. It remembers its value for each call of that function.
Here is an example how you could do it, using millis(). My code fades an LED on and off if some serialEvent happens and it does not block the rest of the code:
const int ledPin = 13;
int setValue = 0;
unsigned long lastTime = 0;
const unsigned long refreshTime = 200;
char buffer1[8];
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
if (millis() - lastTime > refreshTime)
{
lastTime = millis();
fadeLED(setValue);
}
}
void fadeLED(int fadeValue)
{
static int currentValue = 0;
if (fadeValue > currentValue) {
currentValue++;
}
if (fadeValue < currentValue) {
currentValue--;
}
analogWrite(ledPin, currentValue);
}
void serialEvent()
{ Serial.readBytesUntil('\n', buffer1, 8);
switch (setValue)
{
case 0:
setValue = 255;
break;
case 255:
setValue = 0;
break;
default:
break;
}
}

Related

Trigger a function on alarm time

i am trying to make an alarm clock with esp8266 and ws2812b leds. On alarm time it must call the sunrise() function. The sunrise() function works fine when i put it directly in the loop function. But it doesn't work in alarm trigger.
if (AlarmData.AlarmOn[Current.Day])
{
if (!AlarmActive)//do not enter this routine if alarm already active
{
if (Current.Hour == AlarmData.Hour[Current.Day])
{
if (Current.Minute == AlarmData.Minute[Current.Day])
{
if (Current.Second > 0 && Current.Second < 3)
{
AlarmActive = true;
sunrise();
alarmTriggerTime = micros();
Serial.println("Its time for your alarm!");
}
}
}
}
}
My code is below. thanks in advance for your help
#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include "EEPROMAnything.h"
#include "get_time.h"
#include "OTA.h"
#include <stdio.h>
#include <stdint.h>
#include "web_portal.h"
#include "ESP8266TimerInterrupt.h"
#include "restore_factory_settings.h"
#include "FastLED.h"
#define NUM_LEDS 60
#define DATA_PIN 5
// Define the array of leds
CRGB leds[NUM_LEDS];
#define timeZone 3
#define TenSecs 10000000
#define OneMin 60000000
#define TenMins 600000000
#define TIMER_INTERVAL_MS 1000
ESP8266Timer ITimer;
int timer;
int alarmTriggerTime;
struct CurrentTime Current;
bool AlarmActive;
struct AlarmDataStruct AlarmData;
int WiFiTimer;
char factory_settings_stored [3];
bool OneSecoundPassed;
void ICACHE_RAM_ATTR TimerHandler(void)
{
OneSecoundPassed = true;
}
void setup() {
Serial.begin(115200);
Serial.println("Booting");
EEPROM.begin(512);
EEPROM_readAnything(150, factory_settings_stored);
if (memcmp(&factory_settings_stored, "YES", 3) != 0)
{
restore_factory_settings();
}
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(0);
WiFi.mode(WIFI_STA);
WiFiManager wm;
bool response;
response = wm.autoConnect("AutoConnectAP"); // anonymous ap
if (!response) {
Serial.println("Failed to connect");
// ESP.restart();
}
else {
//if you get here you have connected to the WiFi
Serial.println("Lets Go");
}
start_server();
SetupOTA();
setup_time(timeZone);
Current = Current_Time();
EEPROM_readAnything(100, AlarmData);
Serial.print("Alarm set for ");
Serial.print(AlarmData.Hour[Current.Day]);
Serial.print(":");
Serial.println(AlarmData.Minute[Current.Day]);
timer = micros();
WiFiTimer = timer;
// Interval in microsecs
if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler))
{
Serial.println("Starting ITimer OK, millis() = " + String(millis()));
}
else
{
Serial.println("Can't set ITimer correctly. Select another freq. or interval");
}
}
void loop() {
if (!AlarmActive)
{
ArduinoOTA.handle();
handle_client();
if ((micros() - WiFiTimer) > TenMins) // check if wifi connection lost and if so try to reconnect
{
if (WiFi.status() != WL_CONNECTED)
{
ESP.restart(); //try to reconnect rather than resetting
}
WiFiTimer = micros();
}
if (OneSecoundPassed)
{
updateLocalTime();
OneSecoundPassed = false;
}
//update current hour from NTP server
if ((micros() - timer) > TenMins)
{
Current = Current_Time();
timer = micros();
}
}
if (AlarmActive)
{
if ((micros() - alarmTriggerTime) > TenMins)
{
AlarmActive = false; //if alarm active for 10mins and no one switches it off then do it auto
}
}
if (AlarmData.AlarmOn[Current.Day])
{
if (!AlarmActive)//do not enter this routine if alarm already active
{
if (Current.Hour == AlarmData.Hour[Current.Day])
{
if (Current.Minute == AlarmData.Minute[Current.Day])
{
if (Current.Second > 0 && Current.Second < 3)
{
AlarmActive = true;
sunrise();
alarmTriggerTime = micros();
Serial.println("Its time for your alarm!");
}
}
}
}
}
else
{
if (!AlarmActive)
{
}
}
}
void updateLocalTime () {
Current.Second++;
if (Current.Second >= 60)
{
Current.Second = 0;
Current.Minute++;
if (Current.Minute >= 60)
{
Current.Minute = 0;
Current.Hour++;
if (Current.Hour >= 24)
{
Current.Hour = 0;
Current.Day++;
if (Current.Day >= 7)
{
Current.Day = 0;
}
}
}
}
char tempTime[6];
if (Current.Minute < 10 && Current.Second < 10)
{
sprintf(tempTime, "0%d:0%d", Current.Minute, Current.Second);
}
else if (Current.Minute < 10)
{
sprintf(tempTime, "0%d:%d", Current.Minute, Current.Second);
}
else if (Current.Second < 10)
{
sprintf(tempTime, "%d:0%d", Current.Minute, Current.Second);
}
else
{
sprintf(tempTime, "%d:%d", Current.Minute, Current.Second);
}
}
void sunrise() {
static const uint8_t sunriseLength = 30; //(min)
static const uint8_t interval = (sunriseLength * 60) / 256;
static const uint8_t binterval = (sunriseLength * 60) / 256;
// current gradient palette color index
static uint8_t heatIndex = 0; // start out at 0
static uint8_t brIndex = 0;
// HeatColors_p is a gradient palette built in to FastLED
// that fades from black to red, orange, yellow, white
// feel free to use another palette or define your own custom one
CRGB color = ColorFromPalette(HeatColors_p, heatIndex);
fill_solid(leds, NUM_LEDS, color); // fill the entire strip with the current color
EVERY_N_SECONDS(binterval) {
if (brIndex < 255) {
FastLED.setBrightness(brIndex);
brIndex++;
}
}
EVERY_N_SECONDS(interval) {
if (heatIndex < 255) {
heatIndex++;
}
}
FastLED.show();
}
Your sunrise function should be called over and over, every call changes the LED color a slight bit. This is why it works then you put it into the loop function.
However, you have guarded it so that it is specifically only called once - so now it is called, sets the LEDs to the dimmest setting possible, and is not called again.
What you need to do is separate this out into two parts: One that detects when to activate sunrise, and one that is continuously called when it is time. F.ex:
void loop()
{
/* stuff */
if (AlarmData.AlarmOn[Current.Day])
{
if (!AlarmActive) // do not enter this routine if alarm already active
{
if (Current.Hour == AlarmData.Hour[Current.Day])
{
if (Current.Minute == AlarmData.Minute[Current.Day])
{
if (Current.Second > 0 && Current.Second < 3)
{
AlarmActive = true;
alarmTriggerTime = micros();
Serial.println("Its time for your alarm!");
}
}
}
}
}
if (AlarmActive)
sunrise();
}

how do i call a function in loop from another function in void loop arduino

#define CLK 2
#define DT 3
#define SW 4
#include <EEPROM.h>
#include <Wire.h>
#include <LiquidCrystal_PCF8574.h>
LiquidCrystal_PCF8574 lcd(0x27);
int counter = 0;
int currentStateCLK;
int lastStateCLK;
int lastCLK,cnt=0,btnState,lastPress=0;
String currentDir ="";
unsigned long lastButtonPress = 0;
char *mainmenu[] ={"SET MODE","SET TEMP","SET HUMD","SERVO","RESET"};
char *setmode[] ={"INCUBATOR","HATCHER","BACK"};
void setup() {
// Set encoder pins as inputs
Wire.begin();
Wire.beginTransmission(0x27);
pinMode(CLK,INPUT);
pinMode(DT,INPUT);
pinMode(SW, INPUT_PULLUP);
lcd.begin(16, 2);
lcd.setBacklight(255);
lcd.home(); lcd.clear();
Serial.begin(9600);
lastStateCLK = digitalRead(CLK);
delay(100);
if(EEPROM_READ(0)==NULL){
SET_MODE();
}
Serial.print(EEPROM_READ(0));
}
void loop(){
disp();
rot();
}
void disp(){
lcd.setCursor(0,0);
lcd.print(" KGF");
}
void rot() {
int lim=sizeof(mainmenu)/2;
Serial.print(lim);
currentStateCLK = digitalRead(CLK);
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
lcd.clear();
lcd.setCursor(0, 1);
if (digitalRead(DT) != currentStateCLK) {
counter --;
if(counter<0){
counter=lim-1;
}
}
else {
// Encoder is rotating CW so increment
counter ++;
if(counter>lim-1){
counter=0;
}
lcd.print(mainmenu[counter]);
}
lastStateCLK = currentStateCLK;
int btnState = digitalRead(SW);
if (btnState == LOW) {
//if 50ms have passed since last LOW pulse, it means that the
//button has been pressed, released and pressed again
if (millis() - lastButtonPress > 50) {
if(counter==0){
SET_MODE();
}
}
}
lastButtonPress = millis();
}
delay(1);
}
void SET_MODE(){
int lim=sizeof(setmode)/2;
int currentCLK = digitalRead(CLK);
if (currentCLK != lastCLK && currentCLK == 1){
lcd.clear();
lcd.setCursor(0, 1);
if (digitalRead(DT) != currentCLK) {
cnt --;
if(cnt<0){
cnt=lim-1;
}
}
else {
// Encoder is rotating CW so increment
cnt ++;
if(cnt>lim-1){
cnt=0;
}
}
lcd.print(setmode[cnt]);
}
lastCLK = currentCLK;
btnState = digitalRead(SW);
if (btnState == LOW) {
//if 50ms have passed since last LOW pulse, it means that the
//button has been pressed, released and pressed again
if (millis() - lastButtonPress > 50) {
if(setmode[cnt]=="BACK"){
exit(0);
}
lcd.clear();
lcd.setCursor(0, 1);
EEPROM_WRITE(0,setmode[cnt]);
lcd.print("DONE");
}
lastPress = millis();
}
delay(1);
}
void EEPROM_WRITE(int addrOffset, const String &strToWrite)
{
byte len = strToWrite.length();
EEPROM.write(addrOffset, len);
for (int i = 0; i < len; i++)
{
EEPROM.write(addrOffset + 1 + i, strToWrite[i]);
}
}
String EEPROM_READ(int addrOffset)
{
int newStrLen = EEPROM.read(addrOffset);
char data[newStrLen + 1];
for (int i = 0; i < newStrLen; i++)
{
data[i] = EEPROM.read(addrOffset + 1 + i);
}
data[newStrLen] = '\0';
return String(data);
}
I want to call the SET_MODE() function in the loop from rot() function, I am building a menu based program so the SET MODE menu should redirect to the SET_MODE() function, and as I will be adding more menu and sub-menus how can I perform this task.
The SET_MODE() function doesn't work in loop I do not know why, it only works when I all it under void loop() directly.

Converting code written with delay to millis() instead

I have a basic code I wrote on Arduino, however, I need to change the delay to Millis instead.
Whatever I do I can't get it to work, it's always getting stuck at a red light and won't ever turn green.
I'm posting the original delay code as code I wrote using Millis seems useless and may confuse what I'm trying to do.
const int redPin = 2;
const int yellowPin = 3;
const int greenPin = 4;
int redDuration = 10000;
int greenDuration = 5000;
void setup() {
pinMode(redPin, OUTPUT);
pinMode(yellowPin, OUTPUT);
pinMode(greenPin, OUTPUT);
}
void loop() {
setTrafficLight(1,0,0);
delay(redDuration);
setTrafficLight(1,1,0);
delay(2000);
setTrafficLight(0,0,1);
delay(greenDuration);
setTrafficLight(0,1,0);
delay(2000);
}
void setTrafficLight(int redState, int yellowState, int greenState) {
digitalWrite(redPin, redState);
digitalWrite(yellowPin, yellowState);
digitalWrite(greenPin, greenState);
}
Save the time when it started waiting.
If the difference of current time and the start time become the time to wait, proceed to the next status.
const int redPin = 2;
const int yellowPin = 3;
const int greenPin = 4;
int redDuration = 10000;
int greenDuration = 5000;
unsigned long startTime;
int status = 0; // using enum may be better
void setup() {
pinMode(redPin, OUTPUT);
pinMode(yellowPin, OUTPUT);
pinMode(greenPin, OUTPUT);
startTime = millis();
status = 0;
}
void loop() {
unsigned long currentTime = millis();
switch (status) {
case 0: // initial state
setTrafficLight(1, 0, 0);
status = 1;
break;
case 1: // waiting instead of delay(redDuration)
if (currentTime - startTime >= redDuration) {
setTrafficLight(1, 1, 0);
startTime = currentTime;
status = 2;
}
break;
case 2: // waiting instead of first delay(2000)
if (currentTime - startTime >= 2000) {
setTrafficLight(0, 0, 1);
startTime = currentTime;
status = 3;
}
break;
case 3: // waiting instead if delay(greenDuration)
if (currentTime - startTime >= greenDuration) {
setTrafficLight(0, 1, 0);
startTime = currentTime;
status = 4;
}
break;
case 4: // waiting instead of second delay(2000)
if (currentTime - startTime >= 2000) {
startTime = currentTime;
status = 0;
}
break;
default: // for in-case safety
status = 0;
break;
}
}
void setTrafficLight(int redState, int yellowState, int greenState) {
digitalWrite(redPin, redState);
digitalWrite(yellowPin, yellowState);
digitalWrite(greenPin, greenState);
}

Verification code doesn't work in Arduino

I am new to Arduino and I'm trying to make a program that receives IR codes from a TV remote, uses them as a 4 number pass code lighting up a LED as you press each button. And then comparing the code to a hard-coded one. In this case 1234.
I made a function to verify that the value entered is equal to the pass. If so, light up a green LED and else, light up a red one.
However, even if I input the correct code, only the red led lights up.
Here is my whole code as I'm not sure which part of it is the one causing problems:
const int pass[4] = {1, 2, 3, 4};
int value[4] = {};
int digitNum = 0;
int input;
void loop()
{
value[digitNum] = input; //where input is a number between 0 and 9
digitNum++;
if(digitNum == 1){
lightFirstLed();
}
else if(digitNum == 2){
lightSecondLed();
}
else if(digitNum == 3){
lightThirdLed();
}
else if(digitNum == 4){
lightFourthLed();
verify();
}
}
void verify()
{
bool falseCharacter = false;
for(int i = 0; i <= 4; i++){
if(value[i] != pass[i]){
falseCharacter = true;
}
}
if(!falseCharacter){
lightGreenLed();
}
else{
lightRedLed();
}
}
Functions in the form of light*Led actually do what they're supposed to do.
I tried changing the verify function around, that ended up making the green LED the one that always shone. I've been doing this for hours and I'm starting to feel disparate.
I would really appreciate any help. And please tell me if anything I'm doing does not comply with best practices even if it's out of the scope of this question.
For full code and design, here's a link to autodesk's simulator: https://www.tinkercad.com/things/0keqmlhVqNp-mighty-leelo/editel?tenant=circuits?sharecode=vVUD2_4774Lj4PYXh6doFcOqWUMY2URIfW8VXGxutRE=
EDIT: Now reset doesn't work
Your for loop in verify is accessing outside the array:
const int pass[4] = {1, 2, 3, 4};
int value[4] = {};
for(int i = 0; i <= 4; i++){
if(value[i] != pass[i]){
falseCharacter = true;
}
}
Change i <= 4 to i < 4. Also, when falseCharacter is set to true, break from the loop:
for(int i = 0; i < 4; i++)
{
if(value[i] != pass[i])
{
falseCharacter = true;
break;
}
}
Update
You need an else statement in loop:
void loop(void)
{
if(irrecv.decode(&results))
{
if (results.value == powBtn)
{
reset();
}
else if (results.value == zeroBtn)
{
input = 0;
}
else if (results.value == oneBtn)
{
input = 1;
}
else if (results.value == twoBtn)
{
input = 2;
}
else if (results.value == threeBtn)
{
input = 3;
}
else if (results.value == fourBtn)
{
input = 4;
}
else if (results.value == fiveBtn)
{
input = 5;
}
else if (results.value == sixBtn)
{
input = 6;
}
else if (results.value == sevenBtn)
{
input = 7;
}
else if (results.value == eightBtn)
{
input = 8;
}
else if (results.value == nineBtn)
{
input = 9;
}
else
{
return; /*** !!! Unrecognized Value !!! ***/
}
value[digitNum] = input;
digitNum++;
if(digitNum == 1)
{
digitalWrite(LED1, HIGH);
}
else if(digitNum == 2)
{
digitalWrite(LED2, HIGH);
}
else if(digitNum == 3)
{
digitalWrite(LED3, HIGH);
}
else if(digitNum == 4)
{
digitalWrite(LED4, HIGH);
verify();
}
else
{
if (results.value == powBtn)
{
reset();
}
}
// Receive the next value
irrecv.resume();
}
}

How to pause a void in C++ Arduino Uno

My question is very simple but I'm stuck on it for a while now.
I need to make a script that when pushed on a button loop a() is put on pause. And when you press it again it should go from where it ended. But I can't figure out a way to do it.
I hope someone can help me.
This is my code:
int Aan = 1;
int Uit = 0;
int analogPin = A3;
int LED1 = 13;
int LED2 = 12;
int LED3 = 11;
int LED4 = 10;
int val;
bool r = false;
void setup() {
pinMode(analogPin, INPUT_PULLUP);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(LED3, OUTPUT);
pinMode(LED4, OUTPUT);
digitalWrite(LED1, Aan);
digitalWrite(LED2, Aan);
digitalWrite(LED3, Aan);
digitalWrite(LED4, Aan);
}
void loop() {
val = digitalRead(analogPin);
if (val == LOW)
{
if (r == true)
{
r = false;
}
if (r == false)
{
r = true;
}
}
if (r == true)
{
a();
}
}
void a() {
for (int i = 10; i <= 13; i++)
{
pinMode(i, OUTPUT);
digitalWrite(i, Uit);
delay(100);
digitalWrite(i, Aan);
}
for (int i = 13; i >= 10; i--)
{
pinMode(i, OUTPUT);
digitalWrite(i, Uit);
delay(100);
digitalWrite(i, Aan);
}
}
Just to explain whats happening.. Void a() makes 4 different leds light up and go out. The pattern thats used is Knight Rider (If you don't know the tv show just google the car of him)
I will assume you want the "Knight Rider" pattern to constantly run.
I've made a couple of changes. First, I added a function to run the led sequence one way. Second, I added a while loop that will always run once, and will continue to run while the button is pushed.
bool paused = false;
int buttonState = HIGH;
void loop() {
a();
}
// This only works if leds ports are consecutive
void runSequence(int ledStart, int ledEnd)
{
int direction = ledStart < ledEnd ? 1 : -1;
for (int i = ledStart; i != ledEnd + direction; i += direction) {
digitalWrite(i, Uit);
do {
delay(100);
} while (LOW == digitalRead(analogPin)); // Check button state
digitalWrite(i, Aan);
}
}
void a() {
runSequence(LED4, LED1);
runSequence(LED1, LED4);
}
EDIT Changes based on comment
bool paused = false;
int buttonState = HIGH;
int currentLED = LED1;
int currentDirection = -1;
void loop() {
checkButton();
if (!paused) {
// Flash the led
digitalWrite(currentLED, Uit);
delay(100);
digitalWrite(currentLED, Aan);
// Change direction?
if (LED1 == currentLED || LED4 == currentLED) {
currentDirection *= -1;
}
// Setup for next iteration
currentLED += currentDirection;
}
}
void checkButton() {
int state = digitalRead(analogPin);
// Check if button state has changed
if (state != buttonState) {
buttonState = state;
// Change paused state when button is released
if (state == HIGH) {
paused = !paused;
}
}
}
Inside your a() function put another while loop which becomes false when the button is Up.
Like:
while(digitalRead(analogPin) == LOW)
{
}
As long as the button is pressed down the loop will continue. When you release the button the program will exit the loop and continue execution of your code.