Clonar un mando de radio frecuencia con Arduino y un receptor/emisor de 433Mhz

Esta técnica es una forma rápida y sencilla de clonar un mando de radiofrecuencia, aunque desgraciadamente no funciona con todos los mandos. Ahora con los que funciona puedes duplicar su funcionamiento en pocos minutos. Además de un Arduino necesitaremos un receptor y un emisor de la frecuencia en la que funcione el mando. En nuestro caso 433 MHz.

Vamos a usar un receptor muy sencillo que tiene cuatro patillas  a parte de +5v y GND necesitamos conectar la patilla por la que recibiremos los datos (en mi caso la que está junto a la de +5v) al puerto digital número 2. Es la patilla cuya interrupción escuchará la librería que vamos a usar: rc-switch. La librería se puede encontrar desde la sección librerías del IDE e instalarla.

Una vez instalada la librería cargaremos el ejemplo ReceiveDemo_Advance.

Una vez cargado abriremos el serial monitor donde deberían aparecer los datos que envía el mando cuando pulsemos el botón. Copiaremos el código decimal, el numero de bits, el protocolo y la duración del pulso. estos datos vamos a meterlos en este programa:

#include <RCSwitch.h>
RCSwitch mySwitch = RCSwitch();

void setup() {
  Serial.begin(9600);
  
  // Pîn digital al que se conecta el emisor
  mySwitch.enableTransmit(10);
  
  // Protocolo
  mySwitch.setProtocol(1);

  // Duracion del pulso
  mySwitch.setPulseLength(503);
  
  // Cuantas veces se repite la transmision
  mySwitch.setRepeatTransmit(10);
  
}

void loop() {
  Serial.println("Enviando señal");
  //código decimal y nº bits
  mySwitch.send(2351425, 24); 
  delay(10000);  
}

Para ejecutarlo necesitaremos un emisor de 433 MHz. en este caso ademas de a 5V y GND conectaremos el pin por el que envía los datos al pin digital 10 de la placa de Arduino.

Puedes ver un vídeo con un ejemplo de su uso y más información en mi canal de Youtube:

Haz click para ver el vídeo en mi canal de Youtube

Crear y modificar imágenes con DALL-E

DALL-E no solo permite crear imágenes a partir de un texto. Se pueden modificar y extender. La propia web tiene herramientas para hacerlo.

Pero como es algo que es más fácil de explicar visualmente que por escrito, podéis ver cómo hacerlo explicado en mi siguiente vídeo de mi canal de Youtube:

Haz click en la imagen para ver el vídeo en mi canal de Youtube

Comprobar lo rápido que ordena Arduino

Vamos a ver cómo medir lo rápido que es capaz de ordenar Arduino un listado de 400 enteros (con más qsort falla en Arduino UNO). Aprovecharemos esto para ver cómo medir el tiempo que le cuesta ejecutar el código en Arduino.

Evitar «interrupciones»

Lo primero es quitarnos de «en medio» cualquier molestia a nuestro código en Arduino

  • Procesos asíncronos como la comunicación por Serial o el Watchdog
  • Interrupciones

En el caso de tener el Watchdog activado debemos asegurarnos de que no salta mientras probamos el bloque de código que queremos medir. Podemos deactivarlo con wdt_disable();

Con Serial debemos vaciar la cache usando Serial.flush(); para volcarla

En el caso de las interrupciones se puede usar noInterrupts(); para evitar que se lancen.

Como medida de seguridad extra podemos añadir un delay para dar tiempo a que todo termine. Aunque es una medida más paranoica que otra cosa debido a que Arduino es una arquitectura de un solo hilo (a diferencia de los procesadores modernos que tienen múltiples hilos). No hay manera de que quede alguna «tarea pendiente». Pero, al menos yo, me quedo más tranquilo.

En el caso de nuestro ejemplo no tenemos ni watchdog ni interrupciones, pero si comunicación usando Serial.

