Go Down

Topic: Luces RGB (Read 2343 times) previous topic - next topic

IgnoranteAbsoluto

como podria insertar otro "modo" para que por ejemplo haga lo de la intensidad, haga un parpadeo, o que cambie de colores aleatoriamente
No tengo claro si lo afirmas o lo preguntas. Pero supongo que se trata de una pregunta.

Si quieres que pueda hacer parpadeos o cambiar de colores aleatoriamente, lo primero es tener claro qué es lo que quieres que haga. Luego ver cómo lo podría hacer y qué necesitaría para ello. Después ver cómo implementarlo, si adaptando lo que ya hay, añadiendo cosas nuevas o rehaciendo todo desde cero.

Para empezar, para el parpadeo habría que especificar cuánto tiempo quieres que esté encendido y cuanto apagado y si quieres que todos los parpadeos duren lo mismo o que cada uno tenga unos tiempos diferentes.

Y en cuanto a cambiar de color aleatoriamente ¿cuándo? cada vez que reciba una instrucción (letra) o automáticamente pasado cierto tiempo.

Lo de cambiar de color aleatoriamente cuando se recibe, por ejemplo, una 'U' es relativamente sencillo de implementar, ya que sólo has de cambiar las variables limiteR, limiteG y limiteB con valores aleatorios si te llega el carácter 'U'. Cada "TIEMPO_CAMBIO" milisegundos se actualizan los valores en el LED. Lo que si mantendrá si estaba "fijo" o "tenue".

Por cierto, no le he comentado, pero puedes añadir más letras y combinaciones de colores al array. El cálculo del límite del bucle se realiza "automáticamente".

surbyte

Ya vi el problema del código, le envias la orden y solo lo hace una vez.
Contaba conque una J recibida permaneciera en el loop hasta que se digite otra cosa pero en mis pruebas, luego de la J muestra un 0x00
asi que se detiene
POr eso solo avanzó a 5 de luminosidad y no siguió

OrnyTorrynKo

Lo del parpadeo seria por ejemplo 1s encendido y 1s apagado, seria igual que lo de las luces tenues, cuando le llegue una letra haga ese parpadeo con color rojo, por ejemplo.

Y respecto al cambio de color, como has dicho, cuando le llegue "U" cambie a otro color aleatorio cada unos 5 segundos, o por ejemplo cada vez que haga el aumento y decremento de iluminación cambie de color cada vez que llegue a 0, no se si esto ultimo se podrá hacer, supongo que si pero me faltan conocimientos aun xD

OrnyTorrynKo

Llevo toda la tarde intentando implementar en el código el cambio aleatorio de colores pero no termino de comprander algunas partes de los array y no soy capaz de terminar de programarlo

surbyte

Una matriz como la que uso IgnoranteAbsoluto permite manipular todas tus opciones de manera simple y rápida.

Code: [Select]
// El problema de usar un array es que consume RAM. Otra solución es usar PROGMEM: https://www.arduino.cc/en/Reference/PROGMEM
const byte ARRAY_VALORES[][NUMERO_CAMPOS] = {
    //      R    G    B  paso (se puede lograr más o menos velocidad cambiando el paso) (paso == 0 no cambia nunca el LED)
    {'A',   0,   0, 255,   0}, // Azul fijo
    {'B',   0, 255,   0,   0}, // Verde fijo
    {'C', 255,   0,   0,   0}, // Rojo fijo
    {'D', 255, 255,   0,   0}, // Amarillo fijo
    {'E', 255,   0, 255,   0}, // Rosa fijo
    {'F',   0, 255, 255,   0}, // Celeste fijo

    {'G',   0,   0, 255,   5}, // Azul tenue
    {'H',   0, 255,   0,   5}, // Verde tenue
    {'I', 255,   0,   0,   5}, // Rojo tenue
    {'J', 255, 255,   0,   5}, // Amarillo tenue
    {'K', 255,   0, 255,   5}, // Rosa tenue
    {'L',   0, 255, 255,   5}, // Celeste tenue

    {'V', 255, 255, 255,   0}, // Blanco fijo
    {'W', 255, 255, 255,   5}, // Blanco tenue

    {'X', 140, 255,  55,   0}, // fijo
    {'Y', 140, 255,  55,   3}, // tenue

    {'Z',   0,   0,   0,   0}  // Apagado
};


