#include <Adafruit_NeoPixel.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include <SPI.h>
#define PIN_POT 13 //Input pin Potentiometer is attached to
#define PIN_LED 2 //Input pin Neopixel is attached to
#define NUMPIXELS 16 //Number of pixels in strip
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define DEFAULT_I2C_PINS // Para definir los pines del bus I2C por defecto
#ifdef DEFAULT_I2C_PINS
#define I2C_SDA 21
#define I2C_SCL 22
#endif
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
const int I2C_dir = 0x3C;
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN_LED, NEO_GRB + NEO_KHZ800);
#define BUTTON_CLICK_PIN 26 // pin for button Click
#define BUTTON_DOUBLECLICK_PIN 27 // pin for button Click
unsigned long currentMillis;
unsigned long previousTime = 0; // Previous Pixel Millis
int timeToWait = 50; // Pixel Interval (ms)
// unsigned long patternPrevious = 0; // Previous Pattern Millis
uint8_t patternMode; // Para saber en qué modo de patrón estamos
int patternCurrent = 0; // Current Pattern Number
// int patternInterval = 5000; // Pattern Interval (ms)
int patternCycle = 0; // Pattern Cycle
// int pixelQueue = 0; // Pattern Pixel Queue
uint16_t pixelCurrent = 0; // Pattern Current Pixel Number
uint16_t pixelNumber = NUMPIXELS; // Total Number of Pixels
int upDownCycle = 1;
// uint8_t State; // Para saber en qué "estado" estamos
uint32_t timeToIdle = 15000; // time to go Idle (ms)
uint16_t stripNumLedsOn;
uint8_t stripLastLedPercentOn;
uint8_t stripMaxBrightness = 255; // Default max brightness of the strip (0 - 255)
byte OptionSelected; // En qúe modo estamos (0 = navegación, 1 = led, 2 = info...)
int8_t menu_item_selected;
int8_t menu_item_sel_prev;
int8_t menu_item_sel_next;
const uint8_t menu_item_heigth = 18;
const uint8_t menu_item_left = 6;
const uint8_t menu_Y_top = 12;
const uint8_t menu_box_heigth = 18;
const uint8_t menu_box_left = 2;
struct HSV
{
uint16_t hue;
uint8_t sat;
uint8_t val;
};
HSV C_BrightnessRatio = {34500,255,stripMaxBrightness};
HSV C_WifiConnecting = {192,255,stripMaxBrightness};
HSV C_WifiConnected = {192,255,stripMaxBrightness};
HSV C_ZigbeeConnecting = {192,255,stripMaxBrightness};
HSV C_ZigbeeConnected = {192,255,stripMaxBrightness};
HSV C_ErrorConnection = {192,255,stripMaxBrightness};
HSV C_StandBy = {192,255,stripMaxBrightness};
struct str_MenuItems
{
uint8_t optionNum;
char optionName[25];
};
const int NUM_ITEMS = 7; // number of items in the list
str_MenuItems MenuItems[NUM_ITEMS]
{
{1,"Led 1"},
{2,"Led 2"},
{3,"Led 3"},
{4,"Escena 1"},
{5,"Escena 2"},
{6,"Apagar todo"},
{7,"Informacion"}
};
struct str_LedLamp
{
uint8_t ledNum; // Led Id 1, 2, 3 ...
byte ledIsOn; // State of led: true=ON - false=OFF
uint16_t ledBrightness; // Actual Brightness value
uint16_t ledMaxBrightness; // Max Brightness value
};
str_LedLamp LedLamp[3] =
{
{1,0,0,4095},
{2,0,0,4095},
{3,0,0,4095}
};
// str_LedLamp str_Led_1 = {1,0,0,4095};
// str_LedLamp str_Led_2 = {2,0,0,4095};
// str_LedLamp str_Led_3 = {3,0,0,4095};
boolean idleState = false;
int POT_value = 0;
int POT_previousValue = 0;
int POT_range = 4096;
unsigned long lastTimeButtonStateChanged = millis();
unsigned long buttonDebounceDuration = 100; //milliseconds
void setup()
{
Serial.begin(115200);
pinMode(PIN_POT, INPUT); // Declare Potentiometer PIN as INPUT
// define pins for buttons
// INPUT_PULLUP means the button is HIGH when not pressed, and LOW when pressed
// since it´s connected between some pin and GND
pinMode(BUTTON_CLICK_PIN, INPUT_PULLUP); // click button
pinMode(BUTTON_DOUBLECLICK_PIN, INPUT_PULLUP); // double click button
// Initialize the NeoPixel library
strip.begin();
strip.show();
// strip.setBrightness(150); // Set BRIGHTNESS to about 1/5 (max = 255)
// Para obtener la dirección I2C si no la conocemos
// Wire.begin(21, 22); // sda= GPIO_21 /scl= GPIO_22
// I2C_Scanner();
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, I2C_dir))
{ // Address 0x3D for 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
// display.display();
delay(2000); // This delay is necessary to initialize the display
// Clear the buffer
display.clearDisplay();
display.display();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, 32);
display.println("Iniciando...");
display.display();
blockingRainbow(3);
turnOffLedRing();
// delay(2000);
menu_item_selected = 0;
CalculaOpcionesVisiblesMenuPrincipal();
PintaMenu();
OptionSelected = 0; // Al arrancar entro en modo menú de opciones
patternMode = 1;
POT_value = analogRead(PIN_POT); // Leo el valor del potenciómetro para obtener el valor actual
POT_previousValue = POT_value; // Igualo el valor actual al anterior para que no salte la primera vez al entrar en el Loop
}
void loop()
{
uint8_t optionNum;
currentMillis = millis(); // Update current time
POT_value = analogRead(PIN_POT); // Lectura del potenciómetro
if (POT_value != POT_previousValue) // Si el valor ha cambiado respecto al instante anterior lo represento en el anillo led
{
Serial.println("POT_value: " + String(POT_value) + " - POT_previousValue: " + String(POT_previousValue));
previousTime = currentMillis; // Reset timer counter
Start_Idle_Countdown(); // Inicio la cuenta atrás para entrar en modo "reposo"
Serial.println("patternMode: " + String(patternMode));
// Si el sistema está en idle la primera acción lo que hará será "reactivarlo" (enciende la pantalla
// y se queda a la espera en el menú principal)
if (idleState)
{
idleState = false;
PintaMenu();
return;
}
switch (OptionSelected)
{
case 0: // Pantalla principal de selección de opciones
CalculaOpcionesVisiblesMenuPrincipal();
PintaMenu();
POT_previousValue = POT_value;
turnOffLedRing();
break;
case 1: // Pantalla de gestión del Led 1. Busco en el array de LedLamp a cual se corresponde
for (int i=0; i < sizeof(LedLamp); i++)
{
if (LedLamp[i].ledNum == OptionSelected)
{
CambiaBrilloTiraLed(LedLamp[i]);
i = sizeof(LedLamp); // Una vez encontrado el led correcto en el array fuerzo la salida del For
}
}
POT_previousValue = POT_value;
break;
case 2: // Pantalla de gestión del Led 2. Busco en el array de LedLamp a cual se corresponde
for (int i=0; i < sizeof(LedLamp); i++)
{
if (LedLamp[i].ledNum == OptionSelected)
{
CambiaBrilloTiraLed(LedLamp[i]);
i = sizeof(LedLamp); // Una vez encontrado el led correcto en el array fuerzo la salida del For
}
}
POT_previousValue = POT_value;
break;
case 3: // Pantalla de gestión del Led 3. Busco en el array de LedLamp a cual se corresponde
for (int i=0; i < sizeof(LedLamp); i++)
{
if (LedLamp[i].ledNum == OptionSelected)
{
CambiaBrilloTiraLed(LedLamp[i]);
i = sizeof(LedLamp); // Una vez encontrado el led correcto en el array fuerzo la salida del For
}
}
POT_previousValue = POT_value;
break;
case 4:
PintaPantallaEscenaZ(optionNum, true, false);
POT_previousValue = POT_value;
break;
case 5:
PintaPantallaEscenaZ(optionNum, true, false);
POT_previousValue = POT_value;
break;
case 6:
break;
case 7:
break;
}
// POT_previousValue = POT_value;
}
delay(10);
// Click Button
if (digitalRead(BUTTON_CLICK_PIN) == LOW)
{
if (millis() - lastTimeButtonStateChanged >= buttonDebounceDuration)
{
lastTimeButtonStateChanged = millis();
Serial.print("Button Click clicked!");
Serial.println(" -- menu_item_selected: " + String(menu_item_selected));
Start_Idle_Countdown(); // Inicio la cuenta atrás para el Idle del anillo led
previousTime = currentMillis;
if (idleState)
{
idleState = false;
PintaMenu();
return;
}
switch (OptionSelected)
{
case 0: // Pantalla principal de selección de opciones
optionNum = MenuItems[menu_item_selected].optionNum;
// Si han seleccionado alguno de los leds pinto la pantalla de gestión de leds y entro en modo
// gestión de tira led
if ((optionNum == 1) || (optionNum == 2) || (optionNum == 3))
{
// Identifico qué tira led han seleccionado y pinto la pantalla de gestión de la tira led
for (int i=0; i < sizeof(LedLamp); i++)
{
if (LedLamp[i].ledNum == optionNum)
{
PintaPantallaTiraLed(LedLamp[i], false);
if (LedLamp[i].ledIsOn)
{
CalculateLeds2Fill(LedLamp[i].ledBrightness, POT_range, pixelNumber);
PartialFill(stripNumLedsOn, stripLastLedPercentOn, stripMaxBrightness, C_BrightnessRatio);
}
else
turnOffLedRing();
i = sizeof(LedLamp); // Una vez encontrado y procesado el led fuerzo la salida del For
}
}
// Cambio el OptionSelected al número de opción seleccionada
OptionSelected = optionNum;
}
if ((optionNum == 4) || (optionNum == 5))
{
PintaPantallaEscenaZ(optionNum, false, false);
// Cambio el OptionSelected al número de opción seleccionada
OptionSelected = optionNum;
}
if (optionNum == 6)
{
Serial.println("Aquí ejecutar apagado de todas las tiras led");
// Por ahora no cambio el OptionSelected a ninguna otra opción
OptionSelected = 0;
}
break;
case 1: // Estoy en modo gestión de la tira led 1
// Si han seleccionado el Led 1 busco en el array de LedLamp a cual se corresponde
for (int i=0; i < sizeof(LedLamp); i++)
{
if (LedLamp[i].ledNum == OptionSelected)
{
CambiaEstadoTiraLed(LedLamp[i]);
i = sizeof(LedLamp); // Una vez encontrado el led correcto en el array fuerzo la salida del For
}
}
break;
case 2: // Estoy en modo gestión de la tira led 2
// Si han seleccionado el Led 2 busco en el array de LedLamp a cual se corresponde
for (int i=0; i < sizeof(LedLamp); i++)
{
if (LedLamp[i].ledNum == OptionSelected)
{
CambiaEstadoTiraLed(LedLamp[i]);
i = sizeof(LedLamp); // Una vez encontrado el led correcto en el array fuerzo la salida del For
}
}
break;
case 3: // Estoy en modo gestión de la tira led 3
// Si han seleccionado el Led 3 busco en el array de LedLamp a cual se corresponde
for (int i=0; i < sizeof(LedLamp); i++)
{
if (LedLamp[i].ledNum == OptionSelected)
{
CambiaEstadoTiraLed(LedLamp[i]);
i = sizeof(LedLamp); // Una vez encontrado el led correcto en el array fuerzo la salida del For
}
}
break;
case 4: // Estoy en modo gestión de la escena 1
PintaPantallaEscenaZ(optionNum, false, true);
break;
case 5: // Estoy en modo gestión de la escena 2
PintaPantallaEscenaZ(optionNum, false, true);
break;
case 6:
break;
case 7:
break;
}
}
}
//Double click button
if (digitalRead(BUTTON_DOUBLECLICK_PIN) == LOW)
{
if (millis() - lastTimeButtonStateChanged >= buttonDebounceDuration)
{
lastTimeButtonStateChanged = millis();
Serial.println("Button DoubleClick clicked!");
Start_Idle_Countdown(); // Inicio la cuenta atrás para el Idle del anillo led
previousTime = currentMillis;
if (idleState)
{
idleState = false;
PintaMenu();
return;
}
OptionSelected = 0;
PintaMenu();
turnOffLedRing();
}
}
switch (patternMode)
{
case 0:
if (currentMillis - previousTime >= timeToWait) // Check for expired time
{
previousTime = currentMillis; // Run current frame
if (patternCurrent == 4)
{
turnOffLedRing();
turnOffOled();
patternMode = 99;
patternCurrent = 0;
timeToWait = 10;
pixelCurrent = 0;
}
if (patternCurrent == 3)
{
beatAndFade(C_BrightnessRatio, 50);
if (pixelCurrent >= stripMaxBrightness)
patternCycle++;
if ((patternCycle == 2) && (pixelCurrent <= 150) && (upDownCycle == -1))
{
patternCurrent = 4;
timeToWait = 10000;
pixelCurrent = 0;
upDownCycle = 1;
}
}
if (patternCurrent == 2)
{
if (pixelCurrent == 0)
patternCycle++;
timeToWait = 50;
Commet(C_BrightnessRatio, timeToWait);
if (patternCycle > 3)
{
patternCurrent = 3;
timeToWait = 50;
pixelCurrent = 0;
patternCycle = 0;
}
}
if (patternCurrent == 1)
{
timeToWait = 80;
pixelCurrent++;
uint8_t bright = stripMaxBrightness - pixelCurrent;
if (((stripNumLedsOn > 0) || (stripLastLedPercentOn > 0)) && (bright > 0))
{
PartialFill(stripNumLedsOn, stripLastLedPercentOn, bright, C_BrightnessRatio);
}
else
{
patternCurrent = 2;
pixelCurrent = 0;
patternCycle = 0;
}
}
}
break;
case 1:
if (currentMillis - previousTime >= timeToWait) // Check for expired time
{
previousTime = currentMillis; // Run current frame
// Roulette(1, C_BrightnessRatio, 50);
// Commet(C_BrightnessRatio, 50);
// QuarterFill(1, C_BrightnessRatio, 50);
// rainbow(20);
}
break;
case 2:
break;
case 99:
break;
}
delay(10);
}
/* Función que reinicia el contador para activar el modo Idle del anillo led
*/
void Start_Idle_Countdown()
{
timeToWait = timeToIdle;
patternMode = 0;
patternCurrent = 1;
pixelCurrent = 0;
patternCycle = 0;
upDownCycle = 1;
}
void CalculaOpcionesVisiblesMenuPrincipal()
{
if (POT_value != POT_previousValue)
{
if (POT_value > POT_previousValue)
menu_item_selected++;
else
menu_item_selected--;
}
if (menu_item_selected < 0)
{
menu_item_selected = NUM_ITEMS - 1;
menu_item_sel_prev = menu_item_selected - 1;
menu_item_sel_next = 0;
}
else
{
if (menu_item_selected >= NUM_ITEMS)
{
menu_item_selected = 0;
menu_item_sel_prev = NUM_ITEMS - 1;
menu_item_sel_next = menu_item_selected + 1;
}
else
{
menu_item_sel_prev = menu_item_selected - 1;
if (menu_item_sel_prev < 0)
menu_item_sel_prev = NUM_ITEMS - 1;
menu_item_sel_next = menu_item_selected + 1;
if (menu_item_sel_next >= NUM_ITEMS)
menu_item_sel_next = 0;
}
}
}
void PintaMenu()
{
Serial.println("Pinto el menu");
display.clearDisplay();
// Cabecera
PintaCabecera("Menu");
// Primera opción del selector
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(menu_item_left, menu_Y_top + 5);
display.print(MenuItems[menu_item_sel_prev].optionName);
// Recuadro de la opción seleccionada
display.drawRoundRect(menu_box_left,menu_box_heigth + menu_Y_top, 119, menu_box_heigth, 3, SSD1306_WHITE);
// Opción seleccionada (segunda línea del selector)
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(menu_item_left, menu_Y_top + menu_item_heigth + 5);
display.print(MenuItems[menu_item_selected].optionName);
// Tercera opción del selector
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(menu_item_left, menu_Y_top + (menu_item_heigth * 2) + 5);
display.print(MenuItems[menu_item_sel_next].optionName);
// "línea" (puntos) del scroll vertical
for (int i = menu_Y_top; i < SCREEN_HEIGHT; i+=3)
{
display.drawPixel(SCREEN_WIDTH - 1, i, SSD1306_WHITE);
}
// Calculo la longitud de la barra de scroll según el nº de items (le resto menu_Y_top al alto para quitar
// la parte de la cabecera del menú)
int scrollBarLength = (SCREEN_HEIGHT - menu_Y_top) / NUM_ITEMS;
display.fillRect(SCREEN_WIDTH - 2, (scrollBarLength * menu_item_selected) + menu_Y_top, 3, scrollBarLength, SSD1306_WHITE);
display.display();
}
void PintaPantallaTiraLed(str_LedLamp &Led, boolean justRefreshValues)
{
String state_str = "OFF";
uint16_t brightness_percent = 0;
if (Led.ledIsOn) state_str = "ON";
brightness_percent = (Led.ledBrightness * 100) / Led.ledMaxBrightness;
// Si estamos entrando en la pantalla de gestión del Led
if (not justRefreshValues)
{
display.clearDisplay();
PintaCabecera(MenuItems[menu_item_selected].optionName);
display.drawRect(2, menu_Y_top + 6, 42, 14, SSD1306_WHITE);
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(5, menu_Y_top + 9);
display.print("Estado");
display.setTextSize(2);
display.setCursor(3, 40);
display.print(state_str);
display.drawRect(58, menu_Y_top + 6, 68, 14, SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(62, menu_Y_top + 9);
display.print("Intensidad");
display.setTextSize(2);
display.setCursor(70, 40);
// Calculo el % de brillo según el valor absoluto
if (brightness_percent > 99) display.setCursor(70, 40);
if (brightness_percent <= 99) display.setCursor(82, 40);
if (brightness_percent < 10) display.setCursor(94, 40);
display.print(String(brightness_percent) + "%");
display.display();
}
else // Si ya estamos en la pantalla de estión del Led y solo estamos refrescando alguno de sus datos
{
display.fillRect(2, 40, 120, 20, SSD1306_BLACK);
display.setTextSize(2);
display.setCursor(3, 40);
display.print(state_str);
display.setTextSize(2);
display.setCursor(70, 40);
// Calculo el % de brillo según el valor absoluto
if (brightness_percent > 99) display.setCursor(70, 40);
if (brightness_percent <= 99) display.setCursor(82, 40);
if (brightness_percent < 10) display.setCursor(94, 40);
display.print(String(brightness_percent) + "%");
display.display();
}
}
void PintaCabecera(String headerName)
{
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(2, 1);
display.print(aMayuscula(headerName));
display.drawFastHLine(1, 10, 119, SSD1306_WHITE);
Serial.println("Pinto cabecera menu");
}
void CambiaBrilloTiraLed(str_LedLamp &Led)
{
// Si el led está apagado no hago nada con el brillo
if (Led.ledIsOn)
{
Led.ledBrightness = POT_value;
PintaPantallaTiraLed(Led, true);
CalculateLeds2Fill(POT_value, POT_range, pixelNumber);
PartialFill(stripNumLedsOn, stripLastLedPercentOn, stripMaxBrightness, C_BrightnessRatio);
}
}
void CambiaEstadoTiraLed(str_LedLamp &Led)
{
Led.ledIsOn = !Led.ledIsOn;
PintaPantallaTiraLed(Led, true);
if (Led.ledIsOn)
{
CalculateLeds2Fill(Led.ledBrightness, POT_range, pixelNumber);
PartialFill(stripNumLedsOn, stripLastLedPercentOn, stripMaxBrightness, C_BrightnessRatio);
}
else
turnOffLedRing();
}
/* Función para gestionar el pintado de la opción de encendido/apagado de escenas.
- scenNum : número de escena sobre la que actuar
- navigate : indica si estamos navegando por las opciones de la pantalla (boolean)
- selection : indica si estamos seleccionando una de las opciones (ON/OFF) (boolean)
*/
void PintaPantallaEscenaZ(uint8_t sceneNum, boolean navigate, boolean selection)
{
static boolean isOnSelected = true; // En esta variable estática se guarda el valor de ON/OFF actual (se mantiene entre ejecuciones de la función)
// Si no estamos navegando ni selecionando una opción es porque estamos entrando en la opción del menú
// que hace referencia a alguna de las escenas y por tanto tenemos que pintar la pantalla de gestión de escena
if ((!navigate) && (!selection))
{
display.clearDisplay();
PintaCabecera(MenuItems[menu_item_selected].optionName);
if (isOnSelected)
display.drawRect(2, menu_Y_top + 20, 42, 20, SSD1306_WHITE);
else
display.drawRect(73, menu_Y_top + 20, 42, 20, SSD1306_WHITE);
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, menu_Y_top + 23);
display.print("ON");
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(76, menu_Y_top + 23);
display.print("OFF");
display.display();
return;
}
// Si estamos navegando únicamente se cambia el selector de una opción a otra
if (navigate)
{
isOnSelected = !isOnSelected;
if (isOnSelected)
{
display.drawRect(2, menu_Y_top + 20, 42, 20, SSD1306_WHITE);
display.drawRect(73, menu_Y_top + 20, 42, 20, SSD1306_BLACK);
}
else
{
display.drawRect(2, menu_Y_top + 20, 42, 20, SSD1306_BLACK);
display.drawRect(73, menu_Y_top + 20, 42, 20, SSD1306_WHITE);
}
display.display();
return;
}
// Si estamos seleccionando una opción hago un efecto de selección de la opción actual
if (selection)
{
if (isOnSelected)
{
display.fillRect(2, menu_Y_top + 20, 42, 20, SSD1306_WHITE);
display.setTextSize(2);
display.setTextColor(SSD1306_BLACK);
display.setCursor(10, menu_Y_top + 23);
display.print("ON");
display.display();
delay(50);
display.fillRect(2, menu_Y_top + 20, 42, 20, SSD1306_BLACK);
display.drawRect(2, menu_Y_top + 20, 42, 20, SSD1306_WHITE);
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, menu_Y_top + 23);
display.print("ON");
display.display();
}
else
{
display.fillRect(73, menu_Y_top + 20, 42, 20, SSD1306_WHITE);
display.setTextSize(2);
display.setTextColor(SSD1306_BLACK);
display.setCursor(76, menu_Y_top + 23);
display.print("OFF");
display.display();
delay(50);
display.fillRect(73, menu_Y_top + 20, 42, 20, SSD1306_BLACK);
display.drawRect(73, menu_Y_top + 20, 42, 20, SSD1306_WHITE);
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(76, menu_Y_top + 23);
display.print("OFF");
display.display();
}
return;
}
}
/* Función que recorre todo el anillo iluminando los leds indicados en la variable num
Los parámetros son:
- numLeds : número de leds a iluminar
- color : color de los leds
- wait : tiempo de espera entre ciclos (ciclo general de la aplicación)
*/
void Roulette(uint16_t numLeds, HSV color, int wait)
{
if (numLeds > pixelNumber)
numLeds = pixelNumber;
if (timeToWait != wait)
timeToWait = wait; // Update delay time
strip.clear();
int16_t pos;
for (int i=0; i<numLeds; i++)
{
pos = pixelCurrent + i;
if (pos >= pixelNumber) // Si el led a enceder es mayor que el nº máximo de leds vuelvo a emmpezar
pos = pos - pixelNumber;
strip.setPixelColor(pos, strip.gamma32(strip.ColorHSV(color.hue,color.sat,color.val))); // Set pixel's color (in RAM)
}
strip.show(); // Update strip to match
pixelCurrent++; // Advance current pixel
if (pixelCurrent >= pixelNumber) // Loop the pattern from the first LED
pixelCurrent = 0;
}
void Commet(HSV color, int wait)
{
if (timeToWait != wait)
timeToWait = wait; // Update delay time
strip.clear();
uint32_t rgbColor;
uint8_t fade;
int16_t antLed;
rgbColor = strip.ColorHSV(color.hue,color.sat,color.val);
strip.setPixelColor(pixelCurrent, strip.gamma32(rgbColor)); // Set pixel's color (in RAM)
fade = color.val / 8;
for (int i=1; i<=4; i++)
{
antLed = pixelCurrent - i;
rgbColor = strip.ColorHSV(color.hue,color.sat,(color.val-(fade*i)));
if (antLed < 0)
strip.setPixelColor(pixelNumber - abs(antLed), strip.gamma32(rgbColor));
else
strip.setPixelColor(antLed, strip.gamma32(rgbColor));
}
strip.show(); // Update strip to match
pixelCurrent++; // Advance current pixel
if (pixelCurrent >= pixelNumber) // Loop the pattern from the first LED
pixelCurrent = 0;
}
/* Función para el cálculo de la representación en el anillo led del % de intensidad de la tira led seleccionada
Los parámetros de entrada son:
- valueNow: valor actual a representar (en caso de un potenciómetro sería el valor actual del mismo)
- totalValues: nº total de valores a representar (en caso de un potenciómetro sería por ejemplo 1024 --> de 0 a 1023)
- color: color en formato HSV (Hue, Saturation, Value of Brightness)
El cálculo se realiza de la siguiente forma:
- Se reparte de forma secuencial el máximo de puntos de intensidad que puede gestionar el knob (o un potenciómetro) entre el número de leds del anillo,
de forma que un úmico led del anillo va a representar x valores del knob
- Se calcula a cuantos leds completos del anillo corresponde el valor de intensidad recibido del knob.
P.e. si cada led representa 50 valores del knob y se recibe un 130, el cálculo sería 130/50 = 2.6 lo que implica 2 leds completos y a mayores un 60% del led 3.
*/
void ProportionalFill(int valueNow, int totalValues, HSV color)
{
int LED_knobVal4Full = 0;
int LED_numFullFilled = 0;
int LED_notFullFilled = 0;
uint32_t rgbColor, rgbColor2;
// // Lo primero es calcular cuantos valores del total corresponden a cada led del anillo
// // Evaluamos si el valor máximo a representar es menor que el número total de leds del anillo
// if (totalValues < pixelNumber)
// LED_knobVal4Full = 1; // En este caso, se le asigna un valor 1, por lo que el led se "llenará" directamente por cada unidad del valor
// else
// {
// // Se obtiene el cociente (el nº entero), que representa el nº de valores necesarios para llenar un led del anillo
// LED_knobVal4Full = totalValues / pixelNumber;
// // Se hay resto en la división anterior, indica que hay que redondear hacia arriba en una unidad el nº de valores necesarios para llenar un led del anillo
// if ((totalValues % pixelNumber) > 0)
// LED_knobVal4Full++;
// }
// // Si el valor actual es mayor que valor máximo a representar que se ha configurado, se iluminan todos los leds del anillo
// if (valueNow >= totalValues - 1) //Se resta 1 porque totalValues va de 0 a totalValues - 1
// {
// LED_numFullFilled = pixelNumber;
// LED_notFullFilled = 0;
// }
// else
// {
// LED_numFullFilled = valueNow / LED_knobVal4Full; // Se calcula cuantos leds "llenos" representa el valor actual a representar
// LED_notFullFilled = valueNow % LED_knobVal4Full; // Calculamos el llenado del led que está "a medias"
// }
CalculatePropFill(valueNow, totalValues, LED_knobVal4Full, LED_numFullFilled, LED_notFullFilled);
strip.clear();
//"Lleno" los leds del anillo que se deben iluminar al completo
if (LED_numFullFilled > 0)
{
rgbColor = strip.ColorHSV(color.hue,color.sat,color.val);
Serial.println("rgbColor: " + String(rgbColor));
strip.fill(strip.gamma32(rgbColor), 0, LED_numFullFilled); // Set pixel's color (in RAM)
// strip.fill(strip.ColorHSV(65536/3,color.sat,color.val), 0, LED_numFullFilled); // Set pixel's color (in RAM)
}
//"Lleno" el último led a la intensidad que corresponda
if (LED_notFullFilled > 0)
{
uint8_t brightnessVal;
brightnessVal = (255 * LED_notFullFilled) / LED_knobVal4Full;
rgbColor = strip.ColorHSV(color.hue, color.sat, brightnessVal);
strip.setPixelColor(LED_numFullFilled, rgbColor);
}
//This sends the updated color to the hardware
strip.show();
}
void CalculatePropFill(int valNow, int totVal, int &knobVal4Full, int &numFullFilled, int ¬FullFilled)
{
// Lo primero es calcular cuantos valores del total corresponden a cada led del anillo
// Evaluamos si el valor máximo a representar es menor que el número total de leds del anillo
if (totVal < pixelNumber)
knobVal4Full = 1; // En este caso, se le asigna un valor 1, por lo que el led se "llenará" directamente por cada unidad del valor
else
{
// Se obtiene el cociente (el nº entero), que representa el nº de valores necesarios para llenar un led del anillo
knobVal4Full = totVal / pixelNumber;
// Se hay resto en la división anterior, indica que hay que redondear hacia arriba en una unidad el nº de valores necesarios para llenar un led del anillo
if ((totVal % pixelNumber) > 0)
knobVal4Full++;
}
// Si el valor actual es mayor que valor máximo a representar que se ha configurado, se iluminan todos los leds del anillo
if (valNow >= totVal - 1) //Se resta 1 porque totalValues va de 0 a totalValues - 1
{
numFullFilled = pixelNumber;
notFullFilled = 0;
}
else
{
numFullFilled = valNow / knobVal4Full; // Se calcula cuantos leds "llenos" representa el valor actual a representar
notFullFilled = valNow % knobVal4Full; // Calculamos el llenado del led que está "a medias"
}
}
void CalculateLeds2Fill(int valueNow, int range, uint16_t stripNumPixels)
{
int knobVal4Full = 0;
uint8_t valueLastLed = 0;
stripNumLedsOn = 0;
stripLastLedPercentOn = 100;
// Controlamos que el valor que nos llega no sea mayor que el rango máximo configurado
if (valueNow > (range - 1)) //Se resta 1 porque totalValues va de 0 a totalValues - 1
valueNow = range - 1;
// Lo primero es calcular cuantos valores del total corresponden a cada led del anillo
// Controlamos si el rango de valores es menor que el nº total de leds y en ese caso igualamos ambos para que
// en el momento del cálculo, cada valor del rango represente 1 led
if (range < stripNumPixels)
stripNumPixels = range;
// Se obtiene el cociente (el nº entero), que representa el nº de valores necesarios para llenar un led del anillo
knobVal4Full = range / stripNumPixels;
// Se hay resto en la división anterior, indica que hay que redondear hacia arriba en una unidad el nº de valores necesarios para llenar un led del anillo
if ((range % pixelNumber) > 0)
knobVal4Full++;
Serial.println("valueNow: " + String(valueNow));
Serial.println("range: " + String(range));
Serial.println("stripNumPixels: " + String(stripNumPixels));
Serial.println("knobVal4Full: " + String(knobVal4Full));
stripNumLedsOn = 1 + (valueNow / knobVal4Full); // Se calcula cuantos leds "llenos" representa el valor actual a representar
valueLastLed = valueNow % knobVal4Full; // Calculamos el llenado del led que está "a medias"
Serial.println("stripNumLedsOn: " + String(stripNumLedsOn));
stripLastLedPercentOn = (valueLastLed * 100) / knobVal4Full; // Calculamos qué % de lo que sería el 100% de un led representa este valor parcial
Serial.println("stripNustripLastLedPercentOn: " + String(stripLastLedPercentOn));
}
void PartialFill(uint16_t numLedsOn, uint8_t lastLedPercentOn, uint8_t brightness, HSV color)
{
uint8_t brightnessLastLedOn;
uint32_t valueBrightnessLastLedOn;
uint32_t rgbColor;
valueBrightnessLastLedOn = (brightness * lastLedPercentOn) / 100;
strip.clear();
for (int i = 0; i <= (numLedsOn - 1); i++)
{
rgbColor = strip.ColorHSV(color.hue, color.sat, brightness);
strip.setPixelColor(i, strip.gamma32(rgbColor));
}
rgbColor = strip.ColorHSV(color.hue,color.sat, valueBrightnessLastLedOn);
strip.setPixelColor(numLedsOn - 1, strip.gamma32(rgbColor));
//This sends the updated color to the hardware
strip.show();
}
void QuarterFill(byte Quarter, HSV color, int wait)
{
uint32_t rgbColor;
if (timeToWait != wait)
timeToWait = wait; // Update delay time
if ((Quarter < 1) || (Quarter > 4))
return;
int8_t LedsByQuarter = pixelNumber / 4;
strip.clear();
rgbColor = strip.ColorHSV(color.hue,color.sat,color.val);
strip.fill(strip.gamma32(rgbColor), ((Quarter - 1) * LedsByQuarter), LedsByQuarter); // Calculo (Quarter-1 * LedsByQuarter) para obtener el tramo a iluminar
// if (Quarter == 1)
// strip.fill(strip.gamma32(rgbColor), 0, LedsByQuarter); // Set pixel's color (in RAM)
// if (Quarter == 2)
// strip.fill(strip.gamma32(rgbColor), (1 * LedsByQuarter), LedsByQuarter); // Set pixel's color (in RAM)
// if (Quarter == 3)
// strip.fill(strip.gamma32(rgbColor), (2 * LedsByQuarter), LedsByQuarter); // Set pixel's color (in RAM)
// if (Quarter == 4)
// strip.fill(strip.gamma32(rgbColor), (3 * LedsByQuarter), LedsByQuarter); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
}
// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait)
{
if (timeToWait != wait)
timeToWait = wait; // Update delay time
// Hue of first pixel runs 5 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
// means we'll make 5*65536/256 = 1280 passes through this loop:
// for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256)
// {
// strip.rainbow() can take a single argument (first pixel hue) or
// optionally a few extras: number of rainbow repetitions (default 1),
// saturation and value (brightness) (both 0-255, similar to the
// ColorHSV() function, default 255), and a true/false flag for whether
// to apply gamma correction to provide 'truer' colors (default true).
strip.rainbow(pixelCurrent);
// Above line is equivalent to:
// strip.rainbow(firstPixelHue, 1, 255, 255, true);
strip.show(); // Update strip with new contents
pixelCurrent+= 512; // Advance current pixel
if (pixelCurrent >= 5*65536) // Loop the pattern from the first LED
pixelCurrent = 0;
// }
}
/* Rainbow cycle along whole strip.
cycles: number of cycles to run
Important!! Program flow will not continue until rainbow ends
*/
void blockingRainbow(uint8_t cycles)
{
// Hue of first pixel runs 5 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
// means we'll make 5*65536/256 = 1280 passes through this loop:
for(long firstPixelHue = 0; firstPixelHue < cycles*65536; firstPixelHue += 256)
{
// strip.rainbow() can take a single argument (first pixel hue) or
// optionally a few extras: number of rainbow repetitions (default 1),
// saturation and value (brightness) (both 0-255, similar to the
// ColorHSV() function, default 255), and a true/false flag for whether
// to apply gamma correction to provide 'truer' colors (default true).
strip.rainbow(pixelCurrent);
// Above line is equivalent to:
// strip.rainbow(firstPixelHue, 1, 255, 255, true);
strip.show(); // Update strip with new contents
pixelCurrent+= 512; // Advance current pixel
if (pixelCurrent >= cycles*65536) // Loop the pattern from the first LED
pixelCurrent = 0;
}
}
// Heartbeat effect.
void beatAndFade(HSV color, int wait)
{
uint32_t rgbColor;
byte inc = 2;
uint8_t minBrightness = 100;
// uint8_t brightnessVal;
// brightnessVal = (255 * LED_notFullFilled) / LED_knobVal4Full;
if (timeToWait != wait)
timeToWait = wait; // Update delay time
// Me aseguro de que no se ha configurado un brillo mínimmo mayor que el brillo máximo por defecto
// porque en ese caso este efecto no funcionaría
if (minBrightness >= stripMaxBrightness)
minBrightness = 0;
rgbColor = strip.ColorHSV(color.hue, color.sat, pixelCurrent);
strip.fill(strip.gamma32(rgbColor), 0, pixelNumber);
strip.show(); // Update strip with new contents
pixelCurrent = pixelCurrent + (inc * upDownCycle); // Advance current pixel
if (pixelCurrent > stripMaxBrightness) // Loop the pattern from the first LED
{
upDownCycle = -1;
pixelCurrent = stripMaxBrightness;
// Serial.println("decrement");
}
if (pixelCurrent <= minBrightness)
{
upDownCycle = 1;
pixelCurrent = minBrightness;
// Serial.println("increment");
}
// Serial.println("pixelCurrent: " + String(pixelCurrent));
}
/* Apaga el anillo led
*/
void turnOffLedRing()
{
strip.clear();
strip.show();
stripNumLedsOn = 0;
stripLastLedPercentOn = 0;
pixelCurrent = 0;
}
void turnOffOled()
{
display.clearDisplay();
display.fillScreen(SSD1306_BLACK);
display.display();
Serial.println("Apago la pantalla");
idleState = true;
OptionSelected = 0;
}
/* Convierte un texto en mayúsculas
*/
String aMayuscula(String cadena)
{
for (int i = 0; i < cadena.length(); i++)
{
cadena[i] = toupper(cadena[i]);
}
return cadena;
}
// void I2C_Scanner ()
// {
// Serial.println ();
// Serial.println ("I2C scanner. Scanning ...");
// byte count = 0;
// Wire.begin();
// for (byte i = 8; i < 120; i++)
// {
// Wire.beginTransmission (i); // Begin I2C transmission Address (i)
// if (Wire.endTransmission () == 0) // Receive 0 = success (ACK response)
// {
// Serial.print ("Found address: ");
// Serial.print (i, DEC);
// Serial.print (" (0x");
// Serial.print (i, HEX); // PCF8574 7 bit address
// Serial.println (")");
// count++;
// }
// }
// Serial.print ("Found ");
// Serial.print (count, DEC); // numbers of devices
// Serial.println (" device(s).");
// }
// void Efecto_01()
// {
// for (int i=0; i < NUMPIXELS; i++)
// {
// pixels.setPixelColor(i, pixels.Color(NP_redColor, NP_greenColor, NP_blueColor));
// //This sends the updated color to the hardware
// pixels.show();
// //Delay for a period of time (in milliseconds)
// delay(400);
// }
// //NP_Off();
// pixels.clear();
// pixels.show();
// delay(400);
// }