Medir el tiempo

Para medir el tiempo debemos crear dos variables de tipo unsigned long una guardara el tiempo justo antes de ejecutar el código y otra justo después. Para saber el tiempo total de ejecución del código basta con restarlas. Si el codigo le cuesta ejcutarsemenos de 70 minutos podemos usar micros() si se cuesta mas millis().

Ejemplo

Veamos el ejemplo comentado al principio midiendo el coste de ordenar 400 elementos:

int cmp_desc(const void *c1, const void *c2){  
  return *((int *)c2) - *((int *)c1);
}
int cmp_asc(const void *c1, const void *c2){  
  return *((int *)c1) - *((int *)c2);
}
void setup() {
  Serial.begin(9600);
  Serial.println("Start");
  Serial.flush();
  int array[400] = {32, 162, 26, 82, 85, 220, 157, 149, 142, 255, 160, 66, 151, 37, 204, 0, 242, 92, 34, 24, 124, 14, 19, 173, 187, 160, 223, 225, 59, 151, 237, 245, 125, 35, 143, 125, 210, 53, 129, 136, 126, 174, 88, 138, 17, 132, 68, 116, 154, 7, 77, 31, 167, 59, 147, 165, 232, 59, 184, 169, 175, 116, 77, 110, 118, 211, 19, 111, 142, 247, 0, 120, 229, 138, 43, 173, 210, 98, 218, 75, 142, 101, 204, 14, 30, 130, 102, 142, 214, 176, 153, 178, 3, 199, 135, 191, 149, 22, 230, 64, 15, 165, 225, 187, 85, 204, 69, 144, 147, 9, 49, 134, 208, 171, 151, 231, 217, 37, 42, 68, 135, 102, 212, 216, 51, 177, 137, 242, 117, 154, 241, 32, 119, 210, 5, 180, 63, 209, 180, 39, 106, 11, 232, 6, 216, 249, 223, 113, 29, 78, 210, 138, 69, 2, 63, 37, 91, 146, 155, 243, 92, 174, 143, 122, 102, 130, 203, 168, 186, 255, 17, 167, 162, 41, 158, 81, 56, 213, 209, 11, 248, 108, 146, 82, 230, 159, 132, 17, 168, 165, 161, 190, 4, 53, 181, 149, 52, 64, 219, 215, 72, 4, 79, 186, 8, 16, 40, 32, 82, 115, 56, 138, 46, 126, 255, 124, 21, 85, 211, 13, 21, 21, 111, 227, 88, 128, 247, 158, 188, 210, 196, 190, 24, 38, 198, 81, 168, 245, 174, 40, 74, 236, 78, 68, 48, 44, 130, 34, 133, 118, 215, 242, 168, 21, 123, 84, 77, 140, 30, 83, 94, 29, 94, 138, 46, 223, 228, 13, 2, 70, 87, 74, 47, 100, 193, 86, 80, 237, 130, 142, 152, 239, 113, 114, 133, 160, 217, 34, 161, 214, 168, 92, 216, 178, 67, 188, 110, 136, 183, 147, 127, 209, 88, 102, 133, 196, 15, 66, 237, 189, 208, 0, 98, 147, 116, 130, 214, 231, 58, 150, 227, 155, 117, 133, 42, 98, 114, 254, 17, 80, 113, 63, 215, 190, 35, 171, 89, 180, 91, 26, 147, 39, 126, 66, 34, 1, 139, 87, 183, 129, 153, 106, 219, 245, 143, 182, 62, 99, 27, 82, 198, 234, 158, 122, 16, 119, 254, 241, 170, 186, 197, 192, 46, 133, 179, 54, 236, 35, 34, 97, 48, 150, 19, 26, 235, 17, 15, 182, 201, 151, 30, 40, 94, 188, 192, 149, 220, 250, 16, 35};
  unsigned long timeStart;
  unsigned long timeEnd;
  delay(500);
  timeStart = micros();    
  qsort(array, 400, sizeof(int), cmp_asc);
  timeEnd = micros();  
  Serial.print((timeEnd-timeStart));
  Serial.println();
  
}
void loop()
{
}