Primero si miras, con detenimiento, la matriz repite ordenadamente cada una de tus opciones, mas algunas que agregó justamente para darte mas opciones.
Tienes una matriz de bytes, con muchas filas y solo 5 columnas.
Las columnas tienen un primer valor que es tu comando anterior. El que ingresabas por Serial.
Luego los valores RGB y por ultimo un valor de paso usado para el incremento/decremento.


La variable que contiene todo la llamó ARRAY_VALORES[fila][columna]
si pones diferentes valores en fila y columna, recuperas los datos almacenados en cada uno de las posiciones de la matriz.

De ese modo
ARRAY_VALORES[0][0] devuelve 'A'
ARRAY_VALORES[0][1] devuelve 0
ARRAY_VALORES[0][2] devuelve 0
ARRAY_VALORES[0][3] devuelve 255
ARRAY_VALORES[0][4] devuelve 0

Dime si ahora se ve mejor esto?

OrnyTorrynKo

Si eso llego a entenderlo, el problema es a la hora de implementar otro modo como el del cambio de color aleatoriamente o un parpadeo. Ando perdido en como implementar algo de eso.

surbyte

Tal como esta hecho el código ahora, un parpadeo podrias hacerlo como algo que cambia de 0 a 255 en cada paso, asi que implementa una nueva fila con LETRA asociada, y usa un paso 255 para que salte de Brillo total o apagado.

Cambio de color aleatorio requiere algo como random() entre 3 opciones o 4 RGB y blanco.
Si ese fuera el caso, de nuevo, elige una letra nueva ('M') que será tu opcion de aleatoriedad y harías algo asi
debes agregar
 {'M',   0,   0,  0,   0}, // Aleatorio A..F
 {'N',   0,   0,  0,   0}, // Aleatorio G..L

Code: [Select]