Puedes ver el vídeo donde ejecuto el ejemplo y muestro sus resultados:

Haz click para ver el vídeo en mi canal de Youtube

Como ordenar un array en Arduino

A veces tras la librerías oficiales de Arduino nos «tapan» utilidades que tienen las librerías avr-libc del fabricante del controlador. en este caso vamos a usar la función qsort() que implementar el algoritmo quicksort. No entraremos en detalle, simplemente aceptaremos que ordena y lo hace rápido.

qsort(void *base, size_t n_memb, size_t size, cmp_t *cmp)

En este caso la función qsort() requiere 4 parámetros:

  • *base : array de elementos a ordenar
  • n_memb : número de elementos del array
  • size : tamaño de cada elemento
  • cmp : función que realiza la comparación

Como qsort no sabe mágicamente como quieres ordenar lo elementos del array hay que pasar una función de comparación entre dos elementos que tiene que devolver los siguientes valores:

  • Si p1 == p2 devuelve 0
  • Si p1 va antes de p2 devuelve -1 (o culaquier número negativo)
  • Si p2 va después de p1 devuelve 1 (o culaquier número positivo)

La función comparación tiene la sigueinte firma:

int cmp_desc(const void *c1, const void *c2)

Como los parámetros se pasan como un puntero (a void) hay que realizar un cast al tipo de elemento de nuestro array. En nuestro ejemplo int, veamos el código de ejemplo de la función ordenar descendente (primero los números mayores)

int cmp_desc(const void *c1, const void *c2){  
  return *((int *)c2) - *((int *)c1);
}

Veamos un ejemplo completo con ordenación ascendente y descendente:

int cmp_desc(const void *c1, const void *c2){  
  return *((int *)c2) - *((int *)c1);
}

int cmp_asc(const void *c1, const void *c2){  
  return *((int *)c1) - *((int *)c2);
}


void setup() {
  Serial.begin(9600);

  int array[10] = {10, 5, 34, 76, 7, 6, 5, 23, 2, 42};
  
  qsort(array, 10, sizeof(int), cmp_asc);
  
  Serial.println("Resultado ascendente: ");
  for(int i = 0; i < 10; i++){
    Serial.print(array[i]);
    Serial.print(", ");
  } 
  Serial.println("");

  qsort(array, 10, sizeof(int), cmp_desc);

  Serial.println("Resultado descendente: ");
  for(int i = 0; i < 10; i++){
    Serial.print(array[i]);
    Serial.print(", ");
  } 
  Serial.println("");
}


void loop()
{
}

Y así de sencillo podemos ordenar cualquier array siempre que podemos hacer una función que compare valores. No necesitamos ningún tipo de librerías externas.

Puedes ver como funciona este código en el siguiente vídeo de mi canal de Youtube:

Haz click para ver el vídeo en mi canal de Youtube

Crear prompts de DALL-E y Stable Diffusion con GPT-3

En una muestra de «delegar el trabajo a otros» vamos a intentar que sea GPT-3. El que nos cree el prompt para dibujar con DALL-E o con Stable Diffusion. Para ello vamos a jugar «al teléfono roto», le pediremos a GTP-3 que nos describa una obra de arte famosa, de la descripción quitaremos cualquier referencia a la obra original y le pasaremos ese texto a DALL-E y Stable Diffusion a ver que dibujan.

Antes de empezar or voy a ahorrar las pruebas que estuve haciendo hasta encontrar «las palabras mágicas» para que GPT-3 me describa el aspecto de algo y no me cuente su historia:

Describe what XXXX looks like in 50 words

Donde XXXX es lo que queremos que describa. La parte final de «in 50 words» es necesario para limitar el tamaño del resultado y que se centre en lo importante ya que DALL-E tiene un prompt limitado a 400 caracteres.

Pintura

Empezaremos por un cuadro famoso, la Mona Lisa

Describe what the Mona Lisa looks like in 50 words

Que GTP-3 completa con:

The Mona Lisa is a portrait of a woman with a soft expression and subtle colors. She is seated with her hands folded in her lap, and her gaze seems to follow the viewer around the room.

Tras «limpiarlo» le pasamos a DALL-E:

A portrait of a woman with a soft expression and subtle colors. She is seated with her hands folded in her lap, and her gaze seems to follow the viewer around the room.

Que crea como resultado:

DALL-E

DALL-E hace una curiosa reinterpretación, aunque lo de las manos no parece haberlo entendido.

Stable Diffusion

Stable Diffusion parece haber entendido mejor la tarea dando versiones más parecidas a lo que tenia en mente.

Escultura

Vamos a por la escultura, el David de Miguel Ángel:
Describe what michelangelo’s david looks like in 50 words

Que obtiene:
Michelangelo’s David is a life-size marble sculpture of the Biblical hero David, created between 1501 and 1504. The statue depicts David in the moment before he defeats Goliath, and is considered one of the most famous sculptures in the world.

Dude si dejar la referencia a David o no, decidi dejarla porque no apunta exactamente al David de Miguel Ángel.
A life-size marble sculpture of the Biblical hero David, created between 1501 and 1504. The statue depicts David in the moment before he defeats Goliath, and is considered one of the most famous sculptures in the world.

DALL-E

DALL-E en este caso ha sabido captar el sentido sin copiar la obra. Ademas que esa barba le pega más a un personaje bíblico que el lampiño David de Miguel Angel

Stable Diffusion

Stable Diffusion se ha empeñado en sacar desnudos, muchos más parecidos al de Miguel Angel

Arquitectura

Vamos a por la arquitectura, la Torre Eiffel.

Describe what the Eiffel Tower looks like in 50 words

The Eiffel Tower is a large iron lattice tower located on the Champ de Mars in Paris, France. Built in 1889, it is one of the most recognizable structures in the world. The tower is 324 metres (1,063 ft) tall and has two platforms, the first at 187 metres (614 ft), and the second at 276 metres (906 ft). There are stairs and elevators to the top.

Tras limpiar todas la referencias (que son bastantes):

A large iron lattice tower built in 1889. The tower is 324 metres (1,063 ft) tall and has two platforms, the first at 187 metres (614 ft), and the second at 276 metres (906 ft). There are stairs and elevators to the top.

DALL-E

Para DALL-E Torre Eiffel no hay más que una.

Stable Diffusion

Stable Diffusion da resultados mucho más originales y esta claro que no sabe que hablamos de la Torre Eiffel

Para ver mas ejemplos, en este caso de un dragón, podéis ver el siguiente vídeo de mi canal de Youtube:

Haz click para ver el vídeo en Youtube

Generador de funciones / señales barato y simple usando Arduino

Ya vimos como convertir de forma barata de PWM a analógico. Ahora que tenemos una salida analógica ¿Podemos usarla para generar distintos tipos de señales? Vamos aintentar generar señales de distinto tipo sinusoidal, triangular, cuadrada (aunque para eso tenemos PWM), triangular, ….

Sin embargo vamos a tener alguna limitaciones:

  • Las limitaciones son que el valor mínimo de salida es 0v y el máximo 5v.
  • La frecuencia no puede ser muy alta. La base de nuestro sistema es un señal cuadrada funciona a 32 kHz y convierte la anchura de la señal (0-100%) a voltaje (0-5v). Para que esto ocurra necesita algunos pulsos, ademas que para reconstruir la señal necesita varios puntos. ¿Cuántos? Depende del tipo de señal, pero tomando la sinusoidal como referencia en mis pruebas el limite es unos 500-600HZ con la primera versión del programa y unos 1500-2000Hz con la segunda versión.