for (size_t i = 0; i < (sizeof(ARRAY_VALORES) / NUMERO_CAMPOS); i++) {
            byte datos;
            switch (datos_serial) {
                      case 'M': datos = random(0,5)+'A'; // o sea que random dará valores aleatorios entre 0 y 2, que debemos sumar
                                               // a los grupos A.. F
                                   break;
                      case 'N': datos = random(0,5)+'G'; // o sea que random dará valores aleatorios entre 0 y 2, que debemos sumar
                                               // a los grupos G..L
                                   break;
                      default:  datos = datos_serial;
                         
            }
            if (ARRAY_VALORES[i][INDICE_LETRA] == datos) { // Buscamos la tecla pulsada


De nuevo, es algo que implemento al pasar, pruébalo a ver si funciona.


IgnoranteAbsoluto

Antes de nada, pido disculpas porque lo he hecho un poco con prisas. Creo que la mejor solución es poder mandar "comandos" básicos que configure el LED como queramos. Y así no tenemos que volver a tocar el programa del Ardunino para nada.

Aquí está el programa:

Code: [Select]

const int PIN_G = 9;
const int PIN_B = 10;
const int PIN_R = 11;

const unsigned long TIEMPO_CAMBIO = 30; // Milisegundos que pasa entre cambio de valor. Se puede lograr más o menos velocidad cambiando el paso
const byte VALOR_MINIMO = 2;            // Se puede probar con 0, pero como se apaga del todo hace un efecto que no me gusta mucho
const byte VALOR_MAXIMO = 255;          // No aconsejo cambiarlo

byte valorR = 0;
byte valorG = 0;
byte valorB = 0;
byte limiteR = 0;
byte limiteG = 0;
byte limiteB = 0;

enum estado_lectura_t {
    ESTADO_LEE_R,
    ESTADO_LEE_G,
    ESTADO_LEE_B,
    ESTADO_LEE_PASO,
    ESTADO_LEE_PARPADEO,
    ESTADO_LEE_PARPADEO_APAGADO,
    ESTADO_LEE_PARPADEO_ENCENDIDO,
    ESTADO_REPOSO
};
estado_lectura_t estadoLectura = ESTADO_REPOSO;

enum estado_parpadeo_t {
    ESTADO_PARPADEO_INACTIVO,
    ESTADO_PARPADEO_ENCENDIDO,
    ESTADO_PARPADEO_APAGADO
};
estado_parpadeo_t estadoParpadeo = ESTADO_PARPADEO_INACTIVO;

unsigned long tiempoParpadeoEncendido = 0;
unsigned long tiempoParpadeoApagado = 0;
unsigned long momentoCambioParpadeo = 0;
unsigned int valorLeido = 0;
byte digitosLeidos = 0;
int paso = 0;
int valorReferencia = VALOR_MAXIMO;
unsigned long momentoCambio = 0;

void finValor() {
    switch (estadoLectura) {
        case ESTADO_LEE_R :
                valorR = limiteR = (valorLeido > VALOR_MAXIMO) ? VALOR_MAXIMO : valorLeido;
            break;
        case ESTADO_LEE_G :
                valorG = limiteG = (valorLeido > VALOR_MAXIMO) ? VALOR_MAXIMO : valorLeido;
            break;
        case ESTADO_LEE_B :
                valorB = limiteB = (valorLeido > VALOR_MAXIMO) ? VALOR_MAXIMO : valorLeido;
            break;
        case ESTADO_LEE_PARPADEO :
        case ESTADO_LEE_PARPADEO_APAGADO :
                tiempoParpadeoApagado = valorLeido * 10; // valorLeido se consideran como centésimas de segundo
        case ESTADO_LEE_PARPADEO_ENCENDIDO :
                if (estadoLectura != ESTADO_LEE_PARPADEO_APAGADO) {
                    tiempoParpadeoEncendido = valorLeido * 10; // valorLeido se consideran como centésimas de segundo
                }
                if ( (tiempoParpadeoEncendido == 0) || (tiempoParpadeoApagado == 0) ) {
                    estadoParpadeo = ESTADO_PARPADEO_INACTIVO;
                }
                else if (estadoParpadeo == ESTADO_PARPADEO_INACTIVO) {
                    momentoCambioParpadeo = millis();
                    estadoParpadeo = ESTADO_PARPADEO_ENCENDIDO;
                }
            break;
        case ESTADO_LEE_PASO :
                paso = (valorLeido > (VALOR_MAXIMO - VALOR_MINIMO)) ? (VALOR_MAXIMO - VALOR_MINIMO) : valorLeido;
                valorReferencia = VALOR_MAXIMO;
            break;
        case ESTADO_REPOSO : // No se hace nada
            break;
    }
    digitosLeidos = 0;
    valorLeido = 0;
    estadoLectura = ESTADO_REPOSO;
}

void setValor(byte valor) {
    valorLeido = (valorLeido * 10 + (valor - '0')) % 1000;
    digitosLeidos++;
    if (digitosLeidos >= 3) {
        finValor();
    }
}

void setEstado(estado_lectura_t nuevoEstado) {
    finValor();
    estadoLectura = nuevoEstado;
}

void setup() {
    Serial.begin(9600);
    pinMode(PIN_R, OUTPUT);
    pinMode(PIN_G, OUTPUT);
    pinMode(PIN_B, OUTPUT);
}

void loop() {
    if (Serial.available() > 0) {
        byte datoSerie = Serial.read();
        Serial.write(datoSerie);        // Comentar esta línea si no se quiere "eco"
        switch (datoSerie) {
            case 'R' :
            case 'r' :
                setEstado(ESTADO_LEE_R);
                break;
            case 'G' :
            case 'g' :
                setEstado(ESTADO_LEE_G);
                break;
            case 'B' :
            case 'b' :
                setEstado(ESTADO_LEE_B);
                break;
            case 'V' :
            case 'v' :
                setEstado(ESTADO_LEE_PASO);
                break;
            case 'P' :
            case 'p' :
                setEstado(ESTADO_LEE_PARPADEO);
                break;
            case 'A' :
            case 'a' :
                setEstado(ESTADO_LEE_PARPADEO_APAGADO);
                break;
            case 'E' :
            case 'e' :
                setEstado(ESTADO_LEE_PARPADEO_ENCENDIDO);
                break;
            default :
                if ( (datoSerie >= '0') && (datoSerie <= '9') ) {
                    setValor(datoSerie);
                }
                else {
                    finValor();
                }
        }
    }
    if ( (millis() - momentoCambio) >= TIEMPO_CAMBIO ) {
        momentoCambio = millis(); // Guardamos el instante en el que se ha hecho el cambio
        valorR = map(valorReferencia, 0, 255, 0, limiteR);
        valorG = map(valorReferencia, 0, 255, 0, limiteG);
        valorB = map(valorReferencia, 0, 255, 0, limiteB);
        if (estadoParpadeo == ESTADO_PARPADEO_APAGADO) {
            analogWrite(PIN_R, LOW);
            analogWrite(PIN_G, LOW);
            analogWrite(PIN_B, LOW);
        }
        else {
            analogWrite(PIN_R, valorR);
            analogWrite(PIN_G, valorG);
            analogWrite(PIN_B, valorB);
        }
        if ( ( (valorReferencia + paso) > VALOR_MAXIMO ) || ((valorReferencia + paso) < VALOR_MINIMO) ) {
            // Como se sale de los límites con en el próximo paso, lo cambiamos de sentido
            paso = -paso;
        }
        valorReferencia += paso;
    }

    if ( (estadoParpadeo == ESTADO_PARPADEO_APAGADO) && ( (millis() - momentoCambioParpadeo) >= tiempoParpadeoApagado ) ) {
        estadoParpadeo = ESTADO_PARPADEO_ENCENDIDO;
        momentoCambioParpadeo = millis();
        analogWrite(PIN_R, valorR);
        analogWrite(PIN_G, valorG);
        analogWrite(PIN_B, valorB);
    }
    else if ( (estadoParpadeo == ESTADO_PARPADEO_ENCENDIDO) && ( (millis() - momentoCambioParpadeo) >= tiempoParpadeoEncendido ) ) {
        estadoParpadeo = ESTADO_PARPADEO_APAGADO;
        momentoCambioParpadeo = millis();
        analogWrite(PIN_R, LOW);
        analogWrite(PIN_G, LOW);
        analogWrite(PIN_B, LOW);
    }
}

IgnoranteAbsoluto

Y aquí están las instrucciones.

Al Arduino se le manda por el puerto serie un comando formado por una letra (da igual si es mayúscula o minúscula) y hasta tres dígitos que configuran su valor. No se "realiza la acción" hasta que lleguen los tres dígitos o llegue otro carácter que no sea un dígito del 0 al 9 (no importa si ese otro carácter es un comando válido o no).

Los comandos son:
R establece el valor del rojo (de 0 a 255)
G establece el valor del verde (de 0 a 255)
B establece el valor del azul (de 0 a 255)
V establece la velocidad con que varía de intensidad continuamente (de 0 a 255). El 0 lo desactiva y deja el LED al máximo preestablecido.
P establece en centésimas de segundo el tiempo que está encendido y apagado  durante el parpadeo (de 0 a 999). El 0 desactiva el parpadeo y se queda fijo.
E establece en centésimas de segundo el tiempo que está encendido  durante el parpadeo (de 0 a 999). El 0 desactiva el parpadeo y se queda fijo.
A establece en centésimas de segundo el tiempo que está apagado durante el parpadeo (de 0 a 999). El 0 desactiva el parpadeo y se queda fijo.

Si el valor supera el máximo permitido, se evalúa como el máximo. Los comandos diferentes son "acumulativos" es decir, si le decimos que se ponga rojo, establecemos una velocidad de cambio y le decimos que parpadee, se pondrá rojo, cambiará de brillo y parpadeará.

Se puede probar con el monitor del IDE de Arduino. Tiene "eco" para que se vea lo que le estamos enviando. Si no se quiere que tenga "eco" basta con comentar la línea indicada en el programa.

Ejemplo, si queremos que se ponga verde le mandamos:
G255

Si ahora queremos que pase de verde a celeste bastaría con encender el azul:
B255

Si ahora queremos que se ponga rojo, habría que apagar el verde y el azul, y encender el rojo. Como ya he comentado, si después del comando no se completan los tres dígitos porque se manda otro comando u otro carácter que no reconoce, tomará el valor 0 o lo el de los dígitos que hubieran. Así que para apagar el verde y el azul y encender el rojo no hace falta mandar G000B000R255, basta con mandar:
GBR255

Para que ahora parpadee a un hercio, podemos decirle que lo haga con un tiempo de medio segundo (medio segundo encendido, medio segundo apagado) y como lo que se le dice son centésimas de segundo, hay que mandar es 50 centésimas. Si no vamos a mandar seguidamente otro comando, lo que tendremos que mandar el 50 con tres dígitos (050)
P050

Si ahora queremos que esté encendido segundo y medio en lugar de medio segundo, podemos cambiar sólo el tiempo que está encendido mandando:
E150

Si queremos apagar el parpadeo nos basta con poner a cero cualquier parámetro del parpadeo P000, E000 o A000 pararían el parpadeo y dejarían el LED todo el tiempo encendido.

El parpadeo y el cambio de velocidad son compatibles entre sí y pueden estar activos a la vez. Ejemplo:
RGB255V2E50A003

Sólo me he preocupado de completar con ceros a la izquierda el último comando ya que como no hay otro que lo termine es imprescindible que tenga tres dígitos. También se puede "completar" mandando un carácter "no válido" como puede ser el retorno de carro, o un punto, o una X. Esto haría lo mismo:
RGB255V2E50A3;

Así que si se quiere apagar, bastaría con mandar:
RGB;

Pero eso no para el parpadeo ni el cambio de intensidad. Así que si estaban activos antes de apagar, seguirán activos si se enciende (probar a mandar B255). Para "apagarlo todo" habría que mandar:
RGBVP;

Ahora si se manda B255 se encenderá fijo el azul.

Bueno, espero que se entiendan mis explicaciones y que esto le sirva a OrnyTorrynKo.

OrnyTorrynKo

Nuevamente muchisima gracias a ambos.

El código de surbyte no tengo muy claro como implementarlo y de todas las formas que he probado al mandar "M" se que el led apagado.

Para el codigo de IgnoranteAbsoluto voy a modificar la app completamente para poder probarlo pero tengo la duda si en este código viene lo de las luces tenues y lo de los colores aleatorios o lo has quitado y has puesto el parpadeo? ya que me interesaria mas lo de las luces tenues y los colores aleatorios.
Y otra pregunta que te qeria hacer es si este código o el anterior que realizaste se puede adaptar a anillos rgb que funcionan con la libreria neopixel de Adafruit.

OrnyTorrynKo

IgnoranteAbsoluto he modificado la app y tu código funciona a la perfeccion.

Ya me he dado cuenta de que si esta incluido lo de las luces tenues jejeje. Muchas gracias!

Intentaré incluir lo de el cambio de color aleatorio a ver que tal se me da.

surbyte

Agregaste OrnyTorrynKo lo que te indiqué? Seguramente no.. ni te tomaste el tiempo.

Claro que si IgnoranteAbsoluto te va a resolver cada opción jajaja ni vale la pena considerar lo que te digo.

Asi que lo dejo ahi.

OrnyTorrynKo

Surbyte como dije antes en el comentario anterior intente implementarlo, lo probé incluso antes que el codigo de IgnoranteAbsoluto, pero no me terminaba de quedar claro como incluirlo en el codigo, por lo que me daba fallos y ya probé el otro codigo

IgnoranteAbsoluto

OrnyTorrynKo, como ya dije, lo hice con prisas. Para probar el concepto y ver si funcionaba. Así que no implementé lo de los colores aleatorios. Habrá que esperar a que tenga un hueco y me pueda sentar a implementarlo.

Lo de los neopixel tiene una complicación y un posible problema. La complicación es que ya no se trata de uno, sino de varios LEDs y supongo que querrás manejar cada uno por separado (¿O quieres que todos tengan el mismo comportamiento a la vez?). Para ello lo más práctico sería rediseñar y modificar el programa para que fuera orientado a objetos. Definir una clase que controle un neopixel y crear un array de objetos de esa clase. Por otro lado habría que ingeniar algún sistema para "direccionar" un determinado neopixel para poder aplicar los "comandos" sólo a él, o a un conjunto de ellos.

Otro problema es que si son muchos neopixel los tiempos no pueden ser muy cortos, ya que "tratar" todos los neopixel lleva su tiempo. Y el transmitir el cambio de sólo uno de ellos supone tener que transmitir de nuevo la información de todos ellos, y eso lleva su tiempo.

Otra cosa que hay que tener en cuenta es que por cada neopixel se va a necesitar cierta cantidad de memoria para gestionarlos, así que no pueden ser muchos porque te podrías quedar sin memoria. Y por otro lado ten en cuenta también que consumen bastante corriente y si son muchos tendrás que alimentarlos "externamente". ¿Cuántos neopixel estás pensando manejar?

OrnyTorrynKo

IgnoranteAbsoluto, no te preocupes por no implementarlo, solo tenia la duda y bastante me estas ayudando jeje.

Por otro lado, volviendo al tema, en un principio me conformo con que todos los pixel trabajen de la misma forma, no es necesario que cada uno tenga un comportamiento distinto. En principio tenia pensado un anillo de 8 leds y para controlarlo seria el Arduino Nano, ya que quiero que me ocupe el menos sitio posible.
¿Que opinas o que me recomendarias?

Go Up