Ya vimos como generar distintas funciones para las ondas, ahora hay que añadir un cambio, las generaremos devolviendo un valor entre 0 y 1. Las funciones modificadas son las siguientes:

//Distintas señales a elegir
double signal = tf; //dientes de sierra
double signal = (sin(tf*2*PI)+1)/2 ; //seno
double signal = sin(tf*PI); //seno positiva
double signal = (sin(tf*2*PI) > 0) ? 1: 0; //cuadrada
double signal = (sin(tf*2*PI) >= 0) ? 2*tf: 2-(2*tf); //triangular

Solo podemos usar una cada vez. Puedes definir tus propias funciones mientras que devuelvan un valor entre 0 y 1. Donde 0 representa 0v y 1 es igual a 5v, cualquier valor intermedio representa un voltaje proporcional. Por ejemplo 0.5 representa 2.5v

Veamos el código:

float t = 0;
float f = 100; //frecuencia
float p = 1/f; //periodo
unsigned long oldMicros = 0;
unsigned long nowMicros = 0;

const byte PRESCALER2 = 0b001;

void setup() {
  //ajusta la frec. salida PWM pin 3
  TCCR2B = (TCCR2B & 0b11111000) | PRESCALER2;
  pinMode(3, OUTPUT);
}

double dt = 0;
double tf = 0;
double signal = 0;

void loop() {
  nowMicros = micros();
  dt = double(nowMicros - oldMicros)/1000000;
  
  t += dt;
  tf = t*f;
  //Distintas señales a elegir
  //signal = tf; //dientes de sierra
  signal = (sin(tf*2*PI)+1)/2 ; //seno  
  //signal = sin(tf*PI); //seno positiva
  //signal = (sin(tf*2*PI) > 0) ? 1: 0; //cuadrada
  //signal = (sin(tf*2*PI) >= 0) ? 2*tf: 2-(2*tf); //triangular
  
  byte value = byte(255 * signal);
  analog(value);

  oldMicros = nowMicros;
  
  //cuando alcanza un periodo se reinicia
  if(t >= p){ 
    t = 0;
  }
}

//compensa el desajuste de la salida analogica
void analog(byte value){
  value += (255 - value) >> 4; 
  analogWrite(3, value); 
}

Con esta versión se puede alcanzar una frecuencia máxima (probada con una señal sinusoidal) de entre 500-600Hz.

Alcanzando mayores frecuencias

Para intentar exprimir al máximo nuestro Arduino vamos a optimizar el código todo lo posible. Para ello vamos a reducir los cálculos necesarios en cada iteración. Lo haremos precalculando en un array los valores de la onda en varios puntos. Luego en cada iteración calcularemos que punto nos toca mostrar del array. Esta forma da lugar a ondas más «sucias» con más ruido. Pero también permite mayores frecuencias.

float f = 1000; //frecuencia
float p = 1/f; //periodo
float t = 0;
int i = 0;
unsigned long oldMicros = 0;
unsigned long nowMicros = 0;
const int WAVE_POINTS = 2000; //puntos generados

byte wave[WAVE_POINTS]; //valores generados
double waveDt = p/WAVE_POINTS; //tiempo entre cada punto 

const byte PRESCALER2 = 0b001;

void setup() {
  //ajusta la frec. salida PWM pin 3
  TCCR2B = (TCCR2B & 0b11111000) | PRESCALER2;
  pinMode(3, OUTPUT);
  
  //Precalculamos la onda
  for(i = 0; i < WAVE_POINTS; i++){
    t += waveDt;
    double tf = t*f;
    //double signal = tf; //dientes de sierra
    //double signal = (sin(tf*2*PI)+1)/2 ; //seno  
    //double signal = sin(tf*PI); //seno positiva
    //double signal = (sin(tf*2*PI) > 0) ? 1: 0; //cuadrada
    double signal = (sin(tf*2*PI) >= 0) ? 2*tf: 2-(2*tf); //triangular
    wave[i] = analog(byte(255 * signal));
  }
}

double dt = 0;

void loop() {
  nowMicros = micros();
  dt = double(nowMicros - oldMicros)/1000000;

  t += dt/waveDt;
  analogWrite(3, wave[int(t)]);

  oldMicros = nowMicros;    

  //reinicia al recorrer todos los puntos
  if(t > WAVE_POINTS){ 
    t = 0;
  }
}

//compensa el desajuste de la salida analogica
byte analog(byte value){
  value += (255 - value) >> 4; 
  return value;
}

Con esta versión se puede alcanzar una frecuencia máxima (probada con una señal sinusoidal) de entre 1500-2000Hz usando 2000 puntos para la tabla donde se precalculan los valores.

Esta versión tiene el problema de que ocupa casi todas la memoria SRAM de la placa, aunque se pueden reducir el número de puntos se reduce la calidad de la señal.

Ventajas y desventajas

Frente a cualquier generador de funciones barato tiene la desventaja de que la señal es más ruidosa y es posible que no alcance frecuencias tan elevadas.

Por otro lado en la parte de las ventajas esta el precio y en que la forma de la onda y su frecuencia es completamente programable, pudiendo hacerlo que nosotros queremos.

Puede ver un vídeo explicativo donde profundizo más en el tema en mi canal de Youtube:

Haz click para ver el vídeo en Youtube

Crear un cadáver exquisito usando text-to-image (DALL-E)

Un cadáver exquisito es una técnica creativa en la que varios autores construyen un texto, un dibujo o pintura de forma colaborativa pero sin saber que han hecho los otros autores. El resultado no se ve hasta que la obra esta terminada. A veces se le puede dar un tema, otras no. Vamos a simular estre proceso con DALL-E usando dos fotos, una de un asno y otra de un coche.

Subiremos ambas fotos, las uniremos y borraremos la parte central, dejando solo una pequeña parte que DALL-E pueda usar como pista para continuar ambas imágenes. La preparación tiene este aspecto:

Preparando el lienzo para DALL-E

Hay que tener claro que DALL-E solo recibe información del cuadrado que indica el editor, así que no tenemos que preocuparnos por lo que haya fuera.

En este caso queremos dejar volar la imaginación de DALL-E y como texto le vamos a pasar solo «photo»

Mi resultado favorito fue este:

El cadáver exquisito creado por DALL-E

Si deseas ver este proceso con más detalle (y los resultados descartados) puedes verlo en el siguiente vídeo de mi canal de Youtube:

Haz click para ver el vídeo en mi canal de Youtube

Arduino convertir PWM a analógico por un módico precio.

Antes de leer este post aviso, esta solución es barata pero muy mala, con una salida plagada de ruido. Para aplicaciones que necesiten mejor conversión es mejor adquirir un DAC que lo haga. Sin embargo para «cacharrear» es una buena solución.

Una de las pegas que encuentro a Arduino UNO es no tener una verdadera salida analógica, que permita devolver un voltaje entre 0v y 5v. Lo más parecido que tenemos es PWM que codifica el valor en forma de la anchura (duración) del pulso. Sin embargo es posible convertir esa señal a voltaje. Vamos a ver cómo conseguir una verdadera salida analógica, variación de voltaje, en Arduino UNO conectando un circuito RC (resistencia condensador) muy sencillo a la salida PWM. La parte teórica del asunto es que el circuito RC actúa como un filtro de paso bajo para filtrar la señal PWM y convertirla en un voltaje constante (más o menos). Usando mi simulador de filtros veamos gráficamente lo que ocurre cuando aplicamos a una señal cuadrada un filtro paso bajo con una frecuencia de corte unas 50 veces menor que la señal cuadrada (En el simulador elegimos como señal una onda cuadrada, filtro paso bajo, una frecuencia para el filtro de 0.0075 Hz y quitamos todas las fuentes de ruido el ruido). El resultado es una señal que tras un periodo tiende a ser estabilizarse alrededor de un valor:

Ese valor será proporcional al tamaño en anchura de la parte alta del pulso cuadrado. Por lo que podemos convertir una pulso PWM, que modula su valor como la anchura de la parte alta de la señal, en un voltaje.

El circuito tiene la siguiente forma: (ahora veremos de donde salen esos valores)

Filtro paso bajo RC

Como ya vimos, para calcular la frecuencia de un filtro paso bajo podemos usar la siguiente formula:

f = 1 / (2* Pi * R * C)

Siendo f la frecuencia, C la capacidad del condensador y R la resistencia.

Para nuestro caso f = 1 / (2 * Pi * 4400 * 10^-7) = 361.71 Hz

Pero hemos dicho que la frecuencia de corte ha de ser unas 50 veces menor que la funcionamiento…pero eso es en la teoría. Haciendo pruebas en la vida real va mejor con alrededor de 100 veces. Por lo que PWM debería funcionar a unos 36 kHz. El problema esta que PWM no trabaja a esa frecuencia….a no ser que la cambiemos como vimos en esta entrada Por lo que fijaremos su frecuencia en unos 32 kHz.

El código será el siguiente:

const byte PRESCALER2 = 0b001;
void setup() {
  TCCR2B = (TCCR2B & 0b11111000) | PRESCALER2;
  pinMode(3, OUTPUT);
}

void loop() {
  analogWrite(3, 255); //5v
  delay(5000);
  analogWrite(3, 204); //4v
  delay(5000);
  analogWrite(3, 153); //3v
  delay(5000);
  analogWrite(3, 102); //2v
  delay(5000);
  analogWrite(3, 51); //1v
  delay(5000);
  analogWrite(3, 0); //0v  
  delay(3000);
}

El resultado tiene bastante ruido y los valores resultantes no son muy exactos. Por lo que no se puede usar para casos donde se requiera gran precisión en el valor.

Para ajustar más los valores podemos usar una pequeña corrección para subir el valor del la señal PWM en los valores más bajos (son los que más se descuadran) y que se aproxime al valor de voltaje que debería tener:

void analog(byte value){
  value += (255 - value) >> 4; 
  analogWrite(3, value); 
}

El ejemplo anterior con esta pequeña corrección:

const byte PRESCALER2 = 0b001;
void setup() {
  TCCR2B = (TCCR2B & 0b11111000) | PRESCALER2;
  pinMode(3, OUTPUT);
}

void loop() {
  analog(255); //5v
  delay(5000);
  analog(204); //4v
  delay(5000);
  analog(153); //3v
  delay(5000);
  analog(102); //2v
  delay(5000);
  analog(51); //1v
  delay(5000);
  analog(0); //0v  
  delay(3000);
}

void analog(byte value){
  value += (255 - value) >> 4; 
  analogWrite(3, value); 
}

Puedes ver un vídeo sobre este artículo, con demostración del funcionamiento, en mi canal de Youtube:

Haz click para ver el video en Youtube

Modificar la frecuencia del PWM en Arduino.

Vamos a ver cómo modificar la frecuencia a la que trabaja el PWM de Arduino UNO. Para esto es necesario cambiar la frecuencia de uno de los tres timers que posee. Cada uno de ellos controla la frecuencia de dos pines PWM y cumple una función distinta:

TimerPines PWMRegistroFunciones
Timer 0D5, D6TCCR0Bmicros(), milis() y delay()
Timer 1D9, D10TCCR1BServo
Timer 2D3, D11TCCR2Btone()

En la columna «registro» se indica el registro cuyos tres últimos bits controlan el «prescaler» da cada timer. Sin entrar en detalle de como funciona, este prescaler nos permite ajustar la frecuencia de la señal PWM actuando como un divisor de la misma. En las siguientes tablas podemos ver los valores correspondientes. Recordar que los bits CS*2, CS*1, CS*0 corresponden a los tres bits de menor peso del registro de 8 bits.

Timer 0 – TCCR0B

CS02CS01CS00DivisorFrecuencia
000Parado
001162500 Hz
01087812.50 Hz
01164976.56 Hz
100256244.14 Hz
101102461.04 Hz

Timer 1 – TCCR1B

CS12CS11CS10DivisorFrecuencia
000Parado
001131372,55 Hz
01083921,16 Hz
01164490,20 Hz
100256122,55 Hz
101102430,64 Hz

Timer 2 – TCCR2B

CS22CS21CS20DivisorFrecuencia
000Parado
001131372,55 Hz
01083921,16 Hz
01132980,39 Hz
10064490,20 Hz
101128245,10 Hz
110256122,55 Hz
111102430,64 Hz

Ejemplo

En nuestro ejemplo nos vamos a centrar en los pines D3 y D11. ¿Por qué estos dos? Para evitar problemas innecesarios, ambos usan el timer 2 que va asociado únicamente a la función tone(). Además es el timer que más nos permite jugar con sus frecuencias.

El prescaler lo ajustaremos con la siguiente instrucción:

TCCR2B = (TCCR2B & 0b11111000) | PRESCALER2;

Donde PRESCALER2 indicara la configuración de los bits CS22, CS21 y CS20. Veamos el código del ejemplo:

const byte PRESCALER2 = 0b001;

void setup() {
  TCCR2B = (TCCR2B & 0b11111000) | PRESCALER2;
  pinMode(3, OUTPUT);
}

void loop() {
  analogWrite(3, 128);
  delay(1000);
}

Si quieres profundizar más en este tema y saber más sobre Arduino puedes echar un vistazo a mi libro.

Si quiere ver el ejemplo funcionando puedes mirar este vídeo de mi canal de Youtube:

Haz click para ver el vídeo en mi canal de Youtube

¿Puede DALL-E borrar las marcas de agua de las fotos?

Tras leer el titulo poca introducción necesita esta entrada. Tan solo necesito una foto con marca de agua, para evitarme problemas voy a usar una foto mía (tomada por mi, no de mi). Y añadirle una marca de agua poco sutil.

Foto de un burro con marca de agua

Ahora la subo a DALL-E y con mucho cuidado borro la marca de agua

Ahora pidamos a DALL-E la foto de un burro «photo of a donkey» y elijamos el mejor resultado (según mi punto de vista):

¿Y si le pedimos un caballo? «photo of a horse«

Pero un caballo y un burro están «próximos» y si le pido algo muy distinto como un coche «photo of a car«

Parece ser que el contexto de la foto pesa mucho y se impone a nuestros deseos. ¿Y si no nos complicamos la vida y pedimos simplemente una foto? «photo»

¡Correcto! DALL-E tiene suficiente información en la imagen como para completarla.

Aunque todas las imágenes parezcan iguales hay diferencias entre ellas.

Quizás este sistema es muy descarado, me gustaría quitar la marca de agua y evitarme una denuncia del autor. ¿Y si genero una variación a partir de la imagen con marca de agua?. Como siempre me quedo con el mejor resultado de los cuatro:

Es difícil que el autor original defienda que esta es su misma foto (el burro no esta ni en la misma pose). Ademas DALL-Eme ha movido la marca de agua a un sitio donde molesta menos, no pasa nada por que ya sabemos como eliminarla.

Definitivamente no es que DALL-E permita eliminar marcas de agua, es que permite generar imágenes nuevas a partir de fotos con marca de agua y que ni el propio autor seria capaz de reconocer.