Tutorial de Arduino:
Arduino es una plataforma de hardware libre, basada en una placa con un microcontrolador y un entorno de desarrollo, diseñada para facilitar el uso de la electrónica en proyectos multidisciplinares.
El hardware consiste en una placa con un microcontrolador Atmel AVR y puertos de entrada/salida. Los microcontroladores más usados son el Atmega168, Atmega328, Atmega1280, ATmega8 por su sencillez y bajo coste que permiten el desarrollo de múltiples diseños. Por otro lado el software consiste en un entorno de desarrollo que implementa el lenguaje de programación Processing/Wiring y el cargador de arranque (boot loader) que corre en la placa.
Arduino se puede utilizar para desarrollar objetos interactivos autónomos o puede ser conectado a software del ordenador (por ejemplo: Macromedia Flash, Processing, Max/MSP, Pure Data). Las placas se pueden montar a mano o adquirirse. El entorno de desarrollo integrado libre se puede descargar gratuitamente.
Al ser open-hardware, tanto su diseño como su distribución es libre. Es decir, puede utilizarse libremente para el desarrollo de cualquier tipo de proyecto sin haber adquirido ninguna licencia.
Arduino recibió una Mención Honorífica en la sección Digital Communities de la edición del 2006 del Ars Electronica Prix. El equipo Arduino (Arduino team) es: Massimo Banzi, David Cuartielles, Tom Igoe, Gianluca Martino, y David Mellis.
La placa Arduino Standard tiene 14 pines con entradas/salidas digitales (6 de las cuales pueden ser usadas como salidas PWM), 6 entradas analógicas, un cristal oscilador a 16Mhz, conexión USB, entrada de alimentación, una cabecera ISCP, y un botón de reset.
Los programas hechos con Arduino se dividen en tres partes principales: estructura, valores (variables y constantes), y funciones. El Lenguaje de programación Arduino se basa en C/C++.
http://arduino.cc/es/Reference/HomePage
Existen varios tipos de Placas Arduino por ejemplo:
- Arduino Nano
- Arduino Mini
- Arduino BT
- LilyPad Arduino
- Arduino Fio
- Arduino Pro
- Arduino Pro Mini
- Xbee shield
- Ethernet shield
- Arduino UNO
- Arduino Mega
Al ser un entorno Open hardware y ser una gran comunidad, se han diseñado muchos tipos de placas Arduino distintas aparte de las mencionadas anteriormente. Como todas están basadas en la misma base de programación vamos a centrarnos en la Placa Arduino UNO que es una placa standard y nos permite hacer pruebas de una manera sencilla.
Entradas y Salidas
Cada uno de los 14 pines digitales del Arduino puede ser usado como entrada o salida digital, usando las funciones :
-pinMode()
-digitalWrite()
-digitalRead()
Operan a 5 voltios. Cada pin puede proporcionar o recibir un máximo de 40 mA y tiene una resistencia interna «pull-up» (desconectada por defecto) de 20-50 KOhms. Además, algunos pines tienen funciones especiales:
Pines serie Rx y Tx : Sirven para recibir transmitir datos (TTL), están conectados al chip FTDI, el cual hace conversión de USB a serie- TTL.
Interruptores externos: 2 y 3. Estos pines pueden ser configurados para disparar un interruptor en un valor bajo, un margen creciente o decreciente, o un cambio de valor. Con la función:
-attachInterrupt().
PWM: 3, 5, 6, 9, 10 y 11. Proporcionan salida PWM de 8 bits con la función:
– analogWrite() .
I ²C: 4 (SDA) y 5 (SCL): El software Arduino incluye una librería Wire para simplifcar el uso del bus I ²C. Son buses para comunicarse con otros dispositivos. Soportan comunicación con libreria Wire. Esta libreria se utiliza para comunicarse con elementos TWI (Two Wire Interfaces) que tienen mas velocidad.
SPI: 10 (SS), 11 (MOSI), 12 (MISO), 13 (SCK). Estos pines soportan comunicación SPI, la cual, aunque proporcionada por el hardware subyacente, no está actualmente incluida en el lenguaje Arduino.
LED: 13. Hay un LED empotrado conectado al pin digital 13. Cuando el pin está a valor HIGH, el LED está encendido, cuando el pin está a LOW, está apagado.
Tiene 6 entradas analógicas, cada una de las cuales proporciona 10 bits de resolución (por ejemplo 1024 valores diferentes). Por defecto miden 5 voltios desde tierra, aunque es posible cambiar el valor más alto de su rango usando el pin AREF y algún código de bajo nivel. Para programar y leer la entrada de estos pines analógicos utilizamos la función:
-analogRead()
Además, algunos pines tienen funcionalidad especializada:
AREF: Voltaje de referencia para las entradas analógicas. Usado con analogReference(). Es para entradas analógicas. Nos permite dar a la placa un voltaje de referencia al suministrarle una alimentación externa. si queremos medir el voltaje con un rango máximo de 3,3v, alimentaríamos 3,3v en el pin AREF con un regulador de voltaje IC. entonces el ADC representaría 3,22 mV.
Reset. Pone esta linea a LOW para resetear el microcontrolador. Típicamente usada para añadir un botón de reset a dispositivos que bloquean a la placa principal.
Comunicación
Serial: Arduino tiene un numero de infraestructuras para comunicarse con un ordenador, otro Arduino, u otros microcontroladores. Los microcontroladores mas populares actualmente son los ATmega que utiliza proveen comunicación serie UART TTL (5 V), la cual está disponible en los pines digitales 0 (Rx) y 1 (Tx). Usados para recibir (Rx) y transmitir (Tx) datos TTL en serie. Estos pines estan conectados a los pines correspondientes del chip FTDI USB-a-TTL Serie.
Por ejemplo: Las placas Arduino Diecimila y Duemilanove utilizan chip FTDI FT232RL como conversor USB-Serial. La Arduino UNO utiliza un chip Atmega16U2.
Un FTDI en la placa canaliza esta comunicación serie al USB y los drivers FTDI (incluidos con el software Arduino) proporcionan un puerto de comunicación virtual al software del ordenador. El software Arduino incluye un monitor serie que permite a datos de texto simple ser enviados a y desde la placa Arduino.
Una librería SoftwareSerial permite comunicación serie en cualquiera de los pines digitales de Arduino.
Programación
El Arduino puede ser programado con el software Arduino.
El ATmega del Arduino vienen con un bootloader pregrabado que permite subirle nuevo código sin usar un programador hardware externo. Se comunica usando el protocolo original STK500. También puedes saltar el bootloader y programar el ATmega a través de la cabecera ICSP (In-Circuit Serial Programming).
Alimentación
Puede ser alimentado vía la conexión USB o con una fuente de alimentación externa. El origen de la alimentación se selecciona automáticamente. Las fuentes de alimentación externas (no-USB) pueden ser tanto un transformador o una batería. El transformador se puede conectar usando un conector jack macho de 2.1mm con centro positivo en el conector hembra de la placa. Los cables de la batería puede conectarse a los pines Gnd y Vin en los conectores de alimentación (POWER).
Los pines de alimentación son los siguientes:
-VIN. La entrada de voltaje a la placa Arduino cuando se esta usando una fuente externa de alimentación (en opuesto a los 5 voltios de la conexión USB). Se puede proporcionar voltaje a través de este pin.
-5V. La fuente de voltaje estabilizado usado para alimentar el microcontrolador y otros componentes de la placa. Esta puede provenir de VIN a través de un regulador integrado en la placa, o proporcionada directamente por el USB o otra fuente estabilizada de 5V.
-3V3. Una fuente de voltaje a 3.3 voltios generada en el chip FTDI integrado en la placa. La corriente máxima soportada 50mA.
-GND. Pines de toma de tierra.
Arduino UNO
La Placa Arduino UNO tiene un micro controlador ATMEGA16U2-MU que utiliza como conversor USB en vez del chip FTDI que utilizaban las antiguas placas.
Ademas tiene un procesador micocontrolador Atemega328, siendo mas rápida y optima que las antiguas Arduino Duemilanove y la Diecimila con microcontrolador Atemega168.
IOREF
En el bloque de alimentación se han añadido 2 pines, uno marcado como IOref servirá para que la placa reconozca el tipo de alimentación que requieren los shieds (3.3v ó 5v) y al otro se le dará uso en futuras versiones.
Instalación del Software Arduino
En Mac:
Descargar la ultima version del IDE de Arduino en el siguiente link:
http://arduino.cc/hu/Main/Software
En Windows:
https://www.arduineando.com/instalar-arduino-en-windows/
Drivers FTDI:
http://www.ftdichip.com/Drivers/VCP.htm
Download puredata:
http://puredata.info/downloads/pd-extended
Entorno de Desarrollo para Arduino (Arduino Software)
El entorno de Desarrollo Arduino está constituido por un editor de texto para escribir el código, un área de mensajes, una consola de texto, una barra de herramientas con botones para las funciones comunes, y una serie de menús. Permite la conexión con el hardware de Arduino para cargar los programas y comunicarse con ellos.
Circuitos Basicos
Divisor de voltaje
Un divisor de voltaje o de Tensión consta de al menos dos resistencias en serie con una fuente de voltaje. Para dos resistencias el voltaje se divide de acuerdo con: V1 = V R1 / (R1 + R2) y V2 = V R2 / (R1 + R2)
El voltaje Vs(t) se divide en los voltajes que caen en las resistencias R1 y R2.
Sensor de Luz con un LDR y Arduino
En arduino vamos a construir un Divisor de Voltaje para controlar el pulso de apagado y encendido de un LED con un sensor LDR.
Un sensor de luz se compone de un LDR como parte de un divisor de tensión resistivo.
Ejemplo:
Vout=((Rbotton/(Rbottom+Rtop))*Vin Si el LDR es usado como Rtop, como en el primer circuito, da tensión alta (HIGH) en la salida cuando el LDR está en la luz, y una tensión baja (LOW) en la salida cuando el LDR está en la sombra. La acción del divisor de voltaje es inversa cuando la LDR es usada como Rbottom en lugar de Rtop, como en el segundo circuito. El circuito da tensión Baja (LOW) en la salida cuando la LDR está en la luz, y una tensión alta (HIGH) en la salida cuando la LDR está en la sombra. El circuito divisor de tensión dará una tensión de la salida que cambia con la iluminación, de forma inversamente proporcional a la cantidad de luz que reciba (sensor de oscuridad).
Componentes:
- 1 LDR
- 1 Resistencia de 1kΩ
- Un par de cables
Circuito:
El LDR se conecta a un pin analógico, como otros sensores que miden cantidades físicas de tipo analógico. El Montaje es un Puente Resistivo (o Divisor de Voltaje) que consta de dos resistencias, una a Tierra y otra a 5 V. En este circuito se mide el cambio en la intensidad de corriente al variar la resistencia del sensor, en este caso el LDR que varía según la luz. Ésta va conectada a 5 Voltios (o Vdd) y la otra resistencia conectada a Tierra será de 1 K Ohms. El pin Analógico leerá el cambio de voltaje.
Circuitos:
Para saber que resistencia usar en determinado momento, hay que tener en cuenta la ley de ohm: V = I X R donde V= voltaje, I= corriente y R= resistencia. En éste ejemplo tenemos:
-1 resistencia de 1kohm.
-1 LDR que funciona como una resistencia variable entre 50ohm y 1kohm.
Estas dos resistencias estaban conectadas en serie a un circuito de 5v con una corriente de 2,5mA, medida con el multimetro. En una conexión en serie la resistencia total es la suma de todas las resistencias, es decir: Rt = R1 + R2 entonces: Rt = 1kohm + 1kohm = 2kohm Si aplicamos la ley de ohm: V = I x R, entonces: 5v = 2,5mA x 2kohm
Para comprobar los datos, podemos hacer un Debug por el Serial imprimiendo el valor recibido en el monitor. La velocidad para comunicarnos con el Monitor serial es 9600 Baudios. Con otros dispositivos puede ser otra, por ejemplo el protocolo MIDI sería a 31250 Baudios.
Estructura de Programación
Por lo general un programa en Arduino constará de 3 partes:
- Declaración de variables.
- Función de inicialización. (setup)
- Función de Loop. (Programa)
Declaración de Variables
Las variables son expresiones que almacenan valores, como las capturas o lecturas de los valores de entrada de un pin analógico o sensor. Inicializas o das valor a una variable, haciéndola igual al valor que quieres almacenar.
Declaración de funciones
Las funciones te permiten crear piezas modulares de código, de forma que se puedan realizar ciertas rutinas y retornar al área de código desde donde se realizó la llamada a la función. Cuando Arduino ejecuta esta línea, busca la declaración de dicha función en algún lugar del código, y le pasa el valor de la variable «value» como un argumento (contenido entre los paréntesis) a la función. De forma que una función se define por el tipo de valor que devuelve (enlace a declaración de variables), por su nombre, por la lista de argumentos o parámetros que le son pasados (expresión entre paréntesis) y el bloque de código que se ejecuta cuando se realiza la llamada.
Función de inicialización.
void setup(): La función setup() es llamada justo en el momento en que el programa comienza. Se utiliza para inicializar variables, definir los modos de entrada o salida de los pines, indicar librerías, etc.
Función de Loop. (Programa)
void loop(): Después de crear la sección setup(), que inicializa y asigna los valores iniciales, la sección loop() hace precisamente lo que su nombre indica en inglés(bucle), y se repite continuamente, permitiendo que tu programa mute y responda. Se usa para controlar de forma activa la tarjeta Arduino. El tiempo del bucle, varía según el número de instrucciones que contenga. Y se puede conocer y controlar con las funciones de temporización (millis(), delay(), etc).
Retomando el ejemplo:
En la Variable Val se almacenan los valores del LDR que al ser una «cantidad analógica» serán de o a 1023.
La Programación en Arduino es la siguiente:
//Analog Read_LDR_Print
int LightPin = 3; // selecciona el pin de entrada para el sensor de luz int ledPin = 13; // selecciona el pin para el LED int val = 0; // variable para almacenar el valor capturado desde el sensor void setup() { pinMode(ledPin, OUTPUT); // declara el ledPin en modo salida Serial.begin(9600); // abre el puerto serie a 9600 bps } void loop() { val = analogRead(LightPin); // read the value from the sensor digitalWrite(ledPin, HIGH); // enciende el LED delay(val); // detiene el programa por un tiempo digitalWrite(ledPin, LOW); // apaga el LED delay(val); // detiene el programa por un tiempo int sensorValue = analogRead(A3); // lee el pin analogo 3 Serial.println(sensorValue); // imprime valores entre 0 y 1023 que recibe del pin analogo 3 }
Funciones de Programación:
Pines digitales:
pinMode(pin, mode): Configura el pin especificado para que se comporte como una entrada (input) o una salida (output).
digitalWrite(pin, value): Asigna el valor de salida HIGH o LOW al pin especificado.
digitalRead(pin): Lee o captura el valor de entrada del pin especificado, dará valores HIGH o LOW. Devuelve un valor de tipo entero HIGH(1) o LOW (0).
Pines analógicos:
analogRead: Lee o captura el valor de entrada del especificado pin analógico, la tarjeta Arduino realiza una conversión analógica a digital de 10 bits. Esto quiere decir que mapeará los valores de voltaje de entrada, entre 0 y 5 voltios, a valores enteros comprendidos entre 0 y 1023.
La placa Arduino posee 6 canales conectados a un conversor analógico digital de 10 bits. Esto proporciona una resolución en la lectura de: 5 voltios / 1024 unidades, es decir, 0.0049 voltios (4.9 mV)por unidad. El rango de entrada puede ser cambiado usando la función analogReference().
Los pines analógicos son solo de entrada por lo tanto no necesitan ser declarados como modo INPUT (entrada) o OUTPUT (salida).
Información Serial:
Arduino se comunica por el puerto serial vía USB con nuestro ordenador a través del chip FTDI o ATMEGA16U2-MU. La comunicación serial se puede utilizar para hacer un debugging (saber lo que está pasando en nuestro programa) o para comunicarnos con otros programas.
Aquí lo utilizaremos para leer los valores analógicos. Lo primero que hay que hacer es abrir el puerto y definir la velocidad con la que se nos vamos a comunicar con en Arduino:
void setup() {
Serial.begin(9600); // Velocidad de la transmisión y apertura del puerto
}
La velocidad para comunicarnos con el Monitor serial es 9600 Baudios. Con otros dispositivos puede ser otra, por ejemplo el protocolo MIDI sería a 31250 Baudios.
Intrucciones Seriales:
- Serial.begin(speed) //define el baudrate de la comunicación serie
- Serial.print(data) // envía data como dato serial
- Serial.println(data) // envía data como dato serial con un cambio de linea y retorno de carro
Ejemplo:Hello World!
Sketch Hello world:
//Compila el programa y súbelo a Arduino. //Haz click sobre el icono de monitor serial void setup() { Serial.begin(9600); // Velocidad de la transmisión y apertura del puerto } void loop() { Serial.println("Hello World!"); // imprime Hello world en el Monitor serial }
Sketch Segundero:
//Compila el programa y súbelo a Arduino. //Haz click sobre el icono de monitor serial
int cont= 0;
void setup() {
Serial.begin(9600); // Velocidad de la transmisión y apertura del puerto
}
void loop() {
Serial.println(cont); // imprime Hello world en el Monitor serial delay(1000); cont = cont +1; }
Multiled y potenciometro con Arduino
Una barra de 10 leds conectados en Serie. Creamos un divisor de voltaje y controlamos el encendido y apagado de los leds con una resistencia variable de 10k.
Circuito:
La programación en el sketch de Arduino es:
//Mi multiled potenciometro:
// estas son costantes: const int analogPin = A0; // el pin al que el potenciometro esta conectado const int ledCount = 10; // el numero de leds en la barra int ledPins[] = { 2, 3, 4, 5, 6, 7,8,9,10,11 }; //describe el orden de los numeros de pines donde conectamos los leds void setup() { // hace un conteo en bucle sobre todos los pines enviandolos todos a salida for (int thisLed = 0; thisLed < ledCount; thisLed++) { pinMode(ledPins[thisLed], OUTPUT); } } void loop() { // lee el potenciometro: int sensorReading = analogRead(analogPin); // mapea el resultado de rango desde 0 hasta el numero de leds: int ledLevel = map(sensorReading, 0, 1023, 0, ledCount); // Hace un conteo en bucle sobre todos los leds: for (int thisLed = 0; thisLed < ledCount; thisLed++) { // si la cantidad de elementos es menor que el nivel, // encienda el pin para estos elementos: if (thisLed < ledLevel) { digitalWrite(ledPins[thisLed], HIGH); } // apague todos los pines mayores al ledlevel: else { digitalWrite(ledPins[thisLed], LOW); } } }
Funciones de Programación:
Control del Flujo de Programa, CondicionalesIf
Cuando se quiere que el microcontrolador haga algo dependiendo de alguna condición se utilizara la instrucción If. La instrucción If verifica la condición del enunciado escrito entre paréntesis y si esta condición es verdadera hará las acciones que estén entre las claves.
If (expresión)
{ Hacer esto ; y esto también ; }
Donde la expresión puede ser evaluada por cualquiera de estas opciones:
a == b (a es igual a b)
a != b (a no es igual a b)
a < b (a es menor que b)
a > b (a es mayor que b)
a <= b (a es menor que o igual a b)
a >= b (a es mayor que o igual a b)
If-Else-If.
if (A < 500)
{ // Hacer esto }
else if (B >= 1000)
{ // Haz lo otro }
else { // Haz lo que te parezca }
Map: Re-mapea un número desde un rango hacia otro. Ésto significa que, un valor (value) con respecto al rango fromLow-fromHight será mapeado al rango toLow-toHigh.
Estructuras de repetición FOR
Las instrucción FOR ejecuta en un loop varias instrucciones de código, y lo hacen un determinado número de veces, el número de veces que se realizan depende de el número de ciclos que determinemos en el encabezado de la instrucción. En el siguiente ejemplo las instrucciones instrucc1, 2, 3,4,5 se ejecutarán 10 veces, después de la décima seguirá el flujo del programa.
FOR (i=1; 1 <= 10; i ++)
{ instrucc 1 ; instrucc 2 ; instrucc 3 ; instrucc 4 ; instrucc 5 ; }
Declaración FOR: La declaración for es usada para repetir un bloque encerrado entre llaves. El bucle for tiene tres partes o argumentos en su inicialización: for (initialization; condition; increment) {
//función(es);
}
Realiza el control sobre una secuencia de repetición. Se compone de tres partes: init (inicializado de la variable local), test (condición) , y update (actualización del valor la variable local), cada parte debe ser separada por punto y coma «;». El bucle continua hasta que la condición establecida se cumple (es verdad) o no (falsa). Es útil cuando se usa en combinación con vectores y operar sobre grupo de datos/pines.
Otro ejemplo:
El coche fantastico
/* Coche fantático 1*/ int pinArray[] = {2, 3, 4, 5, 6, 7,8,9}; int count = 0; int timer = 70; void setup(){ for (count=0;count<8;count++) { pinMode(pinArray[count], OUTPUT); } } void loop() { for (count=0;count<8;count++) { digitalWrite(pinArray[count], HIGH); delay(timer); digitalWrite(pinArray[count], LOW); delay(timer); } for (count=7;count>=0;count--) { digitalWrite(pinArray[count], HIGH); delay(timer); digitalWrite(pinArray[count], LOW); delay(timer); } }
Sistema Binario
El sistema binario, en matemáticas e informática, es un sistema de numeración en el que los números se representan utilizando solamente las cifrascero y uno (0 y 1). Es el que se utiliza en los ordenadores, debido a que trabajan internamente con dos niveles de voltaje, por lo cual su sistema de numeración natural es el sistema binario (encendido 1, apagado 0).
http://platea.pntic.mec.es/~lgonzale/tic/binarios/numeracion.html
Byte: es una palabra inglesa, que es equivalente a octeto (es decir a ocho bits), para fines correctos, un byte debe ser considerado como una secuencia de bits contiguos, cuyo tamaño depende del código de información o código de caracteres en que sea definido.
PWM
La modulación por ancho de pulsos (también conocida como PWM, siglas en inglés de pulse-width modulation) de una señal o fuente de energía es una técnica en la que se modifica el ciclo de trabajo de una señal periódica.
PWM tiene diferentes usos:
- Atenuación de un LED.
- Disponer de una salida analógica; si la salida digital está filtrada, esto proveerá de un voltaje entre el 0% y el 100%.
- Generar señales de audio.
- Proveer de un control de velocidad variable para motores.
- Generar una señal modulada, por ejemplo para utilizar un LED infrarojo para control remoto.
- Control paso a paso de Motores servo con una resolucion de 8 bits.
Si prendemos y apagamos la señal de alimentación de la carga (led o motor) lo suficientemente rápido como para que el parpadeo no se note, podríamos “simular” la variación de luminosidad de un led o el cambio en la velocidad del motor:
Esto funciona siempre y cuando no hagamos este “switcheo” más lento de 30 veces por segundo. A partir de ahí el «blink» del led se empezará a notar y el ojo humano captará ese parpadeo. En el caso de un motor, éste se moverá en una forma pulsante. Lo que se hace con PWM es variar dinámicamente el “duty cycle” de manera que el tiempo de alta disminuya o aumente y en proporción inversa, el de baja aumente o disminuya dependiendo de si queremos una led más atenuado o más brillante, o un motor más lento o más rápido, respectivamente. Esto lo podemos ver en un ejemplo práctico con Arduino, un led y una resistencia de 220 ohms.
Variar la intensidad de un LED usando un salida PWM
El circuito es el siguiente:
El LED está conectado al pin digital del Arduino con una resistencia de 220 Ohms. La pata corta siempre a tierra y la positiva, más larga del lado de la resistencia. La resistencia se coloca con el fin de proteger el led. Enviamos valores entre 0 y 255. Arduino lee estos Bytes y los representa como luminocidad del led a través de pulsos PWM. Los valores irán gradualmente de 0 a 255, ya que PWM es una «simulación» de analógico, en la que 0 = 0 Voltios y 255 = 5Voltios. Lo que hace el PWM es ir de estado alto (5 V) a estado bajo (0 V.) en una determinada frecuencia, esa frecuencia determina una media que el LED interpreta como analógica. Para programar en arduino el control de dimmerizado del Led es necesario utilizar un pin que genére PWM, en este ejemplo utilizamos el pin 9.
Sketck Arduino:
// Dimmer auto LED int contador = 0; // variable int pin = 9; // pin 9 que puede generar PWM void setup() { // No tenemos que declarar que es una salida analógica } void loop() { for(contador = 0 ; contador <= 255; contador ++) // ciclo para ir subiendo el voltaje desde 0 a 5 voltios { analogWrite(pin, contador); // PWM delay(15); } delay(600); }
Funciones de Programación:
Analogwrite()
Escribe un valor analógico (PWM) en un pin. Puede ser usado para controlar la luminosidad de un LED o la velocidad de un motor.
Después de llamar a la función analogWrite(), el pin generará una onda cuadrada estable con el ciclo de trabajo especificado hasta que se vuelva a llamar a la función analogWrite() (o una llamada a las funciones digitalRead() o digitalWrite() en el mismo pin). La frecuencia de la señal PWM sera de aproximadamente 490 Hz.
// Dimmer auto LED Up and Down int contador = 0; // variable int pin = 9; // pin 9 que puede generar PWM void setup() { // No tenemos que declarar que es una salida analógica } void loop() { for(contador = 0 ; contador <= 255; contador ++) // ciclo para ir subiendo el voltaje desde 0 a 5 voltios { analogWrite(pin, contador); // PWM delay(15); } for(contador = 255 ; contador >= 0; contador --) // ciclo para ir bajando el voltaje desde 5 a 0 voltios { analogWrite(pin, contador); // PWM delay(15); } delay(600); }
Led dimerizado con Arduino y Puredata
También Podemos controlar el PWM con un programa que se comunique con arduino a través del puerto serial del ordenador. El puerto serial se comunica con el sistema binario en palabras de 8 bits (bytes).
Sabiendo esto, para enviar información a través del puerto serial, debemos convertir nuestra información ASCII a información binaria, y para eso utilizamos la función byte. Un byte representa valores desde 0 hasta 255. Por eso, los datos que enviamos desde un programa externo a través del serial en este caso Puredata, son valores comprendidos en este rango.
En este caso Progamamos en Arduino:
sketch de Arduino:
//Dimmer LED y Puredata const int ledPin = 9; // El pin al que el LED está conectado void setup() { // Inicializamos puerto serie a 9600 bps: Serial.begin(9600); // inicializamos el ledPin como salida: pinMode(ledPin, OUTPUT); } void loop() { byte brightness; // chequeamos que los datos hayan sido enviados por el ordenador: if (Serial.available()) { // Lee el byte mas reciente (debe ser entre 0 y 255): brightness = Serial.read(); // Determina la luminocidad del LED: analogWrite(ledPin, brightness); } }
Funciones de programación
byte: Un byte almacena un número sin signo de 8-bit, desde 0 hasta 255.
Ejemplo:
byte b = B10010; // «B» es el formateador binario (B10010 = 18 decimal)
Serial:
read(): Lee los datos entrantes del puerto serie.
available(): Devuelve el número de bytes (caracteres) disponibles para ser leidos por el puerto serie. Se refiere a datos ya recibidos y disponibles en el buffer de recepción del puerto (que tiene una capacidad de 128 bytes).
Programamos en el Patch de Puredata:
Para obtener este patch de PD debes copiar el siguiente codigo (cmd-c):
Código text de PD:
#N canvas 702 82 390 360 10; #X msg 150 152 close; #X msg 52 152 open \$1; #X msg 20 111 4; #X msg 55 111 3; #X obj 52 248 comport 3 9600; #X obj 291 55 vsl 15 128 0 225 0 0 empty empty empty 0 -9 0 10 -262144 -1 -1 0 1; #X floatatom 292 213 5 0 0 0 - - -; #X text 0 16 El patch envia un numero binario entre 0 255 por el puerto serial al arduino; #X connect 0 0 4 0; #X connect 1 0 4 0; #X connect 2 0 1 0; #X connect 3 0 1 0; #X connect 5 0 6 0; #X connect 6 0 4 0;
Lo pegas (cmd-v) en un archivo de texto del programa «Text Edit» y en la barra de herramientas buscas:
Formato/Convertir en texto normal.
Despues de convertirlo a texto normal le das un nombre y lo guardas en formato .pd.
Ahora puedes abrir tu Patch con Puredata!!!
Nota: En el ejemplo del video tengo un fader con valores de 0 a 1, estos valores son Mapeados a valores de 0 a 255 cuando los enviamos a Arduino por el serial. El Patch que esta en el código y en la foto, tiene los valores reales que se envían: 0 a 255.
Materiales
LED:
LED significa «Light Emiting Diode». Son diodos que se iluminan cuando pasa la electricidad.
Tienen dos «patas», una positiva más larga (ánodo) y otra negativa (cátodo).Se conectan a Arduino por medio de una resistencia para proteger el LED y el microcontrolador. Las resistencia que utilizaremos son de 220 Ohms.
Resistencias
La resistencia eléctrica de un objeto es una medida de su oposición al paso de corriente. Una resistencia ideal es un elemento pasivo que disipa energía en forma de calor según la ley de Joule.
También establece una relación de proporcionalidad entre la intensidad de corriente que la atraviesa y la tensión medible entre sus extremos, relación conocida como ley de Ohm. De acuerdo con la ley de Ohm la resistencia de un material puede definirse como la razón entre la caída de tensión y la corriente en dicha resistencia, así: R = V / I Donde: R = Resistencia V = Voltaje I= Corriente
Como leer el valor de una resistencia
En una resistencia tenemos generalmente 4 líneas de colores, aunque podemos encontrar algunas que contenga 5 líneas (4 de colores y 1 que indica tolerancia). Vamos a tomar como ejemplo la más general, las de 4 líneas. Leemos las primeras 3 y dejamos aparte la tolerancia que es plateada (±10%) o dorada (±5%).
- La primera línea representa el dígito de las decenas.
- La segunda línea representa el dígito de las unidades.
- La tercera línea representa la potencia de 10 por la cual se multiplica el número.
Por ejemplo:
- Registramos el valor de la primera línea (verde): 5
- Registramos el valor de la segunda línea (amarillo): 4
- Registramos el valor de la tercera línea (rojo): 102 o 100
- Unimos los valores de las primeras dos líneas y multiplicamos por el valor de la tercera
54 X 102 = 5400Ω o 5,4 kΩ y este es el valor de la resistencia expresada en Ohmios
Aquí puedes encontrar una aplicación que te calcula el valor de la resistencia:
http://www.pagaelpato.com/tecno/resistencias/resistencia.htm
Sensor de Luz o LDR (Light Dependent Resistor):
Un LDR es una resistencia variable, que varia su valor dependiendo de la cantidad de luz que incide sobre su superficie. Cuanta mas intensidad de luz incide en la superficie de la LDR menor será su resistencia y cuanto menos luz incide mayor será la resistencia. Suelen ser utilizados como sensores de luz ambiental o como una fotocélula que activa un determinado proceso en ausencia o presencia de luz.
Los valores que puede tomar una LDR en total oscuridad y a plena luz puede variar un poco de un modelo a otro, en general oscilan entre unos 50 a 1000 ohmios (1K) cuando están iluminadas (por ejemplo, con luz solar) y valores comprendidos entre 50K (50,000 Ohms) y varios megohmios (millones de ohms) cuando está a oscuras.
Placas de Prueba, Protoboard ó BreadBoard
El protoboard o breadbord: Es una especie de tablero con orificios, en la cual se pueden insertar componentes electrónicos y cables para armar circuitos. Los orificios estan interconectados entre si por columnas como lo vemos en la foto. En algunas placas tambien hay filas interconectando los orificios, estas se encuentran en los bordes de la placa, como lo muestra la segunda foto.
Corriente AC y DC
La coriente AC quiere decir corriente alterna mientras que la DC es corriente directa o continua. La diferencia es que la alterna hace ciclos es decir es positiva y negativa por ciclos de sesenta picos por segundo es decir graficamente sube y baja (es positiva y negativa) 60 veces en un segundo o en pocas palabras su polaridad cambia de + a – .
Por Eso un tomacorriente no tiene indicador de positivo o negativo. En la corriente AC los estados de fase y neutro cambian de posicion con cada ciclo. En cambio, la continua o DC: como su nombre lo dice es continua o suministra una misma polaridad siempre, como las baterias que tienen polos positivo y negativo. Cuando trabajamos con información digital, siempre trabajamos con corriente directa.
Motor DC, velocidad controlada con PWM
Con este mismo patch de Puredata y este mismo sketch de Arduino podemos controlar la velocidad de un motor DC. Con la capacidad de Arduino, vemos que se pueden controlar dispositivos de bajo consumo, como los LEDs. Sin embargo, cuando tratamos de mayores consumos, sobre todo, con cargas con inducciones, como un motor de corriente continua, relés, etc; por precaución, se necesita utilizar al menos, un transistor externo.
La velocidad del motor DC depende del voltaje. Los valores de Arduino varian entre 0 y 255, estos valores determinan el PWM que enviamos al motor. El motor lo interpreta como un voltaje que varia entre 0 y 5v. Para cambiar la dirección solo hay que cambiar su polaridad. Para este circuito necesitamos más componentes: un trasistor de potencia, una resistencia de 1 kohm y un Diodo.
Transistor
El transistor es un dispositivo electrónico semiconductor que cumple funciones de amplificador, oscilador, conmutador o rectificador. Tiene 3 patas: base, colector y emisor. Será un interruptor electrónico, (no mecánicos) que abrirá y cerrará el «interruptor» de nuestro circuito.
Con un transistor se controla una mayor cantidad de corriente con la pequeña aportación de la señal procedente de Arduino. Cuando a un transistor, se le conecta la carga al colector y el emisor a tierra, de modo que aplicamos a la base, la salida de Arduino del pin elegido, podemos mover el motor que girará en proporción a la señal que le entrega el pin.
Hay diferentes tipos de transistores, pero en este curso sólo estudiaremos los bipolares. Dentro de ellos, según como sea la conexión de sus componentes, hay dos tipos, los NPN y los PNP. Se simbolizan de la siguiente manera:
El de la izquierda es un transistor NPN y el de la derecha un transistor PNP. En el NPN la flecha que indica el sentido de la corriente sale hacia fuera (la corriente irá de colector a emisor) mientras que en el PNP la flecha entra (la corriente irá de emisor a colector).
Transistor BC547B
Transistor PNP 2N3906
Diodo
El diodo es un semiconductor. La función de los diodos es dejar pasar la corriente en un solo sentido. Tiene polaridad, la banda oscura indica el cátodo(-). También se utliza una resistencia de 1 kohm para protegerlo.
Circuito:
Con Transistor BD 135, BD 137 o BD 139.
Para este circuito necesitaremos usar un Transistor NPN.
Necesitamos una fuente de alimentación externa, en este caso una pila de 9 Voltios. El diodo nos sirve de protección para el Arduino, el cual se alimenta con 5 Voltios, dejando que la corriente de 9 Voltios sólo circule para alimentar al motor. En cambio la Tierra si debe estar conectada a la Tierra del Arduino, pues si no el circuito se desestabiliza. Esto es una regla en todos los circuitos que hagamos con DC: las tierras o masas deben estar siempre conectadas.
También podemos programar el Arduino para que haga una variación predeterminada de velocidad al motor sin necesidad de utilizar Puredata. La programación sería la siguiente:
sketch de Arduino:
// Motor DC PWM int pulso = 0; // variable donde almacenamos el valor del pulso int pinMotor = 10; // Pin 10 que puede generar PWM void setup() { // No tenemos que declarar que es una salida analógica } void loop() { for(pulso = 0 ; pulso <= 255; pulso ++) // ciclo para ir subiendo el voltaje desde 0 a 5 voltios { analogWrite(pinMotor, pulso); // enviamos el pulso al motor via PWM delay(15); } delay(600); }
Motor DC control de encendido y apagado con arduino
Esta es una manera mas sencilla de programar el encendido y apagado de nuestro motor DC, utilizando delays. Arrancamos el motor, esperamos un tiempo, después apagamos el motor.
sketch de Arduino:
int motorPin = 9; int onTime = 2500; int offTime = 1000; void setup() { pinMode(motorPin, OUTPUT); } void loop() { digitalWrite(motorPin, HIGH); // Enciende delay (onTime); // Espera tiempo onTime digitalWrite(motorPin, LOW); // Apaga delay (offTime); // Espera tiempo offTime }
Motor DC Control de velocidad con Arduino
Al igual que antes cambiamos la iluminación del led, ahora podemos controlar la velocidad del motor con la función analogWrite(pin, valor).
sketch de Arduino:
int motorPin = 9; int onSpeed = 200; int onTime = 2500; int offSpeed = 50; //Un numero entre 0 (Para) y 255 (maxima velocidad) int offTime = 1000; //el numero en milisegundos para que el motor se apague void setup() { pinMode(motorPin, OUTPUT); } void loop() { analogWrite(motorPin, onSpeed); // enciende el motor delay(onTime); // espera los milisegundos que especificamos onTime analogWrite(motorPin, offSpeed); // apaga el motor delay(offTime); // espera el tiempo offTime para apagar el motor }
Motor DC Control de Aceleración con Arduino
Aceleramos y desaceleramos el motor: ahora usamos un bucle para acelerar y frenar el motor, usando del mismo modo la función analogWrite(), que en el caso anterior.
sketch de Arduino:
int motorPin = 9; void setup() { pinMode(motorPin, OUTPUT); } void loop() { int delayTime = 50; for(int i = 0; i < 256; i++){ //aceleramos analogWrite(motorPin, i); delay(delayTime); } for(int i = 255; i >= 0; i--){ //frenamos analogWrite(motorPin, i); delay(delayTime); } }
Aquí vemos que las variables que no necesitamos inicializar las podemos también declarar en nuestro programa en la función loop.
Motor DC Controlado con un potenciometro
Usamos ahora un potenciómetro para variar la velocidad del motor. El potenciómetro se conecta de la siguiente forma:
Extremos: (VCC 5V) y (GND) respectivamente.
Señal: al pin 0 de las entradas análogicas de Arduino.
sketch de Arduino:
int motorPin = 9; int potPin=0; int potValue; void setup() { pinMode(motorPin, OUTPUT); } void loop() { potValue = analogRead(potPin) / 4; analogWrite(motorPin, potValue); }
Arduino dispone de 6 entradas analógicas, que tienen un voltaje de 0 a 5voltios que convertidas a señales digitales tendríamos de 0 a 1023, esto es 10 bits de resolución. Dividimos por 4 analogRead() porque esta función devuelve un valor comprendido entre 0 y 1023 (10 bits) y la función analogWrite () toma valores comprendidos entre 0 y 255 (8 bits).
Motor Servo
Un motor servo es un dispositivo actuador que tiene la capacidad de ubicarse en cualquier posición dentro de su rango de operación, y de mantenerse estable en dicha posición.
Consta de un motor de corriente continua, con una caja reductora y un circuito de control, para su posicionamiento.
Los servos estandar llevan 3 cables : tensión, tierra y control. Se mueven en función de pulsos que le enviamos a traves del cable de control y estos pulsos que le enviamos son los que establecen la posición del servo. El servo espera pulsos cada 20 milisegundos para tener una idea correcta de la posición que ha de tener. En general los servos giran unos 180 grados, aunque hay algunos que giran más. Tienen una amplitud de pulsos de entre 0.5 y 2.5 milisegundos para establecer la posición. Los servomotores hacen uso de la modulación por ancho de pulsos (PWM) para controlar la dirección o posición de los motores de corriente continua. La mayoría trabaja en la frecuencia de 50 hercios, así las señales PWM tendrán un periodo de veinte milisegundos. La electrónica dentro del servomotor responderá al ancho de la señal modulada. Si los circuitos dentro del servomotor reciben una señal de entre 0,5 a 1,4 milisegundos, este se moverá en sentido horario; entre 1,6 a 2 milisegundos moverá el servomotor en sentido antihorario; 1,5 milisegundos representa un estado neutro para los servomotores estándares.
PPM
PPM o -Pulse Position Modulation es un código implementado en la lbrería de servos. Es como un PWM pero con un duty cycle muy bajo, para servos utiliza 50Hz en vez de 490Hz. Esto permite utilizar varios servos a la vez. Funciona de la siguiente manera: El tiempo de duración del pulso es: 2ms, en el cual se determina la posición del servo. Esta secuencia es repetida 50 veces por segundo. El punto limite de la izquierda 1 ms, punto medio 1.5 ms y punto limite de la derecha 2 ms. Servo #1: Far Left Command: Time ON = 1 + 0.0 ms = 1.0 ms Servo #2: Neutral Command: Time ON = 1 + 0.5 ms = 1.5 ms Servo #3: Far Right Command: Time ON = 1 + 1.0 ms= 2.0 ms.
En los siguientes tutoriales podemos encontrar una alternativa sencilla de conectar el motor servo a la placa Arduino:
http://arduino.cc/es/Tutorial/Sweep
http://blog.bricogeek.com/noticias/tutoriales/tutorial-control-de-servomotores/
En estos tutoriales vemos que el motor es conectado directamente a los 5 v y GND de la placa arduino. Esto funciona muy bien con mini-motores o en el caso en que hagamos movimientos ocacionales y sutiles. En el caso en que queramos hacer movimientos rapidos en bucle, es preferible conectar el motor a una fuente eléctrica DC externa de 5v ya que el motor podria pedir mas corriente de la que la placa Arduino le pueda dar. Los pines de salida de Arduino pueden entregar hasta 40 mA, sin embargo, los motores pueden tener peaks de alrededor de 700mA.
Circuito:
Conexión Motor servo: Cable negro ó marron al (-) de la fuente eléctrica externa y a GND de Arduino. Cable Rojo al (+) de la fuente eléctrica externa. Cable Blanco ó Amarillo al Pin PWM de Arduino.
Controlando un Motor Servo
En este ejemplo el motor Servo gira 180 grados gracias al Sketch de Arduino. El motor es conectado a una fuente de poder externa y la señal de control es enviada por el pin digital 9.
sketch de Arduino:
#include <Servo.h> Servo myservo; // creamos un objeto servo para controlar nuestro servo int pos = 0; // variable para almacenar la posición del servo void setup() { myservo.attach(9); // atribuye al pin 9 el objeto servo } void loop() { for(pos = 0; pos < 180; pos += 1) // avanza de 0 a 180 grados { // en saltos de un grado myservo.write(pos); // mueve a la posición de la variable 'pos' delay(20); // espera 20ms } for(pos = 180; pos>=1; pos-=1) // va de 180 a 0 grados { myservo.write(pos); delay(20); } }
Funciones de Programación
Existe una libreria para porgramar motores servo que tenemos que llamar al iniciar nuestro sketch. Con esta libreria podemos controlar multiples servo. 1 servo por cada pin digital.
#define: Un define es un componente útil en el lenguaje de progración C, que permite dar un nombre a un valor antes de que el programa sea compilado. Puedes definir números en Arduino, para que no ocupen espacio de memoria de programa en el chip. La definición en Arduino tiene la misma sintaxis que la definición en C: #define constantName value #include.
Para utilizarlas, sólo es necesario añadir la correspondiente declaración #include al comienzo de tu código o proyecto.
Metodos Standard de la libreria Servo
attach(int): Convierte el pin en un servo driver. Este remplaza al pinMode.
detach(): Libera un pin del servo driving.
write(int): Da el angulo del servo en grados, 0 a 180.
read(): Devuelve el valor al ultimo write().
attached(): Devuelve 1 si el servo está conectado.
Controlando un Motor Servo con una Camara a través de Arduino y Puredata
Con el objeto blob de Puredata podemos determinar la posición de un objeto capturado con una cámara. Su posición esta determinada por el porcentaje de luminosidad que capte la cámara en una región de pixeles determinada. La información captada por Puredata la enviamos a Arduino y de esta forma podemos controlar la posición de un motor Servo con la posición de nuestra mano en el espacio.
Sketch de Arduino:
#include <Servo.h> // Libreria muy buena de servos Servo myservo; // variable servo byte puredata=0; // almacena el valor binario enviado desde puredata void setup(){ Serial.begin(9600); myservo.attach(9); // define el pin al que esta conectado el servo } void loop () { if (Serial.available() > 0) { // si hay algun dato entrado por el serial procede puredata=Serial.read(); // lee los datos binarios del puerto serie myservo.write(puredata); // Da el angulo del servo en grados, 0 a 180. Serial.println(puredata); // Imprimimos los datos en el monitor serial } }
Patch Puredata:
Códigio text de Pd:
#N canvas 208 22 925 661 10; #X floatatom 459 312 5 0 0 0 - - -; #X obj 545 -13 gemwin; #X msg 545 -99 create; #X msg 597 -31 destroy; #X msg 577 -74 1; #X msg 579 -53 0; #X obj 438 276 pix_blob 4; #X obj 545 26 gemhead; #X obj 538 213 pix_texture; #X obj 537 351 square 4; #X obj 430 427 gemhead; #X obj 430 453 translateXYZ; #X floatatom 685 110 3 0 100 2 threshold - -; #X obj 545 148 pix_movement 0.5; #X obj 679 133 / 100; #X obj 545 171 alpha; #X obj 517 142 tgl 15 1 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1; #X floatatom 553 77 5 0 1 1 pass - -; #X obj 545 101 pix_threshold; #X msg 631 76 0.6 0.1 0.8; #X obj 537 253 separator; #X floatatom 570 271 5 0 0 0 - - -; #X floatatom 603 294 5 0 0 0 - - -; #X floatatom 641 305 5 0 0 0 - - -; #X obj 537 324 rotateXYZ 0 0 0; #X obj 454 387 * -1; #X msg 630 254 180; #X obj 430 480 sphere 0.2; #X obj 460 361 - 4; #X obj 460 338 * 8; #X obj 740 -18 t b b b; #X msg 717 64 80; #X msg 651 17 dialog; #X obj 545 52 pix_video; #X obj 738 -70 loadbang; #X obj 132 51 line 0 1; #X obj 133 19 pack f 200; #X obj 61 270 comport 1 9600; #X msg 63 190 open 3; #X obj 131 97 vsl 15 128 0 180 0 0 empty empty empty 0 -9 0 10 -262144 -1 -1 9935 1; #X floatatom 139 240 5 0 0 0 - - -; #X obj 130 -15 * 180; #X text 20 -75 Enviamos el Angulo de giro de motor Servo. Valores entre 0 y 180 grados.; #X connect 0 0 29 0; #X connect 0 0 41 0; #X connect 2 0 1 0; #X connect 3 0 1 0; #X connect 4 0 1 0; #X connect 5 0 1 0; #X connect 6 1 0 0; #X connect 7 0 33 0; #X connect 8 0 6 0; #X connect 8 0 20 0; #X connect 10 0 11 0; #X connect 11 0 27 0; #X connect 12 0 14 0; #X connect 13 0 15 0; #X connect 14 0 13 1; #X connect 15 0 8 0; #X connect 16 0 15 0; #X connect 17 0 18 1; #X connect 18 0 13 0; #X connect 19 0 18 2; #X connect 20 0 24 0; #X connect 21 0 24 1; #X connect 22 0 24 2; #X connect 23 0 24 3; #X connect 24 0 9 0; #X connect 25 0 11 1; #X connect 26 0 22 0; #X connect 28 0 25 0; #X connect 29 0 28 0; #X connect 30 0 19 0; #X connect 30 1 31 0; #X connect 30 2 26 0; #X connect 31 0 12 0; #X connect 32 0 33 0; #X connect 33 0 18 0; #X connect 34 0 30 0; #X connect 35 0 39 0; #X connect 36 0 35 0; #X connect 38 0 37 0; #X connect 39 0 37 0; #X connect 39 0 40 0; #X connect 41 0 36 0;
Ejemplo Standard Firmata y pduino con fuente de alimentación externa
Aquí hay un ejemplo con el «Standard-firmata» de Arduino y el «arduino-test» de Pduino. Aquí podemos apreciar como se conecta el motor a una fuente de poder externa.
Hay que recordar que la tierra (GND) debe ser la misma para la fuente de poder externa, el motor y la placa Arduino, por lo tanto deben estar interconectadas.
Motor Servo controlado con un potenciometro
En este ejercicio controlamos la posición del motor servo con una entrada analoga de un potenciometro de 10 kohm. Conservamos las conexiones del motor servo a la fuente de poder externa de 5v del ejemplo anterior. Añadimos un Potenciometro de 10 kohm al circuito.
Potenciometro
Un Potenciometro es una resistenia variable, la cual se puede ajustar manualmente.
Para nuestro circuito conectamos el potenciometro al pin de 5v de Arduino, a GND y la señal al Pin analogo 0. El Motor lo alimentamos con una fuente de poder externa de 5v, la tierra la compartimos con Arduino y el cable de señal lo conectamos al pin PWM digital 9.
Circuito:
sketch de Arduino:
#include <Servo.h> Servo myservo; // crea un objeto servo de control int potpin = 0; // el pin de entrada analoga del potenciometro int val; // variable para leer el valor del pin analogo void setup() { myservo.attach(9); // conecta el servo del pin 9 al objeto servo } void loop() { val = analogRead(potpin); // lee el valor del potenciometro (valores entre 0 y 1023) val = map(val, 0, 1023, 0, 179); // lo escala para usarlo con servo (valores entre 0 y 180) myservo.write(val); // le da la posicion al sevo deacuerdo al valor de la escala delay(20); // espera a que servo llegue al valor de escala }
Desconectando Arduino del Ordenador y Alimentandolo con una Fuente de poder externa
Ya teniendo guardada la información en el microcontrolador de Arduino, podemos desconectar la placa Arduino del USB del ordenador y alimentarla con nuestra fuente de poder externa de 5v. Entonces conectamos el (+) a VIN y a Nuestro motor Servo. De esta manera, alimentamos la placa Arduino y el motor con nuestra bateria sin necesidad de depender del Ordenador.
Control de 2 servos con MIDI.
Funciones de programación:
Bucles while
Los bucles while se ejecutan continuamente, hasta que la expresión de dentro del paréntesis, (), pasa a ser falsa. Algo debe modificar la variable comprobada, el bucle while nunca terminará. Lo que modifique la variable puede estar en el código, como una variable que se incrementa, o ser una condición externa, como el valor que da un sensor.
while(expresion){
// sentencia(s) }
Sketch Arduino:
#include <Servo.h> // Libreria muy buena de servos Servo myservo1; // variable servo1 Servo myservo2; // variable servo2 byte puredata=0; // almacena el valor binario enviado desde puredata int myVar1 = 0; int myVar2 = 0; void setup(){ Serial.begin(9600); myservo1.attach(9); // define el pin al que esta conectado el servo1 myservo2.attach(10); // define el pin al que esta conectado el servo2 } void loop () { if (Serial.available() > 0) { // si hay algun dato entrado por el serial procede puredata = Serial.read(); // lee los datos binarios del puerto serie myservo1.write(puredata); // Da el angulo del servo1 en grados, 0 a 180. myservo2.write(puredata); // Da el angulo del servo2 en grados, 0 a 180. Serial.println(puredata); // Imprimimos los datos en el monitor serial if(puredata == 0) { while(Serial.available()<=0) { } puredata = Serial.read(); // lee serial A myservo1.write(puredata); // Da el angulo del servo1 en grados, 0 a 180. Serial.println(puredata); // imprime para referencia while(Serial.available()<=0) { } puredata = Serial.read(); // lee serial B myservo2.write(puredata); // Da el angulo del servo2 en grados, 0 a 180. Serial.println(puredata); // imprime para referencia } } }
Patch PD:
Text PD:
#N canvas 507 161 725 587 10; #X msg 125 400 devices; #X msg 58 390 close; #X floatatom 242 250 5 0 0 0 - - -; #X floatatom 284 251 5 0 0 0 - - -; #X obj 198 346 unpack 0 0 0; #X obj 241 31 vsl 15 128 180 0 0 0 empty empty empty 0 -8 0 8 -262144 -1 -1 5700 1; #X obj 285 171 int; #X obj 241 172 int; #X floatatom 192 405 5 0 0 0 - - -; #X floatatom 231 405 5 0 0 0 - - -; #X floatatom 288 410 5 0 0 0 - - -; #X obj 125 482 print; #X obj 198 274 pack 0 0 0; #X obj 93 232 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 -1; #X floatatom 198 250 5 0 0 0 - - -; #X obj 93 258 t b b; #X msg 198 218 0; #X msg 93 164 1; #X msg 93 183 0; #X obj 324 170 loadbang; #X obj 288 146 hsl 128 15 180 0 0 0 empty empty empty -2 -6 0 8 -262144 -1 -1 4600 1; #X msg 242 218 50; #X msg 284 218 50; #X obj 323 200 t b b b b b b b; #X text 58 143 ON/OFF Enviar a arduino; #X text 6 365 ON/OFF conexion PD-arduino; #X obj 125 450 comport 4 9600; #X msg 8 390 open 3; #N canvas 0 22 475 382 midi 1; #X obj 24 -399 ctlin; #X floatatom 24 -373 5 0 0 0 - - -; #X floatatom 38 -353 5 0 0 0 - - -; #X floatatom 54 -335 5 0 0 0 - - -; #X floatatom -5 -197 5 0 0 0 - - -; #X floatatom 63 -197 5 0 0 0 - - -; #X obj -29 -170 spigot; #X floatatom -32 -141 5 0 0 0 - - -; #X obj -7 -265 r control; #X obj 38 -313 s control; #X obj -8 -315 s data; #X obj -51 -224 r data; #X floatatom 249 -186 5 0 0 0 - - -; #X floatatom 317 -186 5 0 0 0 - - -; #X obj 225 -159 spigot; #X floatatom 222 -130 5 0 0 0 - - -; #X obj 247 -254 r control; #X obj 203 -213 r data; #X obj 221 -100 s number; #X obj 249 -225 expr $f1 \; if ($f1 == 39 \, 1 \, 0) \;; #X obj -5 -236 expr $f1 \; if ($f1 == 36 \, 1 \, 0) \;; #X obj -33 -111 s number2; #X connect 0 0 1 0; #X connect 0 1 2 0; #X connect 0 2 3 0; #X connect 1 0 10 0; #X connect 2 0 9 0; #X connect 5 0 6 1; #X connect 6 0 7 0; #X connect 7 0 21 0; #X connect 8 0 20 0; #X connect 11 0 6 0; #X connect 13 0 14 1; #X connect 14 0 15 0; #X connect 15 0 18 0; #X connect 16 0 19 0; #X connect 17 0 14 0; #X connect 19 0 12 0; #X connect 19 1 13 0; #X connect 20 0 4 0; #X connect 20 1 5 0; #X restore 63 62 pd midi; #X obj 238 -17 r number; #X obj 283 90 r number2; #X obj 227 428 pipe 2; #X obj 289 428 pipe 4; #X obj 92 210 metro 50; #X text 140 464 Envia coordenadas a arduino; #X text 17 -51; #X obj 241 7 * 1.4; #X obj 285 116 * 1.4; #X connect 0 0 26 0; #X connect 1 0 26 0; #X connect 2 0 12 1; #X connect 3 0 12 2; #X connect 4 0 8 0; #X connect 4 1 9 0; #X connect 4 2 10 0; #X connect 5 0 7 0; #X connect 6 0 3 0; #X connect 7 0 2 0; #X connect 8 0 26 0; #X connect 9 0 31 0; #X connect 10 0 32 0; #X connect 12 0 4 0; #X connect 13 0 15 0; #X connect 14 0 12 0; #X connect 15 0 12 0; #X connect 15 1 4 0; #X connect 16 0 14 0; #X connect 17 0 33 0; #X connect 18 0 33 0; #X connect 19 0 23 0; #X connect 20 0 6 0; #X connect 21 0 2 0; #X connect 22 0 3 0; #X connect 23 3 16 0; #X connect 23 4 21 0; #X connect 23 5 22 0; #X connect 26 0 11 0; #X connect 27 0 26 0; #X connect 29 0 36 0; #X connect 30 0 37 0; #X connect 31 0 26 0; #X connect 32 0 26 0; #X connect 33 0 13 0; #X connect 36 0 5 0; #X connect 37 0 20 0;
MOTORES SERVO CONTROLADOS CON MIDI A TRAVES DE PD Y
ARDUINO
Motor Servo vs. Iphone
Esta es una manera de utilizar comunicación wireless via IP, para controlar motores Servo. Este programa esta hecho con Arduino, Puredata, y Fantastick.
1. Bajas la aplicacion Fantastick en tu Iphone.
2. Te conectas a la misma red wifi en tu ordenador y en tu iphone, o simplemente creas una red local.
3. Te conectas a la red que creaste con tu iphone y en las preferencias de Fantastick escribes el numero de IP de tu red.
4. Bajas el patch Fantastick de PD en http://pinktwins.com/fantastick/
o creas tu patch de red y lo conectas al puerto UDP 6661 especificado en tu iphone.
Ahora ya puedes interactuar con tu iphone y Puredata!!!
Acelerómetro y objeto 3D
Un acelerómetro es un dispositivo transductor que detecta el movimiento o el giro, es decir, es capaz de responder con una señal eléctrica ante una perturbación inducida por la aplicación de una fuerza o la gravedad.
Memsic 2125
En este ejemplo vamos a usar un acelerómetro Memsic 2125. Este es un acelerómetro de dos ejes capaz de medir la aceleración hasta más/menos 2g ( g = campo gravitatorio). Posee una simple conexión digital: dos pines (uno para cada eje) emiten pulsos cuya duración corresponde con la aceleración de cada eje. A la hora de realizar mediciones muy precisas, el sensor cuenta con un pin de temperatura que puede ser utilizado para compensar posibles errores.
Funcionamiento
El circuito integrado MX2125 que confoma el sensor dispone de una cámara de gas calentado y cuatro sensores de temperatura alrdedor de su eje. Cuando el acelerómetro está en horizontal, todos los sensores de temperatura miden la misma temperatura. Al inclinarlo, el gas caliente se acerca más a uno de los sensores de manera que comparando las temperaturas se puede detectar tanto la aceleración estática como dinámica.
El CI MX2125 se encarga de convertir las medidas de temperatura en pulsos PWM, recibidos por el arduino. Si se mantiene quieto sobre la mesa vas a obtener 1G en algun eje, y si lo golpeas es la suma vectorial de 1G (la gravedad de la tierra) mas la componente vectorial del golpe.
Lectura con arduino
Para leer el acelerómetro con el arduino se utilizará la función pulseIn() que está incluída por defecto en arduino. La función pulseIn() devuelve un unsigned long indicando el número de microsegundos que dura un pulso positivo en un pin.
Para convertir la lectura del pulso en un valor entre -1000 y 1000, la función es la siguiente: f(x) = ((x / 10) – 500) * 8
Funciones de Programación:
pulseIn()
Lee un pulso (ya sea HIGH —alto— o LOW —bajo—) en un pin. Por ejemplo, si value es HIGH, pulseIn() espera a que el pin sea HIGH, empieza a cronometrar, espera a que el pin sea LOW y entonces detiene la medida de tiempo. Devuelve la anchura del pulso en microsegundos. Interrumpe la medida y devuelve 0 si el pulso no ha comenzado en un tiempo especificado. La medida del tiempo en esta función ha sido determinada de forma empírica y está sujeta a errores en pulsos largos. Funciona correctamente en pulsos con una anchura de 10 microsegundos a tres minutos.
unsigned long
Las variable long sin firmar (unsigned long) son variables extendidas para almacenar números, y almacenar 32 bits (4 bytes). Por el contrario que las variables long estándar, las unsigned long no almacenan números negativos, haciendo que su rango sea de 0 a 4,294,967,295 (2^32 – 1).
circuito:
Los pines de 5V y GND (Tierra) de Arduino están conectados a los pines 5V y GND del Memsic 2125; el pin digital 2 de Arduino está conectado al pin de salida X del Memsic 2125 y el pin digital 3 está conectado al pin de salida Y.
Sketch Arduino:
/* Memsic2125 Lee el Memsic 2125, acelerometro de dos ejes. Convierte los pulsos de salida del Memsic en milli-g's (1/1000 de la gravedad de la tierra) y lo imprime en el serial del ordenador. El circuito: * Salida X del acelerometro al pin digital 2 * Salida Y del acelerometro al pin digital 3 * +V del acelerometro a +5V * GND del acelerometro a ground */ // constantes const int xPin = 2; // salida X del acelerometro const int yPin = 3; // salida Y del acelerometro void setup() { // inicializamos la comunicacion serial: Serial.begin(9600); //inicializamos los pines digitales conectados // como entradas: pinMode(xPin, INPUT); pinMode(yPin, INPUT); } void loop() { // variables para leer el ancho de pulso: int pulsoX, pulsoY; //variables que contienen la aceleracion int aceleracionX, aceleracionY; // lee los pulsos de los eje X, Y: pulsoX = pulseIn(xPin,HIGH); pulsoY = pulseIn(yPin,HIGH); // convierte el ancho de pulso en aceleracion // las aceleraciones X, Y estan en milli-g's: // la gravedad de la tierra es 1000 milli-g's, o 1g. aceleracionX = ((pulsoX / 10) - 500) * 8; aceleracionY = ((pulsoY / 10) - 500) * 8; //imprime la aceleracion: Serial.print("X"); Serial.println(pulsoX); Serial.print("Y"); Serial.println(pulsoY); Serial.print("x"); Serial.println(aceleracionX); Serial.print("y"); Serial.println(aceleracionY); delay(50); }
Vista del Patch PD:
Patch Puredata Memsic 2125:
#N canvas 515 22 611 704 10; #X declare -lib moocow; #X msg 186 107 close; #X msg 54 72 open \$1; #X msg 160 84 devices; #X msg 146 60 info; #X obj 53 167 sel 10 13; #X obj 54 299 bytes2any 0 0; #X obj 20 385 import moocow; #X obj 165 297 bytes2any 0 0; #X floatatom 54 323 5 0 0 0 - - -; #X floatatom 165 322 5 0 0 0 - - -; #X text 18 323 X_raw; #X text 130 322 Y_raw; #X obj 54 247 route 88 89 120 121; #X obj 298 297 bytes2any 0 0; #X floatatom 298 322 5 0 0 0 - - -; #X obj 433 299 bytes2any 0 0; #X floatatom 433 324 5 0 0 0 - - -; #X text 263 322 X-acc; #X text 398 324 Y-acc; #X obj 53 197 zl group 5; #X msg 57 31 3; #X obj 52 139 comport 3 9600; #X obj 365 139 vsl 15 128 -1000 1000 0 0 empty empty empty 0 -9 0 10 -262144 -1 -1 6655 1; #X obj 404 258 hsl 128 15 -1000 1000 0 0 empty empty empty -2 -8 0 10 -262144 -1 -1 5944 1; #X obj 298 345 s x; #X obj 435 354 s y; #X floatatom 333 462 0 0 0 0 - - -; #X obj 309 491 translate; #X msg 376 443 0 0 1; #X obj 87 580 gemwin; #X obj 309 401 gemhead; #X msg 147 451 create; #X msg 148 480 destroy; #X msg 145 393 1; #X msg 146 423 0; #X floatatom 327 589 0 0 0 0 - - -; #X msg 152 545 lighting 1; #X obj 80 615 gemhead 1; #X obj 80 653 world_light; #X obj 310 663 model venus.obj; #X obj 152 511 loadbang; #X obj 377 417 loadbang; #X obj 310 629 rotateXYZ; #X floatatom 369 587 0 0 0 0 - - -; #X obj 326 538 r x; #X obj 369 537 r y; #X obj 325 567 * 0.1; #X obj 368 566 * 0.1; #X msg 332 440 2; #X msg 22 31 2; #X connect 0 0 21 0; #X connect 1 0 21 0; #X connect 2 0 21 0; #X connect 3 0 21 0; #X connect 4 0 19 0; #X connect 4 2 19 0; #X connect 5 0 8 0; #X connect 7 0 9 0; #X connect 12 0 5 0; #X connect 12 1 7 0; #X connect 12 2 13 0; #X connect 12 3 15 0; #X connect 13 0 14 0; #X connect 14 0 22 0; #X connect 14 0 24 0; #X connect 15 0 16 0; #X connect 16 0 23 0; #X connect 16 0 25 0; #X connect 19 0 12 0; #X connect 20 0 1 0; #X connect 21 0 4 0; #X connect 26 0 27 1; #X connect 27 0 42 0; #X connect 28 0 27 2; #X connect 30 0 27 0; #X connect 31 0 29 0; #X connect 32 0 29 0; #X connect 33 0 29 0; #X connect 34 0 29 0; #X connect 35 0 42 1; #X connect 36 0 29 0; #X connect 37 0 38 0; #X connect 40 0 36 0; #X connect 41 0 28 0; #X connect 41 0 48 0; #X connect 42 0 39 0; #X connect 43 0 42 2; #X connect 44 0 46 0; #X connect 45 0 47 0; #X connect 46 0 35 0; #X connect 47 0 43 0; #X connect 48 0 26 0; #X connect 49 0 1 0;
Acelerometro ADXL 335
Lee el acelerometro de tres ejes ADXL 335.
La forma de conectarlo a la placa Arduino es la siguiente:
Nota: Es importante que tengamos en cuenta que el acelerometro ADXL 335 trabaja con un voltaje de 3,3v a diferencia del Memsic 2125 que trabaja con 5v.
En el ejemplo siguiente conectaremos los ejes X y Y a los pines análogos x=0, y=1… el eje Z no lo utilizaremos:
El circuito:
* Salida X del acelerometro al pin analogico 0
* Salida Y del acelerometro al pin analogico 1
* +V del acelerometro a +3,3V
* GND del acelerometro a ground
Sketch ADXL 335:
/* ADXL 335 Lee el ADXL 335, acelerometro de tres ejes. El circuito: * Salida X del acelerometro al pin analogico 0 * Salida Y del acelerometro al pin analogico 1 * +V del acelerometro a +3,3V * GND del acelerometro a ground */ const int xPin = 0; // eje x del acelerometro const int yPin = 1; // eje y del acelerometro int valox = 0; // variable para almacenar el valor x capturado desde el sensor int valoy = 1; // variable para almacenar el valor y capturado desde el sensor void setup() { // incializa comunicacion: Serial.begin(9600); } void loop() { valox = analogRead(xPin); // lee el valor x sensor valoy = analogRead(yPin); // lee el valor y sensor int sensorValueX = analogRead(0); int sensorValueY = analogRead(1); //imprime los valores: Serial.print("X"); Serial.println(sensorValueX); Serial.print("Y"); Serial.println(sensorValueY); delay(50); }
Patch Puredata ADXL 335:
#N canvas 476 22 792 735 10; #X declare -lib moocow; #X msg 186 107 close; #X msg 54 72 open \$1; #X msg 160 84 devices; #X msg 146 60 info; #X obj 53 167 sel 10 13; #X obj 54 299 bytes2any 0 0; #X msg 22 31 4; #X obj 409 41 import moocow; #X obj 52 139 comport 4 9600; #X obj 165 297 bytes2any 0 0; #X floatatom 54 323 5 0 0 0 - - -; #X floatatom 165 322 5 0 0 0 - - -; #X text 18 323 X_raw; #X text 130 322 Y_raw; #X obj 54 247 route 88 89 120 121; #X text 89 32 Edit this to open the correct serial port; #X obj 53 197 zl group 5; #X msg 58 32 3; #X floatatom 374 415 0 0 0 0 - - -; #X obj 477 509 gemwin; #X obj 307 352 gemhead; #X msg 554 414 create; #X msg 555 443 destroy; #X msg 552 356 1; #X msg 553 386 0; #X floatatom 325 540 0 0 0 0 - - -; #X msg 559 508 lighting 1; #X obj 470 544 gemhead 1; #X obj 470 582 world_light; #X obj 308 614 model venus.obj; #X obj 559 474 loadbang; #X obj 375 368 loadbang; #X obj 308 580 rotateXYZ; #X floatatom 367 538 0 0 0 0 - - -; #X obj 324 489 r x; #X obj 367 488 r y; #X obj 323 518 * 0.1; #X obj 366 517 * 0.1; #X msg 373 393 2; #X obj 307 442 translateXYZ; #X obj 75 422 s x; #X obj 375 149 vsl 15 128 -1000 1000 0 0 empty empty empty 0 -9 0 10 -262144 -1 -1 6706 1; #X obj 414 268 hsl 128 15 -1000 1000 0 0 empty empty empty -2 -8 0 10 -262144 -1 -1 4839 1; #X floatatom 75 398 5 0 0 0 - - -; #X obj 75 354 * 14; #X floatatom 139 391 5 0 0 0 - - -; #X obj 139 347 * 14; #X obj 140 415 s y; #X obj 75 374 - 4606; #X obj 139 367 - 4704; #X connect 0 0 8 0; #X connect 1 0 8 0; #X connect 2 0 8 0; #X connect 3 0 8 0; #X connect 4 0 16 0; #X connect 4 2 16 0; #X connect 5 0 10 0; #X connect 6 0 1 0; #X connect 8 0 4 0; #X connect 9 0 11 0; #X connect 10 0 44 0; #X connect 11 0 46 0; #X connect 14 0 5 0; #X connect 14 1 9 0; #X connect 16 0 14 0; #X connect 17 0 1 0; #X connect 18 0 39 3; #X connect 20 0 39 0; #X connect 21 0 19 0; #X connect 22 0 19 0; #X connect 23 0 19 0; #X connect 24 0 19 0; #X connect 25 0 32 1; #X connect 26 0 19 0; #X connect 27 0 28 0; #X connect 30 0 26 0; #X connect 31 0 38 0; #X connect 32 0 29 0; #X connect 33 0 32 2; #X connect 34 0 36 0; #X connect 35 0 37 0; #X connect 36 0 25 0; #X connect 37 0 33 0; #X connect 38 0 18 0; #X connect 39 0 32 0; #X connect 43 0 40 0; #X connect 43 0 41 0; #X connect 44 0 48 0; #X connect 45 0 47 0; #X connect 45 0 42 0; #X connect 46 0 49 0; #X connect 48 0 43 0; #X connect 49 0 45 0;
Video demostrativo:
Acelerometro ADXL 345
Para conectar este acelerometro a diferencia del ADXL 335, vamos a comunicarnos atraves del protocolo de bus I2C.
I2C (Inter-Integrated Circuit) es un bus de comunicación muy utilizado para comunicar circuitos integrados, uno de sus usos mas comunes es la comunicación entre un microcontrolador y sensores periféricos. El I2C es un bus multi-maestro es decir permite que haya múltiples maestros y múltiples esclavos en el mismo bus.
I2c fue diseñado por phillips, pero se puede usar sin ningún tipo de problema legal puesto que la patente ya ha expirado.
El bus I2C cuenta con dos lineas SDA(datos) y SCL(clock) ademas de masa. Para programarlo vamos a usar la libreria Wire.
El circuito:
En el ejemplo siguiente conectaremos SCL al pin analogico 5 y SDA al pin analogico 4. Tambien conectamos GND y para alimentar el acelerometro a 3,3v como vemos en la imagen:
Nota: Es importante que tengamos en cuenta que el acelerometro ADXL 345 trabaja con un voltaje de 3,3v a diferencia del Memsic 2125 que trabaja con 5v.
Sketch ADXL 345:
/* ADXL 345 El circuito: * Salida SCL del acelerometro al pin analogico 4 * Salida SDA del acelerometro al pin analogico 5 * +V del acelerometro a +3,3V * GND del acelerometro a ground */ #include <Wire.h> #include "binary_const.h" // with this we can use something B8(01010101) that it will convert to 85 at compile time // the above cames really handy and readable when doing per bit configuration in the ADXL345 registers #define DEVICE (0x53) //ADXL345 device address (with SDO tied to ground) #define TO_READ (6) //num of bytes we are going to read each time (two bytes for each axis) #define INTERRUPTPIN 2 // Arduino pin which is connected to INT1 from the ADXL345 // Register map: see ADXL345 datasheet page 14 const int R_DEVID = 0; const int R_THRESH_TAP = 29; const int R_OFSX = 30; const int R_OFSY = 31; const int R_OFSZ = 32; const int R_DUR = 33; const int R_LATENT = 34; const int R_WINDOW = 35; const int R_THRESH_ACT = 36; const int R_THRESH_INACT = 37; const int R_TIME_INACT = 38; const int R_ACT_INACT_CTL = 39; const int R_THRESH_FF = 40; const int R_TIME_FF = 41; const int R_TAP_AXES = 42; const int R_ACT_TAP_STATUS = 43; const int R_BW_RATE = 44; const int R_POWER_CTL = 45; const int R_INT_ENABLE = 46; const int R_INT_MAP = 47; const int R_INT_SOURCE = 48; const int R_DATA_FORMAT = 49; const int R_DATAX0 = 50; const int R_DATAX1 = 51; const int R_DATAY0 = 52; const int R_DATAY1 = 53; const int R_DATAZ0 = 54; const int R_DATAZ1 = 55; const int R_FIFO_CTL = 56; const int R_FIFO_STATUS = 57; byte buff[TO_READ]; //6 bytes buffer for saving data read from the device char str[512]; //string buffer to transform data before sending it to the serial port boolean inspected = 0; void setup() { Wire.begin(); // join i2c bus (address optional for master) Serial.begin(9600); // start serial for output pinMode(INTERRUPTPIN, INPUT); // interrupts setup writeTo(DEVICE, R_INT_MAP, 0); // send all interrupts to ADXL345's INT1 pin writeTo(DEVICE, R_INT_ENABLE, B8(1111100)); // enable signle and double tap, activity, inactivity and free fall detection // free fall configuration writeTo(DEVICE, R_TIME_FF, 0x14); // set free fall time writeTo(DEVICE, R_THRESH_FF, 0x05); // set free fall threshold // single tap configuration writeTo(DEVICE, R_DUR, 0x1F); // 625us/LSB writeTo(DEVICE, R_THRESH_TAP, 48); // 62.5mg/LSB <==> 3000mg/62.5mg = 48 LSB as datasheet suggestion writeTo(DEVICE, R_TAP_AXES, B8(111)); // enable tap detection on x,y,z axes // double tap configuration writeTo(DEVICE, R_LATENT, 0x10); writeTo(DEVICE, R_WINDOW, 0xFF); // inactivity configuration writeTo(DEVICE, R_TIME_INACT, 10); // 1s / LSB writeTo(DEVICE, R_THRESH_INACT, 3); // 62.5mg / LSB // also working good with high movements: R_TIME_INACT=5, R_THRESH_INACT=16, R_ACT_INACT_CTL=B8(00000111) // but unusable for a quite slow movements // activity configuration writeTo(DEVICE, R_THRESH_ACT, 8); // 62.5mg / LSB // activity and inctivity control writeTo(DEVICE, R_ACT_INACT_CTL, B8(11111111)); // enable activity and inactivity detection on x,y,z using ac // set the ADXL345 in measurement and sleep Mode: this will save power while while we will still be able to detect activity // set the Link bit to 1 so that the activity and inactivity functions aren't concurrent but alternatively activated // set the AUTO_SLEEP bit to 1 so that the device automatically goes to sleep when it detects inactivity writeTo(DEVICE, R_POWER_CTL, B8(111100)); } /* Prints out the state of all registers void inspectRegisters() { int regCount = R_FIFO_STATUS - R_THRESH_TAP + 1; byte regBuff[regCount]; readFrom(DEVICE, R_THRESH_TAP, regCount, regBuff); for(int i=0; i<regCount; i++) { Serial.println(R_THRESH_TAP + i); Serial.println(regBuff[i], BIN); } } */ void loop() { /* if(inspected == 0) { //delay(1000); Serial.println("inspecting registers"); inspectRegisters(); delay(1000); inspected = 1; } */ // we use a digitalRead instead of attachInterrupt so that we can use delay() if(digitalRead(INTERRUPTPIN)) { int interruptSource = readByte(DEVICE, R_INT_SOURCE); //Serial.print("### "); //Serial.println(interruptSource, BIN); if(interruptSource & B8(100)) { Serial.println("### FREE_FALL"); } if(interruptSource & B8(1000)) { Serial.println("### Inactivity"); // we don't need to put the device in sleep because we set the AUTO_SLEEP bit to 1 in R_POWER_CTL // set the LOW_POWER bit to 1 in R_BW_RATE: with this we get worst measurements but we save power int bwRate = readByte(DEVICE, R_BW_RATE); writeTo(DEVICE, R_BW_RATE, bwRate | B8(10000)); } if(interruptSource & B8(10000)) { Serial.println("### Activity"); // get current power mode int powerCTL = readByte(DEVICE, R_POWER_CTL); // set the device back in measurement mode // as suggested on the datasheet, we put it in standby then in measurement mode // we do this using a bitwise and (&) so that we keep the current R_POWER_CTL configuration writeTo(DEVICE, R_POWER_CTL, powerCTL & B8(11110011)); delay(10); // let's give it some time (not sure if this is needed) writeTo(DEVICE, R_POWER_CTL, powerCTL & B8(11111011)); // set the LOW_POWER bit to 0 in R_BW_RATE: get back to full accuracy measurement (we will consume more power) int bwRate = readByte(DEVICE, R_BW_RATE); writeTo(DEVICE, R_BW_RATE, bwRate & B8(01111)); } if(interruptSource & B8(100000)) { Serial.print("### DOUBLE_TAP Axes: "); printTapAxes(); Serial.println(""); // closing Axes line } else if(interruptSource & B8(1000000)) { // when a double tap is detected also a signle tap is deteced. we use an else here so that we only print the double tap Serial.print("### SINGLE_TAP Axes: "); printTapAxes(); Serial.println(""); // closing Axes line } delay(150); } int regAddress = 0x32; //first axis-acceleration-data register on the ADXL345 int x, y, z; readFrom(DEVICE, regAddress, TO_READ, buff); //read the acceleration data from the ADXL345 //each axis reading comes in 10 bit resolution, ie 2 bytes. Least Significat Byte first!! //thus we are converting both bytes in to one int x = (((int)buff[1]) << 8) | buff[0]; y = (((int)buff[3])<< 8) | buff[2]; z = (((int)buff[5]) << 8) | buff[4]; //we send the x y z values as a string to the serial port sprintf(str, "X%d", x); Serial.println(str); sprintf(str, "Y%d", y); Serial.println(str); sprintf(str, "Z%d", z); Serial.println(str); Serial.write(byte(10)); //It appears that delay is needed in order not to clog the port delay(150); } void printTapAxes() { int tapStatus = readByte(DEVICE, R_ACT_TAP_STATUS); if(tapStatus & B8(100)) { Serial.print("x "); } if(tapStatus & B8(10)) { Serial.print("y "); } if(tapStatus & B8(1)) { Serial.print("z "); } } //---------------- Functions //Writes val to address register on device void writeTo(int device, byte address, byte val) { Wire.beginTransmission(device); //start transmission to device Wire.write(address); // send register address Wire.write(val); // send value to write Wire.endTransmission(); //end transmission } //reads num bytes starting from address register on device in to buff array void readFrom(int device, byte address, int num, byte buff[]) { Wire.beginTransmission(device); //start transmission to device Wire.write(address); //sends address to read from Wire.endTransmission(); //end transmission Wire.beginTransmission(device); //start transmission to device Wire.requestFrom(device, num); // request 6 bytes from device int i = 0; while(Wire.available()) //device may send less than requested (abnormal) { buff[i] = Wire.read(); // receive a byte i++; } Wire.endTransmission(); //end transmission } // read a single bite and returns the readed value byte readByte(int device, byte address) { Wire.beginTransmission(device); //start transmission to device Wire.write(address); //sends address to read from Wire.endTransmission(); //end transmission Wire.beginTransmission(device); //start transmission to device Wire.requestFrom(device, 1); // request 1 byte from device int readed = 0; if(Wire.available()) { readed = Wire.read(); // receive a byte } Wire.endTransmission(); //end transmission return readed; }
Libreria Binary Constant
Tambien tenemos que adjuntar la libreria Binary Constant en una nueva pestaña y nombrala como binary_const.h:
/* Binary constant generator macro By Tom Torfs - donated to the public domain From http://bytes.com/topic/c/answers/216333-binary-constant-macros */ /* All macro's evaluate to compile-time constants */ /* *** helper macros *** / /* turn a numeric literal into a hex constant (avoids problems with leading zeroes) 8-bit constants max value 0x11111111, always fits in unsigned long */ #define HEX__(n) 0x##n##LU /* 8-bit conversion function */ #define B8__(x) ((x&0x0000000FLU)?1:0) \ +((x&0x000000F0LU)?2:0) \ +((x&0x00000F00LU)?4:0) \ +((x&0x0000F000LU)?8:0) \ +((x&0x000F0000LU)?16:0) \ +((x&0x00F00000LU)?32:0) \ +((x&0x0F000000LU)?64:0) \ +((x&0xF0000000LU)?128:0) /* *** user macros *** / /* for upto 8-bit binary constants */ #define B8(d) ((unsigned char)B8__(HEX__(d))) /* for upto 16-bit binary constants, MSB first */ #define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) \ + B8(dlsb)) /* for upto 32-bit binary constants, MSB first */ #define B32(dmsb,db2,db3,dlsb) (((unsigned long)B8(dmsb)<<24) \ + ((unsigned long)B8(db2)<<16) \ + ((unsigned long)B8(db3)<<8) \ + B8(dlsb)) /* Sample usage: B8(01010101) = 85 B16(10101010,01010101) = 43605 B32(10000000,11111111,10101010,01010101) = 2164238933 */
Patch Puredata ADXL 345:
#N canvas 476 22 792 735 10; #X declare -lib moocow; #X msg 186 107 close; #X msg 54 72 open \$1; #X msg 160 84 devices; #X msg 146 60 info; #X obj 53 167 sel 10 13; #X obj 54 299 bytes2any 0 0; #X msg 22 31 4; #X obj 409 41 import moocow; #X obj 52 139 comport 4 9600; #X obj 165 297 bytes2any 0 0; #X floatatom 54 323 5 0 0 0 - - -; #X floatatom 165 322 5 0 0 0 - - -; #X text 18 323 X_raw; #X text 130 322 Y_raw; #X obj 54 247 route 88 89 120 121; #X text 89 32 Edit this to open the correct serial port; #X obj 53 197 zl group 5; #X msg 58 32 3; #X floatatom 374 415 0 0 0 0 - - -; #X obj 477 509 gemwin; #X obj 307 352 gemhead; #X msg 554 414 create; #X msg 555 443 destroy; #X msg 552 356 1; #X msg 553 386 0; #X floatatom 325 540 0 0 0 0 - - -; #X msg 559 508 lighting 1; #X obj 470 544 gemhead 1; #X obj 470 582 world_light; #X obj 308 614 model venus.obj; #X obj 559 474 loadbang; #X obj 375 368 loadbang; #X obj 308 580 rotateXYZ; #X floatatom 367 538 0 0 0 0 - - -; #X obj 324 489 r x; #X obj 367 488 r y; #X obj 323 518 * 0.1; #X obj 366 517 * 0.1; #X msg 373 393 2; #X obj 307 442 translateXYZ; #X obj 54 409 s x; #X obj 375 149 vsl 15 128 -1000 1000 0 0 empty empty empty 0 -9 0 10 -262144 -1 -1 5702 1; #X obj 414 268 hsl 128 15 -1000 1000 0 0 empty empty empty -2 -8 0 10 -262144 -1 -1 6998 1; #X floatatom 54 385 5 0 0 0 - - -; #X floatatom 165 377 5 0 0 0 - - -; #X obj 166 401 s y; #X obj 54 352 * 3; #X obj 165 348 * 3; #X connect 0 0 8 0; #X connect 1 0 8 0; #X connect 2 0 8 0; #X connect 3 0 8 0; #X connect 4 0 16 0; #X connect 4 2 16 0; #X connect 5 0 10 0; #X connect 6 0 1 0; #X connect 8 0 4 0; #X connect 9 0 11 0; #X connect 10 0 46 0; #X connect 11 0 47 0; #X connect 14 0 5 0; #X connect 14 1 9 0; #X connect 16 0 14 0; #X connect 17 0 1 0; #X connect 18 0 39 3; #X connect 20 0 39 0; #X connect 21 0 19 0; #X connect 22 0 19 0; #X connect 23 0 19 0; #X connect 24 0 19 0; #X connect 25 0 32 1; #X connect 26 0 19 0; #X connect 27 0 28 0; #X connect 30 0 26 0; #X connect 31 0 38 0; #X connect 32 0 29 0; #X connect 33 0 32 2; #X connect 34 0 36 0; #X connect 35 0 37 0; #X connect 36 0 25 0; #X connect 37 0 33 0; #X connect 38 0 18 0; #X connect 39 0 32 0; #X connect 43 0 40 0; #X connect 43 0 41 0; #X connect 44 0 45 0; #X connect 44 0 42 0; #X connect 46 0 43 0; #X connect 47 0 44 0;
Ejemplo Acelerometro MP3
En este proyecto hemos hakeado un MP3 para controlarlo con un acelerometro. Su lista reproducción esta determinada con la inclinación del acelerometro.
Sketch:
// constantes const int xPin = 2; // salida X del acelerometro const int yPin = 3; // salida Y del acelerometro int back = 12; int next = 13; void setup() { // inicializamos la comunicacion serial: Serial.begin(9600); //inicializamos los pines digitales conectados // como entradas: pinMode(xPin, INPUT); pinMode(yPin, INPUT); pinMode(back, OUTPUT); pinMode(next, OUTPUT); } void loop() { // variables para leer el ancho de pulso: int pulsoX, pulsoY; //variables que contienen la aceleracion int aceleracionX, aceleracionY; // lee los pulsos de los eje X, Y: pulsoX = pulseIn(xPin,HIGH); pulsoY = pulseIn(yPin,HIGH); // convierte el ancho de pulso en aceleracion // las aceleraciones X, Y estan en milli-g's: // la gravedad de la tierra es 1000 milli-g's, o 1g. aceleracionX = ((pulsoX / 10) - 500) * 8; aceleracionY = ((pulsoY / 10) - 500) * 8; if (aceleracionX < -600) { digitalWrite(next, LOW); digitalWrite(back, HIGH); // ejecutar A delay(100); digitalWrite(back, LOW); } else if (aceleracionX > 600) { digitalWrite(back, LOW); digitalWrite(next, HIGH); // ejecutar B delay(100); digitalWrite(next, LOW); } //imprime la aceleracion: Serial.print("X"); Serial.println(pulsoX); Serial.print("Y"); Serial.println(pulsoY); Serial.print("x"); Serial.println(aceleracionX); Serial.print("y"); Serial.println(aceleracionY); delay(50); }
Interacción de Arduino en el mundo del sonido.
Generador de notas usando la función tone()
Este ejemplo muestra como usar la función tone() para generar notas.
Sketch: tone_mario_bros
/* Melodias 8 bits Mario Bros circuito: * 1 Altavoz de 8-ohm conectado al pin digital 8 con una resistencia de 220 ohm */ #include "pitches.h" // Escribe la melodia en notas: int melody[] = { NOTE_E4, NOTE_E4, NOTE_E4, NOTE_C4, NOTE_E4, NOTE_G4, NOTE_C4, NOTE_G3, NOTE_E3, NOTE_A4, NOTE_B4, NOTE_AS4, NOTE_A4, NOTE_G4, NOTE_E4, NOTE_G4, NOTE_A4, NOTE_F4, NOTE_G4, NOTE_E4, NOTE_C4, NOTE_D4, NOTE_B3, NOTE_C4, NOTE_G3, NOTE_E3, NOTE_A4, NOTE_B4, NOTE_AS4, NOTE_A4, NOTE_G4, NOTE_E4, NOTE_G4, NOTE_A4, NOTE_F4, NOTE_G4, NOTE_E4, NOTE_C4, NOTE_D4, NOTE_B3, NOTE_G4, NOTE_FS4, NOTE_F4, NOTE_DS4, NOTE_E4, NOTE_GS3, NOTE_A3, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_D4, NOTE_G4, NOTE_FS4, NOTE_F4, NOTE_DS4, NOTE_E4, NOTE_C5, NOTE_C5, NOTE_C5, NOTE_G4, NOTE_FS4, NOTE_F4, NOTE_DS4, NOTE_E4, NOTE_GS3, NOTE_A3, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_D4, NOTE_DS4, NOTE_D4, NOTE_C4, NOTE_G4, NOTE_FS4, NOTE_F4, NOTE_DS4, NOTE_E4, NOTE_GS3, NOTE_A3, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_D4, NOTE_G4, NOTE_FS4, NOTE_F4, NOTE_DS4, NOTE_E4, NOTE_C5, NOTE_C5, NOTE_C5, NOTE_G4, NOTE_FS4, NOTE_F4, NOTE_DS4, NOTE_E4, NOTE_GS3, NOTE_A3, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_D4, NOTE_DS4, NOTE_D4, NOTE_C4, NOTE_C4, NOTE_C4, NOTE_C4, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_C4, NOTE_A3, NOTE_G3, NOTE_C4, NOTE_C4, NOTE_C4, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_C4, NOTE_C4, NOTE_C4, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_C4, NOTE_A3, NOTE_G3, NOTE_E4, NOTE_E4, NOTE_E4, NOTE_C4, NOTE_E4, NOTE_G4, NOTE_G3}; // duracion de notas: 4 = corchea, 8 = semicorchea, etc.: int noteDurations[] = { 8, 4, 4, 8, 4, 2, 2, 3, 3, 4, 4, 8, 4, 4, 4, 4, 4, 8, 4, 4, 8, 8, 3, 3, 3, 3, 4, 4, 8, 4, 4, 4, 4, 4, 8, 4, 4, 8, 8, 1, 8, 8, 8, 4, 4, 8, 8, 4, 8, 8, 3, 8, 8, 8, 4, 4, 4, 8, 1, 8, 8, 8, 4, 4, 8, 8, 4, 8, 8, 3, 3, 3, 1, 8, 8, 8, 4, 4, 8, 8, 4, 8, 8, 3, 8, 8, 8, 4, 4, 4, 8, 1, 8, 8, 8, 4, 4, 8, 8, 4, 8, 8, 3, 3, 3, 1, 8, 4, 4, 8, 4, 8, 4, 8, 2, 8, 4, 4, 8, 4, 1, 8, 4, 4, 8, 4, 8, 4, 8, 2, 8, 4, 4, 8, 8, 4, 8, 1 }; void setup() { // Punto de lectura de notas en la melodia: for (int thisNote = 0; thisNote < 140; thisNote++) { // para calcular la duracion de las notas, se divide un segundo // en el typo de nota. //corchea = 1000 / 4, semicorchea = 1000 / 8, etc. int noteDuration = 1000/noteDurations[thisNote]; tone(8, melody[thisNote],noteDuration); // para distinguir la nota, se da un minimo de tiempo entre ellas. //la duracion de las notas + 30% suena bien: int pauseBetweenNotes = noteDuration * 1.30; delay(pauseBetweenNotes); // para el tono que esta tocando: noTone(8); } } void loop() { // no es necesario repetir la melodia. }
Libreria Pitches:
Para nuestro sketch es necesario llamar la libreria Pitches con #include «pitches.h», para que la reconozca se hace una pestaña en el sketch con nombre «pitches.h», la cual tendra la libreria pitches la cual define las notas y sus alturas en frecuencia.
/************************************************* * Public Constants *************************************************/ #define NOTE_B0 31 #define NOTE_C1 33 #define NOTE_CS1 35 #define NOTE_D1 37 #define NOTE_DS1 39 #define NOTE_E1 41 #define NOTE_F1 44 #define NOTE_FS1 46 #define NOTE_G1 49 #define NOTE_GS1 52 #define NOTE_A1 55 #define NOTE_AS1 58 #define NOTE_B1 62 #define NOTE_C2 65 #define NOTE_CS2 69 #define NOTE_D2 73 #define NOTE_DS2 78 #define NOTE_E2 82 #define NOTE_F2 87 #define NOTE_FS2 93 #define NOTE_G2 98 #define NOTE_GS2 104 #define NOTE_A2 110 #define NOTE_AS2 117 #define NOTE_B2 123 #define NOTE_C3 131 #define NOTE_CS3 139 #define NOTE_D3 147 #define NOTE_DS3 156 #define NOTE_E3 165 #define NOTE_F3 175 #define NOTE_FS3 185 #define NOTE_G3 196 #define NOTE_GS3 208 #define NOTE_A3 220 #define NOTE_AS3 233 #define NOTE_B3 247 #define NOTE_C4 262 #define NOTE_CS4 277 #define NOTE_D4 294 #define NOTE_DS4 311 #define NOTE_E4 330 #define NOTE_F4 349 #define NOTE_FS4 370 #define NOTE_G4 392 #define NOTE_GS4 415 #define NOTE_A4 440 #define NOTE_AS4 466 #define NOTE_B4 494 #define NOTE_C5 523 #define NOTE_CS5 554 #define NOTE_D5 587 #define NOTE_DS5 622 #define NOTE_E5 659 #define NOTE_F5 698 #define NOTE_FS5 740 #define NOTE_G5 784 #define NOTE_GS5 831 #define NOTE_A5 880 #define NOTE_AS5 932 #define NOTE_B5 988 #define NOTE_C6 1047 #define NOTE_CS6 1109 #define NOTE_D6 1175 #define NOTE_DS6 1245 #define NOTE_E6 1319 #define NOTE_F6 1397 #define NOTE_FS6 1480 #define NOTE_G6 1568 #define NOTE_GS6 1661 #define NOTE_A6 1760 #define NOTE_AS6 1865 #define NOTE_B6 1976 #define NOTE_C7 2093 #define NOTE_CS7 2217 #define NOTE_D7 2349 #define NOTE_DS7 2489 #define NOTE_E7 2637 #define NOTE_F7 2794 #define NOTE_FS7 2960 #define NOTE_G7 3136 #define NOTE_GS7 3322 #define NOTE_A7 3520 #define NOTE_AS7 3729 #define NOTE_B7 3951 #define NOTE_C8 4186 #define NOTE_CS8 4435 #define NOTE_D8 4699 #define NOTE_DS8 4978
Funciones de programación
tone()
Genera una onda cuadrada de la frecuencia especificada (y un 50% de ciclo de trabajo) en un pin. La duración puede ser especificada, en caso contrario la onda continua hasta que haya una llamada a noTone(). El pin puede conectarse a un zumbador piezoeléctrico u otro altavoz que haga sonar los tonos.
sintáxis:
tone(pin, frecuencia, duracion)
noTone()
Detiene la generación de la señal cuadrada que se activa al hacer uso de la función tone(). No tiene efecto si no se está generando ningún tono. NOTA: si quieres generar tonos diferentes en múltiples pines , necesitas usar la función noTone() en el pin antes de llamar a la función tone() en el siguiente pin.
Controlando la activación de la melodia con un sensor LDR
Generador de frequencias y escalas que se transponen: Freq_out
Conservamos la misma conexión de Mario Bros…
En este ejemplo generamos escalas que se van transponiendo:
sketch:
/* freqout(freq, t) // freq en hz, t en ms * un generador de funcion de tono * genera ondas cuadradas con una frquencia y una duracion determinada * el programa incluye una tabla para subir octavas & una funcion de transposicion */ #include <math.h> // requiere minimo un chip Atmega168 #define outpin 8 // Pin de salida al altavoz int ptime; int k, x, dur, freq, t; int i, j; float ps; // variable para rutina de potencias para el cambio de pitch "pitchShift" float noteval; // valores de notas para escalas de 2 octavas // las divide en potencia de dos para generar otras octavas float A = 14080; float AS = 14917.2; float B = 15804.3; float C = 16744; float CS = 17739.7; float D = 18794.5; float DS = 19912.1; float E = 21096.2; float F = 22350.6; float FS = 23679.6; float G = 25087.7; float GS = 26579.5; float A2S = 29834.5; float B2 = 31608.5; float C2 = 33488.1; float C2S = 35479.4; float D2 = 37589.1; float D2S = 39824.3; float E2 = 42192.3; float F2 = 44701.2; float F2S = 47359.3; float G2 = 50175.4; float G2S = 53159; //octavas - octavas correspondientes al piano float oct8 = 4; float oct7 = 8; float oct6 = 16; float oct5 = 32; float oct4 = 64; float oct3 = 128; float oct2 = 256; float oct1 = 512; float oct0 = 1024; //valores ritmicos int wh = 1024; int h = 512; int dq = 448; int q = 256; int qt = 170; int de = 192; int e = 128; int et = 85; int dsx = 96; int sx = 64; int thx = 32; // escala mayor de A editable float majScale[] = { A, B, CS, D, E, FS, GS, A2, B2, C2S, D2, E2, F2S, G2S, A3}; void setup() { Serial.begin(9600); } void loop(){ for(i= 0; i<=11; i++){ ps = (float)i / 12; // escoge un intervalo de transposicion nuevo en cada nuevo loop for(x= 0; x<=15; x++){ noteval = (majScale[x] / oct4) * pow(2,ps); // transpone la escala en 12 tonos // funcion de potencia que genera transposicion de octavas // elimina " * pow(2,ps) " para detener la funcion de transposicion dur = 100; freqout((int)noteval, dur); delay(10); } } } void freqout(int freq, int t) // freq en hz, t en ms { int hperiod; //calcula 1/2 periodo en us long cycles, i; pinMode(outpin, OUTPUT); // enciende el pin de salida hperiod = (500000 / freq) - 7; // resta 7 us para enviarlo a digitalWrite cycles = ((long)freq * (long)t) / 1000; // calcula los ciclos // Serial.print(freq); // Serial.print((char)9); // ascii 9 is tab - debes coaccionarlo a un char para trabajar // Serial.print(hperiod); // Serial.print((char)9); // Serial.println(cycles); for (i=0; i<= cycles; i++){ // toca una nota t ms digitalWrite(outpin, HIGH); delayMicroseconds(hperiod); digitalWrite(outpin, LOW); delayMicroseconds(hperiod - 1); // - 1 para preparar el digitaWrite } pinMode(outpin, INPUT); //apaga el pin para evitar ruido de otras operaciones }
Generando multiples tonos por distintas salidas de Arduino
Este ejemplo muestra como usar el comando tone() para reproducir diferentes notas en múltiples salidas.
Función: «Tone» y “noTone”:
El comando tono() usando uno de los temporizadores internos del ATmega, configurándolo con la frecuencia que deseas, y usando el temporizador para enviar pulsos a través del pin de salida. Como solo usa un temporizador, solo puedes reproducir una nota a la vez. Sin embargo, puedes producir tonos en múltiples pines secuencialmente. Para hacerlo, necesitas apagar el temporizador en un pin para moverlo al siguiente.
Sketch: Ton0Multiple
/* Reproductor de Multiples tonos Reproduce multiples tonos por distintas salidas en secuencia circuito: * Altavoz de 8-ohm conectado a los pines digitales 6, 7, y 11 */ void setup() { } void loop() { // apaga la funcion tone para el pin 11: noTone(11); // Reproduce la nota A4 en el pin 6 a los 200 ms: tone(6, 440, 200); delay(200); // Apaga la funcion tone para el pin 6: noTone(6); // Reproduce la nota B4 en el pin 7 a los 500 ms: tone(7, 494, 500); delay(500); // Apaga la funcion tone para el pin 7: noTone(7); // Reproduce la nota C5 en el pin 11 a los 300 ms: tone(11, 523, 300); delay(300); }
Variando la frecuencia del tono con una entrada analógica
Este ejemplo muestra como usar la función tone() para generar un tono que sigue los valores de una entrada analógica.
Variando la frecuencia con un Sensor LDR
Lee una entrada analógica y la convierte en un valor en un rango comprendido en el rango de frecuencia audible. Los humanos podemos oír frecuencias entre 20 y 20.000 Hz, pero 100 – 1000 funciona bien para este programa.
Hay que encontrar el rango de variación de la entrada analógica para poder hacer la conversión. En el circuito que se muestra el rango de la entrada analógica va desde 400 hasta aproximadamente 1000.Cambia los valores en la función map() para que se ajusten al rango de tu sensor.
Sketch: seguidor_de_frecuencia
/* seguidor de frecuencia Reproduce una altura de frecuencia que depende de una entrada analoga circuito: * Altavoz de 8-ohm conectado al pin digital 8 * Fotoresistor LDR conectado al pin analogo 0. va de 0 a 5V * Resistencia de 1K a GND del pin analogo 0 */ void setup() { // inicializamos la comunicacion serial (para ver la respuesta del sensor): Serial.begin(9600); } void loop() { // lee la salida del sensor: int sensorReading = analogRead(A0); // imprime los datos de respuesta del sensor Serial.println(sensorReading); // mapea la frecuencia al rango de la entrada del sensor. // se pueden ajustar los valores minimos y maximos // dependiendo de los rangos que da el sensor: int thisPitch = map(sensorReading, 400, 1000, 100, 1000); // reproduce el pitch: tone(8, thisPitch, 10); }
Variando la frecuencia con un potenciometro
Creando un teclado virtual con sensores, Keyboard sensor
Este es un teclado virtual creado con 6 sensores LDR, los cuales están programados a través de arduino para que cada uno de una nota especifica.
En este caso hago una escala Blues…
Sketch: keyboard_sensor
/* Teclado con sensores Reproduce notas que cambianon en base a una entrada analogica circuito: * 6 fotoresistores conectados a +5V y a una entrada analogica, entradas de 0 a 5 * 6 resistencias de 1K conectadas a la entradas analogicas y a tierra GND. * 1 altavoz de 8-ohm conectado al pin digital 8 con una resistencia de 220-ohm */ #include "pitches.h" const int threshold = 200; // el minimo que lee el sensor para generar una nota // notas que reproduce, correspondientes a 6 sensores: int notes[] = { NOTE_D4, NOTE_E4, NOTE_G4, NOTE_A4, NOTE_AS4, NOTE_B4}; void setup() { } void loop() { for (int thisSensor = 0; thisSensor <= 5; thisSensor++) { // lee uno de los sensores: int sensorReading = analogRead(thisSensor); // si el sensor logra el nivel de threshold: if (sensorReading < threshold) { //reproduce la nota correspondiente a este sensor: tone(8, notes[thisSensor], 20); } } Serial.println(); }
Nota: En este caso necesitariamos tambien cargar la libreria «pitches.h» en otra pestaña.
Creando un Sintetizador Granular con Arduino.
Este es un sintetizador granular con arduino y 6 potenciometros conectados a las entradas análogicas y un jack pelado conectado al altavoz y al pin 3 digital.
sketch: Synth_granular
// Sintetizador granular // // Analog in 0: Grano 1 altura // Analog in 1: Grano 2 caida // Analog in 2: Grano 1 caida // Analog in 3: Grano 2 altura // Analog in 4: Grano frecuencia de repeticion // // Digital 3: Salida de Audio (Digital 11 en ATmega8) #include <avr/io.h> #include <avr/interrupt.h> uint16_t syncPhaseAcc; uint16_t syncPhaseInc; uint16_t grainPhaseAcc; uint16_t grainPhaseInc; uint16_t grainAmp; uint8_t grainDecay; uint16_t grain2PhaseAcc; uint16_t grain2PhaseInc; uint16_t grain2Amp; uint8_t grain2Decay; // Mapea los pines analogos #define SYNC_CONTROL (4) #define GRAIN_FREQ_CONTROL (0) #define GRAIN_DECAY_CONTROL (2) #define GRAIN2_FREQ_CONTROL (3) #define GRAIN2_DECAY_CONTROL (1) // Al cambiar esto hay que volver a cablear el audio() #if defined(__AVR_ATmega8__) // // En antiguas placas ATmega8. // la salida es en el pin 11 // #define LED_PIN 13 #define LED_PORT PORTB #define LED_BIT 5 #define PWM_PIN 11 #define PWM_VALUE OCR2 #define PWM_INTERRUPT TIMER2_OVF_vect #elif defined(__AVR_ATmega1280__) // // En la Arduino Mega // La salida es en el pin 3 // #define LED_PIN 13 #define LED_PORT PORTB #define LED_BIT 7 #define PWM_PIN 3 #define PWM_VALUE OCR3C #define PWM_INTERRUPT TIMER3_OVF_vect #else // // Para las ultimas placas ATmega168 y ATmega328 // La salida es en el pin 3 // #define PWM_PIN 3 #define PWM_VALUE OCR2B #define LED_PIN 13 #define LED_PORT PORTB #define LED_BIT 5 #define PWM_INTERRUPT TIMER2_OVF_vect #endif // mapeado logaritmico // uint16_t antilogTable[] = { 64830,64132,63441,62757,62081,61413,60751,60097,59449,58809,58176,57549,56929,56316,55709,55109, 54515,53928,53347,52773,52204,51642,51085,50535,49991,49452,48920,48393,47871,47356,46846,46341, 45842,45348,44859,44376,43898,43425,42958,42495,42037,41584,41136,40693,40255,39821,39392,38968, 38548,38133,37722,37316,36914,36516,36123,35734,35349,34968,34591,34219,33850,33486,33125,32768 }; uint16_t mapPhaseInc(uint16_t input) { return (antilogTable[input & 0x3f]) >> (input >> 6); } // mappeado de paso cromatico // uint16_t midiTable[] = { 17,18,19,20,22,23,24,26,27,29,31,32,34,36,38,41,43,46,48,51,54,58,61,65,69,73, 77,82,86,92,97,103,109,115,122,129,137,145,154,163,173,183,194,206,218,231, 244,259,274,291,308,326,346,366,388,411,435,461,489,518,549,581,616,652,691, 732,776,822,871,923,978,1036,1097,1163,1232,1305,1383,1465,1552,1644,1742, 1845,1955,2071,2195,2325,2463,2610,2765,2930,3104,3288,3484,3691,3910,4143, 4389,4650,4927,5220,5530,5859,6207,6577,6968,7382,7821,8286,8779,9301,9854, 10440,11060,11718,12415,13153,13935,14764,15642,16572,17557,18601,19708,20879, 22121,23436,24830,26306 }; uint16_t mapMidi(uint16_t input) { return (midiTable[(1023-input) >> 3]); } // mapeado de paso pentatonico // uint16_t pentatonicTable[54] = { 0,19,22,26,29,32,38,43,51,58,65,77,86,103,115,129,154,173,206,231,259,308,346, 411,461,518,616,691,822,923,1036,1232,1383,1644,1845,2071,2463,2765,3288, 3691,4143,4927,5530,6577,7382,8286,9854,11060,13153,14764,16572,19708,22121,26306 }; uint16_t mapPentatonic(uint16_t input) { uint8_t value = (1023-input) / (1024/53); return (pentatonicTable[value]); } void audioOn() { #if defined(__AVR_ATmega8__) // ATmega8 tiene registros distintos TCCR2 = _BV(WGM20) | _BV(COM21) | _BV(CS20); TIMSK = _BV(TOIE2); #elif defined(__AVR_ATmega1280__) TCCR3A = _BV(COM3C1) | _BV(WGM30); TCCR3B = _BV(CS30); TIMSK3 = _BV(TOIE3); #else // Se programa el PWM a 31.25kHz, fase justa TCCR2A = _BV(COM2B1) | _BV(WGM20); TCCR2B = _BV(CS20); TIMSK2 = _BV(TOIE2); #endif } void setup() { pinMode(PWM_PIN,OUTPUT); audioOn(); pinMode(LED_PIN,OUTPUT); } void loop() { // En loop se programan los parametros de un oscilador. // //Evitar funciones que hagan muchas interrupciones de encendido y apagado. // causan clicks en el audio. // Mapeo de frecuencia syncPhaseInc = mapPhaseInc(analogRead(SYNC_CONTROL)) / 4; // Mapeo de paso a notas MIDI: C, Db, D, Eb, E, F... //syncPhaseInc = mapMidi(analogRead(SYNC_CONTROL)); // Mapeo de paso pentatonico: D, E, G, A, B //syncPhaseInc = mapPentatonic(analogRead(SYNC_CONTROL)); grainPhaseInc = mapPhaseInc(analogRead(GRAIN_FREQ_CONTROL)) / 2; grainDecay = analogRead(GRAIN_DECAY_CONTROL) / 8; grain2PhaseInc = mapPhaseInc(analogRead(GRAIN2_FREQ_CONTROL)) / 2; grain2Decay = analogRead(GRAIN2_DECAY_CONTROL) / 4; } SIGNAL(PWM_INTERRUPT) { uint8_t value; uint16_t output; syncPhaseAcc += syncPhaseInc; if (syncPhaseAcc < syncPhaseInc) { // Tiempo para comenzar siguente grano grainPhaseAcc = 0; grainAmp = 0x7fff; grain2PhaseAcc = 0; grain2Amp = 0x7fff; LED_PORT ^= 1 << LED_BIT; // Mas rapido que usando digitalwrite } // Incremento de fase de los osciladores de grano grainPhaseAcc += grainPhaseInc; grain2PhaseAcc += grain2PhaseInc; // convierte la fase en una onda triangular value = (grainPhaseAcc >> 7) & 0xff; if (grainPhaseAcc & 0x8000) value = ~value; //Multiplica por la amplitud del grano para tomar el sample output = value * (grainAmp >> 8); // Repeticion del grano en segundos value = (grain2PhaseAcc >> 7) & 0xff; if (grain2PhaseAcc & 0x8000) value = ~value; output += value * (grain2Amp >> 8); // Hace que la amplitud del grano caiga por un factor cada sample (caida exponencial) grainAmp -= (grainAmp >> 8 ) * grainDecay; grain2Amp -= (grain2Amp >> 8 ) * grain2Decay; // Escala la salida al rango que tenemos, hace clipping si es necesario output >>= 9; if (output > 255) output = 255; // Salida a PWM (es mas rapido que usando analogWrite) PWM_VALUE = output; }
Teclado con sensores LDR para hacer Síntesis Aditiva:
Usando Standard Firmata y pduino (arduino-test)
Este es un instrumento creado para tocar con una mano el teclado del ordenador simulando un teclado tradicional, y con la otra mano un teclado virtual creado con sensores LDR y una lampara. La idea es hacer sombra con el dedo sobre el fotoresistor y de esta forma bajar un armónico de la nota tocada… en este instrumento controlo los primeros cuatro armónicos de cada nota con cuatro dedos de la mano.
Sintesis de voz humana
Sketch: speech_daisybel
//Conectamos el altavoz al Pin digital 3 y cargamos el siguiente sketck: #include <avr/io.h> #include <avr/interrupt.h> #include <avr/pgmspace.h> uint16_t pitchPhase, form1Phase,form2Phase,form3Phase; uint16_t pitchPhaseInc,form1PhaseInc,form2PhaseInc,form3PhaseInc; uint8_t form1Amp,form2Amp,form3Amp; uint8_t noiseMod=10; int8_t sinCalc[256] PROGMEM = { /* Esta tabla lee varias funciones de velocidad al mismo tiempo extrayendo la fase y la amplitud del paquete nibble haciendo un calculo siusoidal y mapeando la amplitud exponencial escalandola al rango apropiado. Nibble o Cuado es el conjunto de cuatro dígitos binarios (bits) o medio octeto. ROUND( FLOOR(a/16,1) *SIN( 2 * PI() * IF( MOD(a,16), EXP(0.18*MOD(a,16)), 0 ) /16 )*127 ,0) */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,2,2,3,3,4,5,6,7,8,10,12,14,17,20,24, 0,4,4,5,6,7,9,11,13,15,18,22,26,31,37,45, 0,5,6,7,8,10,12,14,17,20,24,28,34,41,49,58, 0,5,6,7,9,10,12,15,18,21,26,31,37,44,53,63, 0,5,6,7,8,10,12,14,17,20,24,28,34,41,49,58, 0,4,4,5,6,7,9,11,13,15,18,22,26,31,37,45, 0,2,2,3,3,4,5,6,7,8,10,12,14,17,20,24, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,-2,-2,-3,-3,-4,-5,-6,-7,-8,-10,-12,-14,-17,-20,-24, 0,-4,-4,-5,-6,-7,-9,-11,-13,-15,-18,-22,-26,-31,-37,-45, 0,-5,-6,-7,-8,-10,-12,-14,-17,-20,-24,-28,-34,-41,-49,-58, 0,-5,-6,-7,-9,-10,-12,-15,-18,-21,-26,-31,-37,-44,-53,-63, 0,-5,-6,-7,-8,-10,-12,-14,-17,-20,-24,-28,-34,-41,-49,-58, 0,-4,-4,-5,-6,-7,-9,-11,-13,-15,-18,-22,-26,-31,-37,-45, 0,-2,-2,-3,-3,-4,-5,-6,-7,-8,-10,-12,-14,-17,-20,-24 }; int8_t sqrCalc[256] PROGMEM ={ 0,1,2,2,2,3,3,4,5,5,6,8,9,11,13,16, 0,1,2,2,2,3,3,4,5,5,6,8,9,11,13,16, 0,1,2,2,2,3,3,4,5,5,6,8,9,11,13,16, 0,1,2,2,2,3,3,4,5,5,6,8,9,11,13,16, 0,1,2,2,2,3,3,4,5,5,6,8,9,11,13,16, 0,1,2,2,2,3,3,4,5,5,6,8,9,11,13,16, 0,1,2,2,2,3,3,4,5,5,6,8,9,11,13,16, 0,1,2,2,2,3,3,4,5,5,6,8,9,11,13,16, 0,-1,-2,-2,-2,-3,-3,-4,-5,-5,-6,-8,-9,-11,-13,-16, 0,-1,-2,-2,-2,-3,-3,-4,-5,-5,-6,-8,-9,-11,-13,-16, 0,-1,-2,-2,-2,-3,-3,-4,-5,-5,-6,-8,-9,-11,-13,-16, 0,-1,-2,-2,-2,-3,-3,-4,-5,-5,-6,-8,-9,-11,-13,-16, 0,-1,-2,-2,-2,-3,-3,-4,-5,-5,-6,-8,-9,-11,-13,-16, 0,-1,-2,-2,-2,-3,-3,-4,-5,-5,-6,-8,-9,-11,-13,-16, 0,-1,-2,-2,-2,-3,-3,-4,-5,-5,-6,-8,-9,-11,-13,-16, 0,-1,-2,-2,-2,-3,-3,-4,-5,-5,-6,-8,-9,-11,-13,-16 }; // Cambiar estos valores requiere tambien reescribir audioOn() #if defined(__AVR_ATmega8__) #define LED_PIN 13 #define LED_PORT PORTB #define LED_BIT 5 // En la Antigua placa ATmega8,la salida es por el pin 11 #define PWM_PIN 11 #define PWM_VALUE OCR2 #define PWM_INTERRUPT TIMER2_OVF_vect #elif defined(__AVR_ATmega1280__) #define LED_PIN 13 #define LED_PORT PORTB #define LED_BIT 7 #define PWM_PIN 3 #define PWM_VALUE OCR3C #define PWM_INTERRUPT TIMER3_OVF_vect #else #define LED_PIN 13 #define LED_PORT PORTB #define LED_BIT 5 // Para la placa ATmega168, la salida es en el pin 3 #define PWM_PIN 3 #define PWM_VALUE OCR2B #define PWM_INTERRUPT TIMER2_OVF_vect #endif void audioOn() { #if defined(__AVR_ATmega8__) // ATmega8 tiene diferentes registros TCCR2 = _BV(WGM20) | _BV(COM21) | _BV(CS20); TIMSK = _BV(TOIE2); #elif defined(__AVR_ATmega1280__) TCCR3A = _BV(COM3C1) | _BV(WGM30); TCCR3B = _BV(CS30); TIMSK3 = _BV(TOIE3); #else // Ajustar el PWM a 31.25kHz, de fase justa TCCR2A = _BV(COM2B1) | _BV(WGM20); TCCR2B = _BV(CS20); TIMSK2 = _BV(TOIE2); #endif } void setup() { pinMode(PWM_PIN,OUTPUT); audioOn(); pinMode(LED_PIN,OUTPUT); } #define FORMANT_SZ 7 enum { _SP,_DOT,_QM,_COM,_HYP,_IY,_IH,_EH,_AE,_AA, _AH,_AO,_UH,_AX,_IX,_ER,_UX,_OH,_RX,_LX, _WX,_YX,_WH,_R,_L,_W,_Y,_M,_N,_NX, _DX,_Q,_S,_SH,_F,_TH,__H,__X,_Z,_ZH, _V,_DH,_CHa,_CHb,_Ja,_Jb,_Jc,_Jd,_EY,_AY, _OY,_AW,_OW,_UW,_Ba,_Bb,_Bc,_Da,_Db,_Dc, _Ga,_Gb,_Gc,_GXa,_GXb,_GXc,_Pa,_Pb,_Pc,_Ta, _Tb,_Tc,_Ka,_Kb,_Kc,_KXa,_KXb,_KXc }; uint8_t formantTable[] PROGMEM = { 0x0, 0x0, 0x0,0x0,0x0,0x0,0x0,/*00 space*/ 0x13,0x43,0x5b,0x0,0x0,0x0,0x0,/*01 .*/ 0x13,0x43,0x5b,0x0,0x0,0x0,0x0,/*02 ?*/ 0x13,0x43,0x5b,0x0,0x0,0x0,0x0,/*03 ,*/ 0x13,0x43,0x5b,0x0,0x0,0x0,0x0,/*04 -*/ 0xa,0x54,0x6e,0xd,0xa,0x8,0x0,/*05 IY*/ 0xe,0x49,0x5d,0xd,0x8,0x7,0x0,/*06 IH*/ 0x13,0x43,0x5b,0xe,0xd,0x8,0x0,/*07 EH*/ 0x18,0x3f,0x58,0xf,0xe,0x8,0x0,/*08 AE*/ 0x1b,0x28,0x59,0xf,0xd,0x1,0x0,/*09 AA*/ 0x17,0x2c,0x57,0xf,0xc,0x1,0x0,/*10 AH*/ 0x15,0x1f,0x58,0xf,0xc,0x0,0x0,/*11 AO*/ 0x10,0x25,0x52,0xf,0xb,0x1,0x0,/*12 UH*/ 0x14,0x2c,0x57,0xe,0xb,0x0,0x0,/*13 AX*/ 0xe,0x49,0x5d,0xd,0xb,0x7,0x0,/*14 IX*/ 0x12,0x31,0x3e,0xc,0xb,0x5,0x0,/*15 ER*/ 0xe,0x24,0x52,0xf,0xc,0x1,0x0,/*16 UX*/ 0x12,0x1e,0x58,0xf,0xc,0x0,0x0,/*17 OH*/ 0x12,0x33,0x3e,0xd,0xc,0x6,0x0,/*18 RX*/ 0x10,0x25,0x6e,0xd,0x8,0x1,0x0,/*19 LX*/ 0xd,0x1d,0x50,0xd,0x8,0x0,0x0,/*20 WX*/ 0xf,0x45,0x5d,0xe,0xc,0x7,0x0,/*21 YX*/ 0xb,0x18,0x5a,0xd,0x8,0x0,0x0,/*22 WH*/ 0x12,0x32,0x3c,0xc,0xa,0x5,0x0,/*23 R*/ 0xe,0x1e,0x6e,0xd,0x8,0x1,0x0,/*24 L*/ 0xb,0x18,0x5a,0xd,0x8,0x0,0x0,/*25 W*/ 0x9,0x53,0x6e,0xd,0xa,0x8,0x0,/*26 Y*/ 0x6,0x2e,0x51,0xc,0x3,0x0,0x0,/*27 M*/ 0x6,0x36,0x79,0x9,0x9,0x0,0x0,/*28 N*/ 0x6,0x56,0x65,0x9,0x6,0x3,0x0,/*29 NX*/ 0x6,0x36,0x79,0x0,0x0,0x0,0x0,/*30 DX*/ 0x11,0x43,0x5b,0x0,0x0,0x0,0x0,/*31 Q*/ 0x6,0x49,0x63,0x7,0xa,0xd,0xf,/*32 S*/ 0x6,0x4f,0x6a,0x0,0x0,0x0,0x0,/*33 SH*/ 0x6,0x1a,0x51,0x3,0x3,0x3,0xf,/*34 F*/ 0x6,0x42,0x79,0x0,0x0,0x0,0x0,/*35 TH*/ 0xe,0x49,0x5d,0x0,0x0,0x0,0x0,/*36 /H*/ 0x10,0x25,0x52,0x0,0x0,0x0,0x0,/*37 /X*/ 0x9,0x33,0x5d,0xf,0x3,0x0,0x3,/*38 Z*/ 0xa,0x42,0x67,0xb,0x5,0x1,0x0,/*39 ZH*/ 0x8,0x28,0x4c,0xb,0x3,0x0,0x0,/*40 V*/ 0xa,0x2f,0x5d,0xb,0x4,0x0,0x0,/*41 DH*/ 0x6,0x4f,0x65,0x0,0x0,0x0,0x0,/*42 CHa*/ 0x6,0x4f,0x65,0x0,0x0,0x0,0x0,/*43 CHb*/ 0x6,0x42,0x79,0x1,0x0,0x0,0x0,/*44 Ja*/ 0x5,0x42,0x79,0x1,0x0,0x0,0x0,/*45 Jb*/ 0x6,0x6e,0x79,0x0,0xa,0xe,0x0,/*46 Jc*/ 0x0, 0x0, 0x0,0x2,0x2,0x1,0x0,/*47 Jd*/ 0x13,0x48,0x5a,0xe,0xe,0x9,0x0,/*48 EY*/ 0x1b,0x27,0x58,0xf,0xd,0x1,0x0,/*49 AY*/ 0x15,0x1f,0x58,0xf,0xc,0x0,0x0,/*50 OY*/ 0x1b,0x2b,0x58,0xf,0xd,0x1,0x0,/*51 AW*/ 0x12,0x1e,0x58,0xf,0xc,0x0,0x0,/*52 OW*/ 0xd,0x22,0x52,0xd,0x8,0x0,0x0,/*53 UW*/ 0x6,0x1a,0x51,0x2,0x0,0x0,0x0,/*54 Ba*/ 0x6,0x1a,0x51,0x4,0x1,0x0,0xf,/*55 Bb*/ 0x6,0x1a,0x51,0x0,0x0,0x0,0x0,/*56 Bc*/ 0x6,0x42,0x79,0x2,0x0,0x0,0x0,/*57 Da*/ 0x6,0x42,0x79,0x4,0x1,0x0,0xf,/*58 Db*/ 0x6,0x42,0x79,0x0,0x0,0x0,0x0,/*59 Dc*/ 0x6,0x6e,0x70,0x1,0x0,0x0,0x0,/*60 Ga*/ 0x6,0x6e,0x6e,0x4,0x1,0x0,0xf,/*61 Gb*/ 0x6,0x6e,0x6e,0x0,0x0,0x0,0x0,/*62 Gc*/ 0x6,0x54,0x5e,0x1,0x0,0x0,0x0,/*63 GXa*/ 0x6,0x54,0x5e,0x4,0x1,0x0,0xf,/*64 GXb*/ 0x6,0x54,0x5e,0x0,0x0,0x0,0x0,/*65 GXc*/ 0x6,0x1a,0x51,0x0,0x0,0x0,0x0,/*66 Pa*/ 0x6,0x1a,0x51,0x0,0x0,0x0,0x0,/*67 Pb*/ 0x6,0x1a,0x51,0x0,0x0,0x0,0x0,/*68 Pc*/ 0x6,0x42,0x79,0x0,0x0,0x0,0x0,/*69 Ta*/ 0x6,0x42,0x79,0x0,0x0,0x0,0x0,/*70 Tb*/ 0x6,0x42,0x79,0x0,0x0,0x0,0x0,/*71 Tc*/ 0x6,0x6d,0x65,0x0,0x0,0x0,0x0,/*72 Ka*/ 0xa,0x56,0x65,0xc,0xa,0x7,0x0,/*73 Kb*/ 0xa,0x6d,0x70,0x0,0x0,0x0,0x0,/*74 Kc*/ 0x6,0x54,0x5e,0x0,0x0,0x0,0x0,/*75 KXa*/ 0x6,0x54,0x5e,0x0,0xa,0x5,0x0,/*76 KXb*/ 0x6,0x54,0x5e,0x0,0x0,0x0,0x0 /*77 KXc*/ }; uint16_t pitchTable[64] = { //cubre desde A1 hasta C7 58,61,65,69,73,77,82,86,92,97, 103,109,115,122,129,137,145,154,163,173, 183,194,206,218,231,244,259,274,291,308, 326,346,366,388,411,435,461,489,518,549, 581,616,652,691,732,776,822,871,923,978, 1036,1097,1163,1232,1305,1383,1465,1552,1644,1742, 1845,1955,2071,2195 }; uint8_t frameList[] PROGMEM = { #if 1 _Da,3,0,39,_Db,1,0,39,_Dc,1,3,39,_EY,8,6,39,_YX,20,3,39, // Dai.. _Z,10,0,36,_IY,35,3,36, // ..sy _Da,3,0,32,_Db,1,0,32,_Dc,1,3,32,_EY,8,6,32,_YX,20,3,32, // Dai.. _Z,10,0,27,_IY,35,3,27, // ..sy _Ga,2,0,29,_Gb,2,0,29,_Gc,2,0,29,_IH,10,3,29,_V,5,0,29, // Give _M,2,0,31,_IY,10,3,31, // me _YX,5,0,32,_AO,10,0,32,_RX,5,0,32, // your _AH,25,0,29,_NX,5,0,29, // an.. _S,2,0,32,_ER,10,0,32,_RX,3,0,32, // ..swer _Da,3,0,27,_Db,1,0,27,_Dc,1,3,27,_UX,80,3,27,_WX,5,0,27, // do _AY,5,20,34,_YX,10,0,34,_M,8,0,34, // I'm __H,5,0,39,_AX,30,0,39,_F,10,0,39, // half _Ka,3,0,36,_Kb,3,0,36,_Kc,4,0,36,_R,5,0,36,_EY,30,0,36, // cra.. _Z,5,0,32,_IY,40,0,32, // ..zy _AO,10,0,29,_LX,5,0,29, // all _F,5,0,31,_AO,10,0,31, // for _DH,5,0,32,_AH,10,0,32, // the _L,5,0,34,_AH,20,0,34,_V,5,0,34,// love _AA,10,0,36,_V,5,0,36,// of _Y,10,0,34,_UX,80,0,34, // you _IH,10,0,36,_Ta,2,0,36,_Tb,1,0,36,_Tc,2,0,36,// It _W,2,0,37,_OH,10,0,37,_N,1,0,37,_Ta,1,0,37,_Tb,1,0,37,_Tc,1,0,37,// won't _Ba,2,0,36,_Bb,1,0,36,_Bc,2,0,36,_IY,10,0,36,// be _AH,15,0,34,// a _S,2,0,39,_Ta,2,0,39,_Tb,2,0,39,_Tc,2,0,39,_AY,1,10,39,_YX,10,0,39,// sty.. _L,3,0,36,_IH,10,0,36,_SH,2,0,36,// ..lish _M,5,0,34,_AE,10,0,34,// ma.. _R,5,0,32,_IH,60,0,32,_Ja,2,0,32,_Jb,2,0,32,_Jc,2,0,32,// ..rriage _AY,5,10,34,_YX,5,0,34,// I _Ka,2,0,36,_Kb,2,0,36,_Kc,2,0,36,_AH,20,0,36,_N,2,0,36,_Ta,2,0,36,_Tb,2,0,26,_Tc,2,0,36,// can't _AX,15,0,32,// a.. _F,5,0,29,_AO,20,0,29,_R,2,0,29,_Da,1,0,29,_Db,1,0,29,_Dc,1,0,29,// ..fford _AX,15,0,32,// a _Ka,1,0,29,_Kb,1,0,29,_Kc,1,0,29,_AE,12,0,29,// ca.. _R,5,0,27,_IH,45,0,27,_Ja,2,0,27,_Jb,2,0,27,_Jc,2,0,27,// ..rriage _Ba,1,0,27,_Bb,1,0,27,_Bc,1,0,27,_AH,10,0,27,_Ta,1,0,27,_Tb,1,0,27,_Tc,1,0,27,// but _Y,5,0,32,_UH,10,10,32,_L,5,0,32,// you'll _L,3,0,36,_UH,10,0,36,_Ka,1,0,36,_Kb,1,0,36,_Kc,1,0,36,// look _S,2,0,34,_W,2,0,34,_IY,20,0,34,_Ta,2,0,34,_Tb,2,0,34,_Tc,2,0,34,// sweet _AX,15,0,27,// a.. _Ka,2,0,32,_Kb,2,0,32,_Kc,2,0,32,_R,2,0,32,_AA,20,0,32,_S,5,0,32,// ..cross _DH,5,0,36,_AH,10,0,36,// the _S,2,0,34,_IY,10,0,34,_Ta,2,0,34,_Tb,2,0,34,_Tc,2,0,34,// seat _AA,10,0,36,_V,5,0,36,// of _AE,15,0,37,// a _Ba,2,0,39,_Bb,2,0,39,_Bc,2,0,39,_AY,5,5,39,_YX,5,0,39,// bi.. _S,5,0,36,_IH,10,0,36,// ..cy.. _Ka,2,0,32,_Kb,2,0,32,_Kc,2,0,32,_L,9,0,32,// ..cle _M,2,0,34,_EY,5,10,34,_YX,10,0,34,_Da,2,0,34,_Db,2,0,34,_Dc,2,0,34,// made _F,5,0,27,_OY,1,5,27,_RX,5,0,27,// for _Ta,2,0,32,_Tb,2,0,32,_Tc,2,0,32,_UX,50,0,32,// two #endif _Ta,0,0,61 }; int frameTime = 15; // ms uint16_t basePitch; int formantScale; void loop() { formantScale = 54;//random(20,80);//54; uint8_t *framePos = frameList; while(1) { int n; uint8_t startFormant,staticFrames,tweenFrames; uint16_t startPitch,nextPitch; uint8_t nextFormant; int16_t startForm1PhaseInc,startForm2PhaseInc,startForm3PhaseInc; uint8_t startForm1Amp,startForm2Amp,startForm3Amp; uint8_t startMod; uint8_t *formantPos; // Lee el siguiente elemento en la lista de frame startFormant = pgm_read_byte(framePos++); staticFrames = pgm_read_byte(framePos++); if (!staticFrames) break; // Fin de frase tweenFrames = pgm_read_byte(framePos++); startPitch = pitchTable[pgm_read_byte(framePos++)]; nextFormant = pgm_read_byte(framePos); nextPitch = pitchTable[pgm_read_byte(framePos+3)]; pitchPhaseInc = startPitch; formantPos = formantTable + startFormant * FORMANT_SZ; form1PhaseInc = startForm1PhaseInc = pgm_read_byte(formantPos++)*formantScale; form2PhaseInc = startForm2PhaseInc = pgm_read_byte(formantPos++)*formantScale; form3PhaseInc = startForm3PhaseInc = pgm_read_byte(formantPos++)*formantScale; form1Amp = startForm1Amp = pgm_read_byte(formantPos++); form2Amp = startForm2Amp = pgm_read_byte(formantPos++); form3Amp = startForm3Amp = pgm_read_byte(formantPos++); noiseMod = startMod = pgm_read_byte(formantPos++); for (;staticFrames--;) delay(frameTime); if (tweenFrames) { uint8_t* formantPos; int16_t deltaForm1PhaseInc,deltaForm2PhaseInc,deltaForm3PhaseInc; int8_t deltaForm1Amp,deltaForm2Amp,deltaForm3Amp; int8_t deltaMod; uint8_t nextMod; int16_t deltaPitch; tweenFrames--; formantPos = formantTable + nextFormant * FORMANT_SZ; deltaForm1PhaseInc = pgm_read_byte(formantPos++)*formantScale - startForm1PhaseInc; deltaForm2PhaseInc = pgm_read_byte(formantPos++)*formantScale - startForm2PhaseInc; deltaForm3PhaseInc = pgm_read_byte(formantPos++)*formantScale - startForm3PhaseInc; deltaForm1Amp = pgm_read_byte(formantPos++) - startForm1Amp; deltaForm2Amp = pgm_read_byte(formantPos++) - startForm2Amp; deltaForm3Amp = pgm_read_byte(formantPos++) - startForm3Amp; deltaMod = pgm_read_byte(formantPos++) - startMod; deltaPitch = nextPitch - startPitch; deltaMod = nextMod - startMod; for (int i=1; i<=tweenFrames; i++) { form1PhaseInc = startForm1PhaseInc + (i*deltaForm1PhaseInc)/tweenFrames; form2PhaseInc = startForm2PhaseInc + (i*deltaForm2PhaseInc)/tweenFrames; form3PhaseInc = startForm3PhaseInc + (i*deltaForm3PhaseInc)/tweenFrames; form1Amp = startForm1Amp + (i*deltaForm1Amp)/tweenFrames; form2Amp = startForm2Amp + (i*deltaForm2Amp)/tweenFrames; form3Amp = startForm3Amp + (i*deltaForm3Amp)/tweenFrames; pitchPhaseInc = startPitch + (i*deltaPitch)/tweenFrames; noiseMod = startMod + (i*deltaMod)/tweenFrames; delay(frameTime); } } } delay(300); } SIGNAL(PWM_INTERRUPT) { int8_t value; static int8_t noise; int16_t phaseNoise = noise * noiseMod; noise += noise<<2; noise++; // noise' = 5*noise+1 form1Phase += form1PhaseInc; value = pgm_read_byte(sinCalc+(((form1Phase>>8) & 0xf0) | form1Amp)); form2Phase += form2PhaseInc; value += pgm_read_byte(sinCalc+(((form2Phase>>8) & 0xf0) | form2Amp)); form3Phase += form3PhaseInc; value += pgm_read_byte(sqrCalc+(((form3Phase>>8) & 0xf0) | form3Amp)); value = (value * (0xff^(pitchPhase>>8)))>>8; pitchPhase += pitchPhaseInc; if ((pitchPhase+phaseNoise) < pitchPhaseInc) { form1Phase = 0; form2Phase = 0; form3Phase = 0; } PWM_VALUE = value + 0x80; }
Importando objetos 3D a Puredata
El entorno Grafico con el cual trabajamos en Puredata es GEM y para objetos 3D utilizamos openGL. Para que nuestro programa reconozca un modelo 3D necesitamos importarlo con formato .obj. En el siguiente link tenemos modelos 3D ya diseñados en formato obj: http://graphics.im.ntu.edu.tw/~robin/courses/cg03/model/ En Puredata usamos el objeto «model» y llamamos al archivo .obj que queremos declarandolo en el objeto. Es importante que el modelo .obj que queramos llamar se encuentre en la misma carpeta que nuestro patch de PD, de esta manera Puredata lo podra encontrar.
Medidor de Distancias por Ultrasonidos Ping
El Ping es un medidor de distancias por ultrasonidos de Parallax. Detecta la distancia que hay con el objeto más cercano que hay en frente del sensor (hasta 3 metros). Funciona enviando una ráfaga de ultrasonido y esperando a escuchar el eco que se produce cuando esta rebota con el objeto. La placa Arduino envía un pulso corto para disparar la detección y espera por un pulso en el mismo pin. La duración de este segundo pulso es el tiempo que ha tardado el ultrasonido en ir entre el sensor y el objeto y de vuelta al sensor. Conociendo la velocidad del sonido, ese tiempo se puede convertir en distancia.
Circuito
El pin de 5V de el PING))) se conecta al pin de 5V de la placa Arduino, el pin de masa, se conecta al pin de masa del Arduino y el pin SIG (señal) se conecta al pin digital 7 en el Arduino.
Sketck Arduino:
// Ping pd theremin. // constante pin 7 const int pingPin = 7; void setup() { // inicializa la comunicacion serial: Serial.begin(9600); } void loop() { //establece unas variables para la duracion del ping, // y el resultado lo expresa en cm: long duration, cm; // PING es activado con un pulso HIGH de 2 o mas microsegundos. // Entrega un pulso LOW antes para asegurar que el pulso HIGH sea limpio: pinMode(pingPin, OUTPUT); digitalWrite(pingPin, LOW); delayMicroseconds(2); digitalWrite(pingPin, HIGH); delayMicroseconds(5); digitalWrite(pingPin, LOW); // El mismo pin es usado para leer la señal del Ping: El pulso HIGH // Su tiempo de duracion se expresa en microsegundos y es el tiempo que pasa // ente la señal enviada por el Ping y la recepcion de su eco reflectado en un objeto. pinMode(pingPin, INPUT); duration = pulseIn(pingPin, HIGH); // convierte el tiempo en distancia cm = microsecondsToCentimeters(duration); Serial.print(cm); Serial.println(); delay(100); } long microsecondsToCentimeters(long microseconds) { // La velocidad del sonido es de 340 m/s o 29 microsegundos por centimetro. // El sonido viaja de ida y vuelta, entonces para obtener la distancia del // objeto tomamos la mitad de la distancia recorrida. return microseconds / 29 / 2; }
Funciones de Programación
long
Las variables de tipo Long son variables de tamaño extendido para almacenamiento de números, y 32 bits (4 bytes), desde -2,147,483,648 hasta 2,147,483,647.
delayMicroseconds()
Detiene brevemente el programa por la cantidad en tiempo (en microsegundos) especificada como parámetro. Existen mil microsegundos en un milisegundo, y un millon de microsegundos en un segundo.
return
Termina una función y devuelve un valor a la función que la llama. Puede no devolver nada.
return valor;
ó
return;
Ejemplo:
Una función que compara la entrada de un sensor a un umbral:
int comprobarSensor(){ if (analogRead(0) > 400) { return 1; else{ return 0; } }
La palabra clave return es útil para depurar una sección de código sin tener que comentar una gran cantidad de líneas de código posiblemente incorrecto.
void loop(){ // código magnífico a comprobar aquí return; // el resto del programa del que se desconfía // que nunca será ejecutado por estar detrás de return }
Theremin con un sensor de ultrasonido Ping
En este ejemplo, utilizamos la información enviada por arduino al puerto serie, para controlar la lectura de la frequencia de un oscilador de frequencia con la distancia reconocida por el sensor de ultrasonido Ping con respecto a un objeto, en este caso, mi mano.
Theremin y objeto 3D con Ping
Ahora podemos añadir un objeto 3D a nuestro sensor Ping y controlamos la cercania del objeto a nosotros con respecto a la cercania de mi mano al sensor. Esto gracias a Puredata, GEM y openGL, que nos permiten controlar el objeto en un espacio 3D con la información recibida por el puerto serial enviada por Arduino y el sensor de ultrasonido Ping.
Patch PD:
Text PD:
#N canvas 478 22 764 688 10; #X declare -lib moocow; #X msg 186 91 close; #X msg 54 72 open \$1; #X msg 160 68 devices; #X msg 146 44 info; #X msg 57 31 2; #X obj 53 167 sel 10 13; #X obj 53 220 bytes2any 0 0; #X obj 54 244 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -8 0 10 -262144 -1 -1 0 256; #X obj 215 19 import moocow; #X obj 53 195 zl group 4; #X obj 52 139 comport 4 9600; #X msg 22 31 3; #X obj 247 516 *~; #X floatatom 96 385 0 0 0 0 - - -; #X obj 271 488 line~; #X msg 271 432 0.1 100; #X msg 290 457 0 100; #X obj 131 543 metro 500; #X obj 131 518 r metro; #X msg 263 158 \; metro 0; #X msg 262 99 \; pd dsp 1 \; metro 1; #N canvas 0 22 450 300 (subpatch) 0; #X array array99 100 float 0; #X coords 0 -1 100 1 200 100 1; #X restore 195 249 graph; #X obj 94 571 tabwrite~ array99; #X obj 95 414 osc~ 1000; #X obj 55 339 line 0 10; #X obj 56 309 pack f 200; #X obj 51 371 vsl 15 128 0 1000 0 0 empty empty empty 0 -9 0 10 -262144 -1 -1 0 1; #X obj 247 544 dac~; #X obj 56 278 * 40; #X obj 177 417 s z; #X floatatom 565 417 0 0 0 0 - - -; #X obj 507 239 gemwin; #X obj 498 354 gemhead; #X msg 567 110 create; #X msg 568 139 destroy; #X msg 565 52 1; #X msg 566 82 0; #X floatatom 516 542 0 0 0 0 - - -; #X msg 572 204 lighting 1; #X obj 500 274 gemhead 1; #X obj 500 312 world_light; #X obj 499 624 model venus.obj; #X obj 572 170 loadbang; #X obj 566 370 loadbang; #X obj 496 580 rotateXYZ; #X floatatom 558 540 0 0 0 0 - - -; #X obj 515 491 r x; #X obj 558 490 r y; #X obj 514 520 * 0.1; #X obj 557 519 * 0.1; #X msg 564 395 2; #X obj 641 389 r z; #X obj 498 444 translateXYZ; #X obj 641 418 * -0.01; #X obj 642 448 + 3; #X obj 383 624 model elephal.obj; #X obj 268 622 model ateneam.obj; #X connect 0 0 10 0; #X connect 1 0 10 0; #X connect 2 0 10 0; #X connect 3 0 10 0; #X connect 4 0 1 0; #X connect 5 0 9 0; #X connect 5 2 9 0; #X connect 6 0 7 0; #X connect 7 0 28 0; #X connect 9 0 6 0; #X connect 10 0 5 0; #X connect 11 0 1 0; #X connect 12 0 27 0; #X connect 12 0 27 1; #X connect 13 0 23 0; #X connect 13 0 29 0; #X connect 14 0 12 1; #X connect 15 0 14 0; #X connect 16 0 14 0; #X connect 17 0 22 0; #X connect 18 0 17 0; #X connect 18 0 17 0; #X connect 23 0 12 0; #X connect 23 0 22 0; #X connect 24 0 13 0; #X connect 24 0 26 0; #X connect 25 0 24 0; #X connect 28 0 25 0; #X connect 30 0 52 3; #X connect 32 0 52 0; #X connect 33 0 31 0; #X connect 34 0 31 0; #X connect 35 0 31 0; #X connect 36 0 31 0; #X connect 37 0 44 1; #X connect 38 0 31 0; #X connect 39 0 40 0; #X connect 42 0 38 0; #X connect 43 0 50 0; #X connect 44 0 56 0; #X connect 45 0 44 2; #X connect 46 0 48 0; #X connect 47 0 49 0; #X connect 48 0 37 0; #X connect 49 0 45 0; #X connect 50 0 30 0; #X connect 51 0 53 0; #X connect 52 0 44 0; #X connect 53 0 54 0; #X connect 54 0 30 0;
Sampler Theremin
Scratcher
Aqui tenemos un Scratcher con Arduino, Puredata y un sensor de Ultrasonido Ping. La idea es simular el sonido que suena al scratchear un disco… en este caso scratcheamos con la mano en el aire. El sensor de Ultrasonido capta movimientos sutiles de la mano logrando una simulación muy buena en pequeños movimientos y gestos.
Patch Puredata:
A mi Patch de Ping Theremin le he añadido un envío a un «Scratcher» programado con una Sample Table en Puredata.
Ejemplo de posibles funciones de Arduino con un sensor de Ultrasonido
Sensor de Ultrasonido HC-SR04
sketch:
int Pin_echo = 13; int Pin_trig = 12; int Pinrojo=6; int Pinverde=5; int buzzer=4; void setup() { Serial.begin (9600); pinMode(Pinverde,OUTPUT); //Definimos los pines pinMode(Pinrojo, OUTPUT); pinMode(buzzer,OUTPUT); pinMode(Pin_trig, OUTPUT); pinMode(Pin_echo, INPUT); } void loop() { int duracion, cm; digitalWrite(Pin_trig, LOW); delayMicroseconds(2); digitalWrite(Pin_trig, HIGH); delayMicroseconds(10); digitalWrite(Pin_trig, LOW); duracion = pulseIn(Pin_echo, HIGH); cm = duracion / 29 / 2; Serial.print("Distancia:"); Serial.print(cm); Serial.println(" cm"); if (cm>20) //20 cm es la distancia de emergencia { digitalWrite(Pinverde, HIGH); //En caso que un objeto esté lejos, pintamos el LED verde digitalWrite(Pinrojo, LOW); digitalWrite(buzzer, LOW); } else if (cm<20) { digitalWrite(Pinverde, LOW); //Si el objeto está muy cerca, hacemos sonar la alarma buzzer y el LED rojo digitalWrite(Pinrojo, HIGH); tone(buzzer, 440, 500); } delay(500); }
pulseIn()
Lee un pulso (ya sea HIGH —alto— o LOW —bajo—) en un pin. Por ejemplo, si value es HIGH, pulseIn() espera a que el pin sea HIGH, empieza a cronometrar, espera a que el pin sea LOW y entonces detiene la medida de tiempo. Devuelve la anchura del pulso en microsegundos. Interrumpe la medida y devuelve 0 si el pulso no ha comenzado en un tiempo especificado.
Funcionamiento
Los ultrasonidos son señales acústicas cuyas frecuencias – de 20 a 400 Khz– están por encima del rango de frecuencias sensibles al oído humano.
Los sensores de ultrasonidos son capaces de medir la distancia a la que están respecto a un objeto por medio de un sistema de medición de ecos. Los sensores de ultrasonidos están formados por un transductor que emite un pulso corto de energía ultrasónica. Cuando el pulso es reflejado por un objeto, el sensor captura el eco producido por medio de un receptor, y mediante un sistema de tratamiento de la señal, calcula la distancia a la que está de dicho objeto. La fórmula para calcular la distancia de un sensor de ultrasonidos es:
d= 1/2 v·t
Dónde d es distancia, v es velocidad y t es tiempo.
Para calcular la distancia a la que está un objeto, utilizaremos la fórmula:
d=1/2 ·343·t
Pero como nos interesa conocer la distancia en cm, lo que hacemos es calcular cuánto se tarda en recorrer 1 cm: 343 m/s = 34300 cm/s. Entonces, aplicando la formula “Distancia = Velocidad x Tiempo”, tenemos que 1cm = 34300·t. Despejando, t =29,15us. Por lo que finalmente, la fórmula para calcular la distancia con el sensor es:
d = (t/2) / 29.4
Seguidor de frecuencia con Sensor de Ultrasonido HC-SR04
Sketch Seguidor de frecuencia con Sensor de Ultrasonido HC-SR04:
int Pin_echo = 13; int Pin_trig = 12; int Pinrojo=6; int Pinverde=5; int buzzer=4; unsigned int pitch=0;
void setup() { Serial.begin (9600); pinMode(Pinverde,OUTPUT); //Definimos los pines pinMode(Pinrojo, OUTPUT); pinMode(buzzer,OUTPUT); pinMode(Pin_trig, OUTPUT); pinMode(Pin_echo, INPUT); }
void loop() { int duracion, cm; digitalWrite(Pin_trig, LOW); delayMicroseconds(2); digitalWrite(Pin_trig, HIGH); delayMicroseconds(10); digitalWrite(Pin_trig, LOW); duracion = pulseIn(Pin_echo, HIGH); cm = duracion / 29 / 2; pitch = map(duracion, 20, 700, 500, 2500); pitch = (duracion * -1) + 1500; Serial.print("Distancia:"); Serial.print(cm); Serial.println(" cm"); Serial.print(" duracion: "); Serial.println(duracion); if (cm>20) //20 cm es la distancia de emergencia { digitalWrite(Pinverde, HIGH); //En caso que un objeto esté lejos, pintamos el LED verde digitalWrite(Pinrojo, LOW); digitalWrite(buzzer, LOW); } else if (cm<20) { digitalWrite(Pinverde, LOW); //Si el objeto está muy cerca, hacemos sonar la alarma buzzer y el LED rojo digitalWrite(Pinrojo, HIGH); tone(buzzer, pitch, 20); } delay(10); }
Sketch HC-SR04 enviando al Serial:
int Pin_echo = 13; int Pin_trig = 12; int Pinrojo=6; int Pinverde=5; int buzzer=4; unsigned int pitch=0; void setup() { Serial.begin (9600); pinMode(Pinverde,OUTPUT); //Definimos los pines pinMode(Pinrojo, OUTPUT); pinMode(buzzer,OUTPUT); pinMode(Pin_trig, OUTPUT); pinMode(Pin_echo, INPUT); } void loop() { int duracion, cm; digitalWrite(Pin_trig, LOW); delayMicroseconds(2); digitalWrite(Pin_trig, HIGH); delayMicroseconds(10); digitalWrite(Pin_trig, LOW); duracion = pulseIn(Pin_echo, HIGH); cm = duracion / 29 / 2; pitch = map(duracion, 20, 700, 500, 2500); pitch = (duracion * -1) + 1500; Serial.print(cm); Serial.println(); if (cm>20) //20 cm es la distancia de emergencia { digitalWrite(Pinverde, HIGH); //En caso que un objeto esté lejos, pintamos el LED verde digitalWrite(Pinrojo, LOW); } else if (cm<20) { digitalWrite(Pinverde, LOW); //Si el objeto está muy cerca, hacemos sonar la alarma buzzer y el LED rojo digitalWrite(Pinrojo, HIGH); } delay(10); }
Sensor PIR de Parallax y Arduino
El siguiente sketch:
Enciende un led con la detección de movimiento de un sensor PIR de Parallax.
Determina el principio y final de una secuencia de movimientos.
Referencia del Sensor PIR de Parallax: http://www.parallax.com/detail.asp?product_id=555-28027
La salida del sensor va a HIGH si detecta movimiento.
También va a LOW si detecta movimiento entre tiempos.
Este programa ignora las fases cortas de LOW dando un tiempo limite, asumiendo que hay movimiento continuo en estas fases.
Conexiones:
Sketch de Arduino:
//le damos un tiempo de calibracion al sensor (10-60 seg deacuerdo a las especificaciones) int calibrationTime = 30; //el tiempo cuando el sensor envia un impulso LOW long unsigned int lowIn; //la cantidad de tiempo en milisegundos que el sensor debe estar en LOW //antes de que asumamos que se ha detenido el movimiento long unsigned int pause = 5000; boolean lockLow = true; boolean takeLowTime; int pirPin = 3; //El pin digital al que esta conectado el sensor int ledPin = 13; //Inicializamos void setup(){ Serial.begin(9600); pinMode(pirPin, INPUT); pinMode(ledPin, OUTPUT); digitalWrite(pirPin, LOW); //le damos al sensor un tiempo de calibracion Serial.print("calibrating sensor "); for(int i = 0; i < calibrationTime; i++){ Serial.print("."); delay(1000); } Serial.println(" done"); Serial.println("SENSOR ACTIVE"); delay(50); } // Programa void loop(){ if(digitalRead(pirPin) == HIGH){ digitalWrite(ledPin, HIGH); //el LED visualiza el estado HIGH de salida del sensor if(lockLow){ //Nos aseguramos de esperar a la transicion a LOW antes de que haya mas salidas: lockLow = false; Serial.println("---"); Serial.print("motion detected at "); Serial.print(millis()/1000); Serial.println(" sec"); delay(50); } takeLowTime = true; } if(digitalRead(pirPin) == LOW){ digitalWrite(ledPin, LOW); //el led visualiza el estado LOW del sensor if(takeLowTime){ lowIn = millis(); //salva el tiempo de transicion de HIGH a LOW takeLowTime = false; //nos aseguramos de que esto solo pase durante de una fase LOW } //si el sensor esta en LOW por mas de un tiempo determinado hace una pausa, //asumimos que no habra mas movimiento if(!lockLow && millis() - lowIn > pause){ //nos aseguramos que este bloque de codigo solo se ejecute despues de //que se detecte un nuevo movimiento lockLow = true; Serial.print("motion ended at "); //salida Serial.print((millis() - pause)/1000); Serial.println(" sec"); delay(50); } } }
Operadores Booleanos.
Se pueden usar dentro de operaciones condicionales o en una sentencia if.
&& (AND lógico)
Verdadero sólo si ambos operadores son Verdadero, por ejemplo:
if (digitalRead(2) == HIGH && digitalRead(3) == HIGH) { // lee dos pulsadores // ... }
Es Verdadero sólo si ambas entradas estás activadas, es decir, en estado HIGH.
|| (OR lógico)
Verdadero si alguno de los dos operadores es Verdadero, por ejemplo:
if (x > 0 || y > 0) { // ... }
Es Verdadero si alguno de los valores x ó y es mayor que 0.
! (NOT)
Verdadero si el operador es Falso, por ejempo:
if (!x) { // ... }
Es Verdadero si el valor de x es Falso (p. e. si x es igual a 0).
Monitor serial:
En el monitor serial imprimimos el tiempo de detección de movimiento y el tiempo en que termina una fase de movimiento continuo.
Control de un Relé con Arduino.
Este ejemplo muestra como encender una bombilla de 220V de corriente alterna (AC) mediante un circuito de 5V de corriente continua (DC) gobernado por Arduino. Se puede utilizar con cualquier otro circuito de 220V con un máximo de 10A (con el relé del ejemplo).
Relé
El relé es un dispositivo electromecánico, que funciona como un interruptor controlado por un circuito eléctrico en el que, por medio de un electroimán, se acciona un juego de uno o varios contactos que permiten abrir o cerrar otros circuitos eléctricos independientes.
Para activar la bobina del relé utilizamos un transistor (BD135 o BD137) por si la potencia que Arduino nos proporciona no es suficiente, y utilizamos también, un diodo de protección (1N4001, 1N4002, 1N4003, … o 1N4007) para eliminar la corriente inducida al apagar la bobina.
Vamos a utilizar un relé cuya tensión de trabajo puede ser de 5V, un transistor y un diodo de protección que puede ser un 1N4148 o en su defecto un 1N4001. El diodo, evita la destrucción del transistor por la contra corriente producida por la inducción del relé.
circuito:
Blink de 2 segundos con Relé y una lampara de 220v
sketch de Arduino:
/* Enciende y apaga una bombilla de 220V, cada 2 segundos, mediante un relé conectado al PIN 2 de Arduino */ int relayPin = 2; // PIN al que va conectado el relé void setup(){ pinMode(relayPin, OUTPUT); } void loop() { digitalWrite(relayPin, HIGH); // ENCENDIDO delay(2000); digitalWrite(relayPin, LOW); // APAGADO delay(2000); }
Relé y Puredata: control de Encendido con un Trigger
sketch de Arduino:
//Rele Trigger y Puredata const int relePin = 2; void setup() { Serial.begin(9600); pinMode(relePin, OUTPUT); } void loop() { byte Trigger; if (Serial.available()) { Trigger = Serial.read(); analogWrite(relePin, Trigger); } }
Código text de PD:
#N canvas 239 22 480 700 10; #X msg 82 436 close; #X msg 69 406 open \$1; #X msg 72 365 3; #X obj 82 493 comport 3 9600; #X floatatom 143 433 5 0 0 0 - - -; #X text 6 10 El patch envia un numero binario entre 0 255 por el puerto serial al arduino; #X msg 124 347 255; #X msg 165 387 0; #X obj 115 274 bonk~; #X obj 110 206 adc~; #X msg 17 67 \; pd dsp 1; #X obj 146 57 vsl 15 128 0 100 0 0 empty empty empty 0 -9 0 10 -262144 -1 -1 7700 1; #X obj 111 237 *~; #X floatatom 150 227 5 0 0 0 - - -; #X obj 123 313 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X obj 161 349 pipe 500; #X msg 37 366 2; #X text 165 105 determina el nivel de entrada de tu micro; #X text 1 101 Enciende el audio; #X obj 149 203 * 0.001; #X connect 0 0 3 0; #X connect 1 0 3 0; #X connect 2 0 1 0; #X connect 4 0 3 0; #X connect 6 0 4 0; #X connect 7 0 4 0; #X connect 8 1 14 0; #X connect 9 0 12 0; #X connect 11 0 19 0; #X connect 12 0 8 0; #X connect 14 0 6 0; #X connect 14 0 15 0; #X connect 15 0 7 0; #X connect 16 0 1 0; #X connect 19 0 12 1; #X connect 19 0 13 0;
Caja Multi-relé para control de múltiples luces con Arduino.
Esta caja fue fabricada con el fin de utilizar múltiples luces controladas independientemente con Arduino y Puredata. De este modo logramos sincronizar una media audiovisual con el encendido y apagado de las luces.
Arduino y Processing
Los valores en voltaje de 0 a 5v que envia el potenciometro por el Pin análogo, son leidos por analogRead(), como valores entre o y 1023. Estos valores son representados en una gráfica en Processing.
Recordemos que la forma de comunicarse Arduino con otros programas es a través del puerto serie, asi que nuestro sketch de arduino lo que esta haciendo es: Recibe los valores análogos, Los covierte en digital y Los imprime en el Serial. Después Processing lee estos valores y los re interpreta en una gráfica.
Sketch de Arduino:
void setup() { // initialize the serial communication: Serial.begin(9600); } void loop() { // send the value of analog input 0: Serial.println(analogRead(A0)); // wait a bit for the analog-to-digital converter // to stabilize after the last reading: delay(10); }
Sketch de Processing:
// Graphing sketch // This program takes ASCII-encoded strings // from the serial port at 9600 baud and graphs them. It expects values in the // range 0 to 1023, followed by a newline, or newline and carriage return // Created 20 Apr 2005 // Updated 18 Jan 2008 // by Tom Igoe // This example code is in the public domain. import processing.serial.*; Serial myPort; // The serial port int xPos = 1; // horizontal position of the graph void setup () { // set the window size: size(400, 300); // List all the available serial ports println(Serial.list()); // I know that the first port in the serial list on my mac // is always my Arduino, so I open Serial.list()[0]. // Open whatever port is the one you're using. myPort = new Serial(this, Serial.list()[0], 9600); // don't generate a serialEvent() unless you get a newline character: myPort.bufferUntil('\n'); // set inital background: background(0); } void draw () { // everything happens in the serialEvent() } void serialEvent (Serial myPort) { // get the ASCII string: String inString = myPort.readStringUntil('\n'); if (inString != null) { // trim off any whitespace: inString = trim(inString); // convert to an int and map to the screen height: float inByte = float(inString); inByte = map(inByte, 0, 1023, 0, height); // draw the line: stroke(127,34,255); line(xPos, height, xPos, height - inByte); // at the edge of the screen, go back to the beginning: if (xPos >= width) { xPos = 0; background(0); } else { // increment the horizontal position: xPos++; } } }
Sensor Piezo-eléctrico
El sensor Piezo-eléctrico se conecta all pin análogo 0. Conecta también un Led al Pin 13 para ver su reaccion al tocar el piezo-eléctrico.
Sketch Piezo-eléctrico:
// Estas constantes no cambian: const int ledPin = 13; // led conectado al pin digital 13 const int knockSensor = A0; // Un piezo conectado al pin analogo 0 const int threshold = 100; // El valor de threshold que determina cuando hay entrada de sonido. // Estas variables cambian: int sensorReading = 0; // variables para almacenar el valor que lee del sensor pin int ledState = LOW; // variables para almacenar el ultimo estado del Led, para parpadear la luz void setup() { pinMode(ledPin, OUTPUT); // declara que e ledPin es una salida Serial.begin(9600); // usamos el puerto serial } void loop() { // lee el sensor y almacena el valor en la variable sensorReading: sensorReading = analogRead(knockSensor); // si la lectura del sensor es mayor que el threshold: if (sensorReading >= threshold) { // nos da el estado del ledPin: ledState = !ledState; // actualiza el estado del LED: digitalWrite(ledPin, ledState); // Envia la palabra "Knock!" al ordenador, en una nueva linea Serial.println("Knock!"); } delay(100); // retardo para evitar sobre recarga en la memoria del puerto serial }
Instrumento MIDI con sensor Piezo-eléctrico
Bombo de Batería activado con un sensor Piezo-eléctrico vía MIDI con Hairless, Arduino y Logic.
Sketch Bombo MIDI:
//Guerda el umbral de sensibilidad que disparará la acción int Umbral; void setup() { pinMode(5, OUTPUT); pinMode(6, OUTPUT); pinMode(7, OUTPUT); pinMode(8, OUTPUT); pinMode(19, INPUT); // Establece el baud rate del MIDI; Serial.begin(115200); }
void loop() { Umbral=analogRead(5);//Lee el valor del pote para establecer la sensibilidad
if(analogRead(4)>Umbral){ digitalWrite(8,HIGH); Serial.write( byte(0x9F)); Serial.write( byte(35)); //Key Number para percusion 35 bombo Serial.write( byte(127)); //Velocidad (es el volumen. De 0 a 127) delay(220);} else{ digitalWrite(8,LOW); } }
Sensor de fuerza
Con el mismo Sketch de Arduino del Bombo, vamos a conectar un sensor de fuerza en el pin analogico 4. En este caso hacemos un divisor de tension con nuestro sensor de fuerza y una resistencia de 10 kohm. De la misma manera que hicimos en el ejemplo del fotoresistor.
Sensor de Temperatura y Arduino
Para este ejemplo utilizamos un sensor de temperatura LM35.
El LM35 proporciona 10mV por cada grado centígrado y una resolución de 0,5 grados.
El LM35 está calibrado en grados Kelvin y trabaja en un rango que va desde 233,15 ºK a 373,15 ºK (cuyo equivalente en centígrados sería de -40 ºC a +100 ºC).
Los datos recibidos por el pin análogo de arduino los enviamos al serial para poderlos leer. El convertidor analógico digital de la placa Arduino es de 10bits y su resolución es de 5V/1024, esto es, algo menos de 5mv.
Viéndolo de frente, el primer pin es un pin va a 5v, el segundo pin va al pin analógico 1 y el tercer pin va a tierra (GND).
Sketch Arduino:
/* Termometro int val; int tempPin = 1; void setup() { Serial.begin(9600); } void loop() { val = analogRead(tempPin); float mv = ( val/1024.0)*5000; float cel = mv/10; float farh = (cel*9)/5 + 32; Serial.print("TEMPRATURE = "); Serial.print(cel); Serial.print("*C"); Serial.println(); delay(1000); /* Descomenta esto para obtener los grados Farenhit Serial.print("TEMPRATURE = "); Serial.print(farh); Serial.print("*F"); Serial.println(); */ }
Encendiendo y apagando un LED con un Push botton
Conectamos el LED al Pin digital 13 y el Push botton al pin digital 2. El Push botton esta conectado también a 5v y a GND a través de una resistencia de 10 kohms como lo muestra la siguiente imagen:
En el siguiente sketch el LED se enciende cuando presionamos el boton y se apaga cuando lo volvemos a presionar:
Sketch:
int inPin = 2; // input pin Push botton int outPin = 13; // output pin LED int state = HIGH; // the current state of the output pin int reading; // the current reading from the input pin int previous = LOW; // the previous reading from the input pin // the follow variables are long's because the time, measured in miliseconds, // will quickly become a bigger number than can be stored in an int. long time = 0; // the last time the output pin was toggled long debounce = 200; // the debounce time, increase if the output flickers void setup() { pinMode(inPin, INPUT); pinMode(outPin, OUTPUT); } void loop() { reading = digitalRead(inPin); // if the input just went from LOW and HIGH and we've waited long enough // to ignore any noise on the circuit, toggle the output pin and remember // the time if (reading == HIGH && previous == LOW && millis() - time > debounce) { if (state == HIGH) state = LOW; else state = HIGH; time = millis(); } digitalWrite(outPin, state); previous = reading; }
En el siguiente Sketch el LED se enciende solo mientras presionamos el botton.
Sketch:
const int buttonPin = 2; // the number of the pushbutton pin const int ledPin = 13; // the number of the LED pin // variables will change: int buttonState = 0; // variable for reading the pushbutton status void setup() { // initialize the LED pin as an output: pinMode(ledPin, OUTPUT); // initialize the pushbutton pin as an input: pinMode(buttonPin, INPUT); } void loop(){ // read the state of the pushbutton value: buttonState = digitalRead(buttonPin); // check if the pushbutton is pressed. // if it is, the buttonState is HIGH: if (buttonState == HIGH) { // turn LED on: digitalWrite(ledPin, HIGH); } else { // turn LED off: digitalWrite(ledPin, LOW); } }
Controlando video cropping con un sensor LDR
Haciendo Video Cropping con un fotorresistor y Arduino con PD. Arduino utiliza Standard Firmata y Puredata mi patch de cropping_multiple con arduino_test.
Por VJ. du_art
Patch de Puredata: Cropping-multiple
Arduino y MIDI
En este ejemplo vemos como comunicar Arduino con cualquier software o interfaz que se comunique a traves de MIDI.
Para comunicar via MIDI, Arduino y cualquier programa que reciba eventos MIDI, existen diferentes formas de re-interpretar la información. En este ejemplo vemos una posibilidad que da muy buenos resultados.
Pasos a seguir:
1. En este ejemplo conectamos nuestra placa Arduino via USB al ordenador. Nuestra placa Arduino enviara información al ordenador la cual tendra que interpretar como MIDI. Para esto utilizaremos el Driver IAC de nuestro Mac.
4) haz Doble click en «IAC Driver» para abrirlo, y veras algo así:
5) Para usar el IAC Driver, primero debes encenderlo. Haz Click seleccionando y activando el cuadro que está junto a «dispositivo conectado» o en ingles «device is online».
6) En la lista de Puertos, cada uno es un cable MIDI virtual, por los cuales vas a tener 16 canales para enviar MIDI! Puedes renombrarlos para saber cual es cual. (Para renombrarlos, haz doble-click en el puerto y pon un nuevo nombre).
7) Ahora para poder comunicarnos entre Arduino y nuestro Software MIDI, que puede ser Logic, o Garage Band o Main Stage o el que quieras, necesitaras un software que funcione de puente. Es posible hacerlo con Pure Data o Max/MSP o Processing. En este caso utilizaremos un software mas sencillo que ya esta programado y configurado para esta función. Se llama hairless-midiserial.
8) En el Serial port debes poner el puerto USB donde esta conectada tu placa ARDUINO y en MIDI Out y MIDI In poner los puertos IAC que haz activado anteriormente.
Debes tener en cuenta que a diferencia de otros programas que trabajan con MIDI, Hairless midi serial trabaja con un baud rate de 115200 baudios. Asi que en el Serial.begin de tu Sketch debes poner 115200 baudios de velocidad de sampleo.
9) Copia el siguiente sketch y cargalo a tu placa Arduino. (Ten en cuenta que siempre que cargues un sketch, debe estar «not connected», o apagado el puerto serial del «Hairless midi serial», sino Arduino puede tener conflictos con la carga del programa ya que esta utilizando el mismo puerto USB para cargar y para transmitir datos).
Sketch MIDI_Arduino:
void setup() { // MIDI baud rate para comunicarse con hairless: Serial.begin(115200); } void loop() { // Se comunica con el sistema Hexadecimal traducido a numeros midi. //Toca notas desde C-1 (0x30) hasta C-4 (0x54): for (int note = 0x30; note < 0x54; note ++) { //La nota en el canal 1 (0x90), el valor de la nota (note), con una velocidad media (0x45): noteOn(0x90, note, 0x45); delay(100); //La nota en el canal 1 (0x90), el valor de la nota (note), velocidad de silencio (0x00): noteOn(0x90, note, 0x00); delay(100); } } // Toca una nota MIDI. void noteOn(int cmd, int pitch, int velocity) { Serial.write(cmd); Serial.write(pitch); Serial.write(velocity); }
10) Una vez cargado el Sketch en tu placa Arduino, ya puedes abrir o conectar o encender tu «Hairless midi serial». Vas a ver como Arduino funciona como controlador Master y se comunica con tu software Logic, o Garage Band o Main Stage.
11) Si quieres empezar a componer tu musica en arduino para que la interprete Logic por ejemplo, tienes que tener en cuenta que Arduino interpreta valores Hexadecimales y estos valores son enviados a Logic traducidos como numeros MIDI. Te puedes ayudar con las siguientes Tablas de conversion:
http://www.tonalsoft.com/pub/news/pitch-bend.aspx
Comunicando Multiples Arduinos entre si por los puertos I2C TWI
Las dos placas se comunican a través de los puertos I2C TWI. El Pin Analogo 4 (SDA) y el Pin Analogo 5 (SCL). Con la Libreria Wire de Arduino.
Conexiones:
Sketch Arduino Maestro:
#include <Wire.h> #define BETA_ARDUINO !(defined(ARDUINO) && ARDUINO >= 100) const byte MY_ADDRESS = 25; // me const byte SLAVE_ADDRESS = 42; // slave #42 void setup () { Wire.begin (MY_ADDRESS); // initialize hardware registers etc. } // end of setup void loop() { unsigned int value = 1234; // ie. 0x04 0xD2 Wire.beginTransmission (0); // broadcast to all // OR: Wire.beginTransmission (SLAVE_ADDRESS); // send to a certain one #if BETA_ARDUINO Wire.send (highByte (value)); Wire.send (lowByte (value)); #else Wire.write (highByte (value)); Wire.write (lowByte (value)); #endif byte err = Wire.endTransmission(); // non-zero means error delay (100); // wait 0.1 seconds } // end of loop
Sketch Arduino Esclavo:
#include <Wire.h> #define BETA_ARDUINO !(defined(ARDUINO) && ARDUINO >= 100) const byte MY_ADDRESS = 42; // me const byte LED = 13; // LED is on pin 13 byte ledVal = 0; void receiveEvent (int howMany) { // we are expecting 2 bytes, so check we got them if (howMany == 2) { int result; #if BETA_ARDUINO result = Wire.receive (); result <<= 8; result |= Wire.receive (); #else result = Wire.read (); result <<= 8; result |= Wire.read (); #endif // do something with result here ... // for example, flash the LED digitalWrite(LED, ledVal ^= 1); // flash the LED } // end if 2 bytes were sent to us // throw away any garbage while (Wire.available () > 0) { #if BETA_ARDUINO Wire.receive (); #else Wire.read (); #endif } // end while available } // end of receiveEvent void setup () { Wire.begin (MY_ADDRESS); // initialize hardware registers etc. TWAR = (MY_ADDRESS << 1) | 1; // enable broadcasts to be received Wire.onReceive(receiveEvent); // set up receive handler pinMode(LED, OUTPUT); // for debugging, allow LED to be flashed } // end of setup void loop () { } // end of loop
Comunicando Multiples Arduinos entre si por los puertos RX y TX
Hay que conectar la salida TX (Pin 1) del primer Arduino a la entrada RX (Pin 0) del segundo Arduino y asi sucesivamente en cascada con todos los Arduinos que tengamos.
Ademas conectamos entre si los 5V de todos los Arduinos y entre si los GND de todos los Arduinos para alimentar todas las placas.
Cada Arduino en este ejemplo esta conectado a un led al pin 8 que va en serie a una resistencia de 220 ohm para proteger la placa y el LED, cerrando el circuito en GND.
Sketch:
/* Blink en Cascada Host--->RX(device 0)TX--->RX(device 1)TX--->RX(device 2)TX---> Etc */ const int ledPin = 8; // 13 for standard arduino void setup() { // initialize serial: Serial.begin(9600);
// make the pins outputs: pinMode(ledPin, OUTPUT); digitalWrite( ledPin, LOW ); } void blinkFor( long ms ) { digitalWrite( ledPin, HIGH ); delay( ms ); digitalWrite( ledPin, LOW ); } void loop() { int ch, digit; long d = 0; // wait for something to come in if( Serial.available() == 0 ) return; // read in a character. // If it's within '1' .. '9', set the timer for that number of seconds. ch = Serial.read(); if( ch >='0' && ch <= '9' ) { digit = ch - '0'; if( digit > 0 ) { d = digit * 1000; } } if( ch == 'b' ) { // turn off for 1/4 second, send down echo immediately Serial.write( 'b' ); blinkFor( 250 ); } if( d > 0 ) { // turn on the LED for the requested number of seconds blinkFor( d ); // cascade out our digit, one less than ourselves Serial.write( (digit - 1) + '0' ); } }
Otro ejemplo:
Comunicación de 2 placas Arduino a través de los pines seriales TX y RX para la dimerización de un Led con un potenciometro vía puerto serial.
Sketch Prueba Transmisor:
//Transmisor:
int Value5; int val5;
void setup() { // inicia puerto serial Serial.begin(19200); }
void loop() { // lee el pin analogo 5 con un potenciometro Value5 = analogRead(5);
// remap 0 / 255 de la variable Value5 val5 = map(Value5, 0, 1023, 0, 255);
// envia el valor al puerto serial Serial.write(byte(val5));
}
Sketch Prueba Receptor:
// Receptor byte incomingByte;
void setup() { // inicia el puerto serial Serial.begin(19200);
// declara el pin 11 como OUTPUT - LED pinMode (11, OUTPUT); }
void loop() {
// lee si hay bytes disponibles en el puerto serial if (Serial.available()) {
// da el valor al ‘incomingByte’ variable incomingByte = Serial.read();
// escribe el valor en el pin 11 analogWrite(11, int(incomingByte));
} }
Led RGB
Conectamos el Led RGB con 3 resistencias de 33o ohm y a su vez a los pines digitales 9, 10, 11 y la pata mas larga del LED a tierra.
sketch:
/* RGB LED */ int redPin = 11; int greenPin = 10; int bluePin = 9; void setup() { pinMode(redPin, OUTPUT); pinMode(greenPin, OUTPUT); pinMode(bluePin, OUTPUT); } void loop() { setColor(255, 0, 0); // red delay(1000); setColor(0, 255, 0); // green delay(1000); setColor(0, 0, 255); // blue delay(1000); setColor(255, 255, 0); // yellow delay(1000); setColor(80, 0, 80); // purple delay(1000); setColor(0, 255, 255); // aqua delay(1000); } void setColor(int red, int green, int blue) { analogWrite(redPin, red); analogWrite(greenPin, green); analogWrite(bluePin, blue); }
Sensor de humedad
Para el desarrollo de sistemas de control de riego automatas podemos utilizar un sensor de humedad conectado a Arduino que controle una valvula solenoide.
Sketch:
// Sensor de Humedad // Conectamos el sensor de la siguiente forma: // GND -> GND // VCC -> 5V // DAT -> A0 // Descripción de valores del Sensor // 0 -300 Seco // 300-700 Húmedo // 700-950 En Agua int Valor; void setup(){ Serial.begin(9600); } void loop(){ Serial.print("Sensor de Humedad valor:"); Valor = analogRead(0); Serial.print(Valor); if (Valor <= 300) Serial.println(" Seco, necesitas regar"); if ((Valor > 300) and (Valor <= 700)) Serial.println(" Humedo, no regar"); if (Valor > 700) Serial.println(" Encharcado"); delay(1000); }
SENSOR DE HUMEDAD Y TEMPERATURA – DHT y RHT03
Las conexiones son muy sencillas: Vcc a +5V, GND a GND y Data a una entrada digital, en nuestro caso la entrada 2.
Para realizar el programa de Arduino nos vamos a apoyar en la librería DHT de Adafruit para descargar directamente hacer click aquí.
Una vez descargada tendremos que guardar los archivos “DHT.cpp” y “DHT.h” en una carpeta que llamaremos DHT dentro de “/Documents/Arduino/libraries/” en MAC o “My Documents\Arduino\libraries\” en Windows.
El código para leer el sensor con Arduino es el siguiente:
Sketch:
#include "DHT.h" #define DHTPIN 2 // Pin digital // Escogemos segun el tipo de sensor que usamos //#define DHTTYPE DHT11 // DHT 11 #define DHTTYPE DHT22 // DHT 22 (AM2302) //#define DHTTYPE DHT21 // DHT 21 (AM2301) DHT dht(DHTPIN, DHTTYPE); void setup() { Serial.begin(9600); Serial.println("Lee Sensor RHT03!"); dht.begin(); } void loop() { // espera 2 segundos entre una medida y otra. delay(2000); // Cada lectura de humedad y temperatura tarda 250 millisegundos! float h = dht.readHumidity(); // Lee la temperatura en Celsius float t = dht.readTemperature(); // Lee la temperatura en Fahrenheit float f = dht.readTemperature(true); // verifica si hay error en la lectura (intenta de nuevo). if (isnan(h) || isnan(t) || isnan(f)) { Serial.println("Error en la lectura del sensor!"); return; } // Computa el indice de calor // Envia la temperatura en Fahrenheit! float hi = dht.computeHeatIndex(f, h); Serial.print("Humedad: "); Serial.print(h); Serial.print(" %\t"); Serial.print("Temperatura: "); Serial.print(t); Serial.print(" *C "); Serial.print(f); Serial.print(" *F\t"); Serial.print("indice de Calor: "); Serial.print(hi); Serial.println(" *F"); }
YF-S201 Contador de agua / Sensor de Efecto Campo (Hall) – Negro
El sensor de flujo es un dispositivo que, instalado en línea con una tubería, permite determinar cuándo está circulando un líquido o un gas. Se compone de un cuerpo de válvula de plástico, un rotor de agua, y un sensor de efecto Hall. Su velocidad cambia con la diferente tasa de flujo. El sensor de efecto Hall emite la señal de impulso que corresponde.
El sensor de efecto Hall o simplemente sensor Hall o sonda Hall (denominado según Edwin Herbert Hall) se sirve del efecto Hall para la medición de campos magnéticos o corrientes o para la determinación de la posición.
Si fluye corriente por un sensor Hall y se aproxima a un campo magnético que fluye en dirección vertical al sensor, entonces el sensor crea un voltaje saliente proporcional al producto de la fuerza del campo magnético y de la corriente. Si se conoce el valor de la corriente, entonces se puede calcular la fuerza del campo magnético; si se crea el campo magnético por medio de corriente que circula por una bobina o un conductor, entonces se puede medir el valor de la corriente en el conductor o bobina.
PAra hacer una medicion del sensor de flujo de agua con Arduino, conectamos el cable GND (Negro) a GND , Vss (Rojo) a 5V y la señal (amarillo) al pin digital 2, este último con una resistencia en serie de 10kohm.
Sketch:
volatile int NbTopsFan; //mide el valor ascendente de la señal int Calc; int hallsensor = 2; //El Pin Digital que va al sensor void rpm () //la funcion que interrumpe { NbTopsFan++; //La funcion que mide el valor acendente de la señal del sensor de efecto hall } void setup() // { pinMode(hallsensor, INPUT); //inicializa el pin 2 como entrada Serial.begin(9600); //inicializa la tasa de lectura attachInterrupt(0, rpm, RISING); //el interrupt esta attached } void loop () { NbTopsFan = 0; //Pone en 0 el NbTops para comenzar a calcular sei(); //Activa los interrupts delay (1000); //Espera 1 segundo cli(); //desactiva los interrupts Calc = (NbTopsFan * 60 / 7.5); //(Pulso de frequencia x 60) / 7.5Q, = tasa de flujo en L/hora Serial.print (Calc, DEC); //Imprime el numero calculado anteriormente Serial.print (" L/hora\r\n"); //imprime "L/hora" y devuelve una nueva linea }
AttachInterrupt
Una de las formas comunes de interactuar con cosas que suceden «fuera» de nuestro control es por ejemplo monitorear el estado de las líneas. Dentro de nuestro «loop» principal podemos leer continuamente el valor de una línea de entrada y ejecutar por medio de una condicional un código especifico en caso de que detectemos un cambio.
El problema de este método es que el código se vuelve sumamente complejo cuando tenemos que monitorear muchas cosas o cuando nuestro loop principal simplemente tiene que hacer cosas que toman demasiado tiempo.
En esta entrada vamos a comprender como utilizar las interrupciones externas del Arduino. Esto nos será de mucha utilidad para interactuar ya sea con el usuario o con otros dispositivos de hardware que emitan «señales» que nos permitan saber que un evento ha sucedido.
Una cosa a la vez…
¿Como manejar eventos externos?
NOTA
Se debe declarar la variable que vamos a modificar con attachinterrupt como volatile
Syntax
attachInterrupt(interrupt, funcion, modo)
modos:
- LOW activa el interrupt cuando el pin esta en low,
- CHANGE activa el interrupt cuando el pin cambia de valor
- RISING activa el interrupt cuando el pin cambia de low a high,
- FALLING activa el interrupt cuando el pin cambia de high a low.
Libreria de tiempo
Hay una libreria para programar tiempo, que la podemos descargar en este link:
descargamos la libreria y la poenmos dentro de “/Documents/Arduino/libraries/” en MAC o “My Documents\Arduino\libraries\” en Windows.
Reiniciamos arduino y cargamos el siguiente sketch:
Sketch Time_Alarma.pde
/* * Este ejemplo llama la funcion alarma a las 8:30 y a las 17:45 * y simultaneamente enciende la luz durante la noche y la apaga en la mañana * Otra alarma que solo funciona los Sabados a las 8:29:30 * * un Timer es llamado cada 15 segundos * Otro timer es llamado solo una vez a los 10 segundos iniciar programa * * El reloj esta ajustado con fecha y hora Sabado Jan 1 2011 8:29 am */ #include <Time.h> #include <TimeAlarms.h> int led=13; void setup() { Serial.begin(9600); pinMode(led, OUTPUT); setTime(8,29,0,1,1,11); // El reloj lo pone en Sabado a las 8:29:00am de Enero 1 2011 //Crea alarmas Alarm.alarmRepeat(8,30,0, amanecer); // 8:30 todos los dias Alarm.alarmRepeat(17,45,0,tarde); // 17:45 todos los dias Alarm.alarmRepeat(dowSaturday,8,29,30,semanal); // 8:30:30 los Sabados Alarm.timerRepeat(15, Repite); // Timer es llamado cada 15 segundos Alarm.timerOnce(10, solounavez); // Timer es llamado solo una vez a los 10 segundos } void loop(){ relojDigital(); Alarm.delay(1000); //Espera 1 segundo para iniciar el reloj } // Funciones de llamado de alarma void amanecer(){ Serial.println("Alarma: - apaga luz"); digitalWrite(led,LOW); } void tarde(){ Serial.println("Alarma: - enciende luz"); digitalWrite(led,HIGH); // delay(5000); El tiempo no se detiene gracias a la funcion //digitalWrite(led,LOW); } void semanal(){ Serial.println("Alarma: - Hoy es Sabado"); } //void ExplicitAlarm(){ //Serial.println("Alarma: - hora y fecha dada"); //} void Repite(){ Serial.println("Reporta cada 15 segundos"); } void solounavez(){ Serial.println("Solo una vez a los 10 segundos"); } void relojDigital() { // Imprime la hora Serial.print(hour()); imprimeDigitos(minute()); imprimeDigitos(second()); Serial.println(); } void imprimeDigitos(int digitos) { Serial.print(":"); if(digitos < 10) Serial.print('0'); Serial.print(digitos); }
Abrimos el monitor Serial y vemos la impresion del reloj y de las alarmas que hemos programado.
Motor servo Rotación continua
Existen dos tipos de servo motores: normales y de giro continuo. Los servos normales se controlan con PWM y giran a una posición fija dependiendo en el ancho de pulso. Están limitados en movimiento a un rango entre 0 y 180 grados. Los servos de giro continuo se mantienen girando mientras que reciban pulsos PWM. Los más comunes, fabricados por Parallax, giran en una dirección cuando se les proporcionan pulsos por encima de 1500 microsegundos de ancho y en la contraria al darles un ancho menor al citado.
Sketch Motor servo Rotación continua
int motorPin = 4; int boton = 12; int pulsador = 0; /* pin digital para el servo */ void setup(){ pinMode(motorPin,OUTPUT); pinMode(boton,INPUT); /* declara el pin digital como salida */ Serial.begin(9600); } void loop(){ pulsador=digitalRead(boton); if (pulsador == 0){ for(int i = 0; i < 100; i++);{ /* repite 100 veces para que veamos al servo moverse */ digitalWrite(motorPin,HIGH); delayMicroseconds(1850); /* el delay aqui mostrado establece un ancho de pulso */ digitalWrite(motorPin,LOW); delayMicroseconds(1850); } } if(pulsador == 1){ /* for(int i = 0; i < 100; i++);{ digitalWrite(motorPin,HIGH); delayMicroseconds(100); digitalWrite(motorPin,LOW); delayMicroseconds(100); } */ } Serial.println(pulsador); }
Capacitores
Para este ejemplo tenemos que instalar las librerias correspondientes:
-CapacitiveSensor.h
-EducationShield.h
y despues hay que cargarlas con:
#include <CapacitiveSensor.h> #include <EducationShield.h>
Sigue el led
Material:
- 1 Buzzer
- 3 LEDs
- 3 resistencias de 220 ohm
- 3 resistencias de 1Mohm
- 6 cables negros
- 12 cables de diferentes colores (3 largos)
- papel de aluminio o cualquier objeto de material conductor
- cinta adhesiva
- 1 breadboard
las 3 resistencias de 1Mohm se conectan a los pads de materiales conductores como se muestra en la imagen.
van conectados a los pines (3,4,5) cada uno y por el otro extremo al pin 2 todos.
El buzzer va conectado al pin 8 y a GND.
los leds van conectados a los pines (9, 10, 11) cada uno con una resistencia de 220 o 330 ohm en serie y por el otro extremo a GND.
Sketch Capacitores sigue leds:
/* Sigue el led con capacitores */ #include <CapacitiveSensor.h> #include <EducationShield.h> #include <Servo.h> #include "pitches.h" //Define los 3 LEDs int ledPins[] = {9, 10, 11}; int pinCount = 3; VUMeter LEDs; //hay 3 pads para activar CapacitiveSwitch pad[3]; //tienes 500 milisegundos para presionar el pad int reactTime = 500; // el buzzer conectado al pin digital 8 Melody piezo = Melody(8); void setup(){ LEDs.config(pinCount, ledPins); LEDs.begin(); //Configuracion de pads pad[0] = CapacitiveSwitch(2,3); pad[1] = CapacitiveSwitch(2,4); pad[2] = CapacitiveSwitch(2,5); pad[0].config(100); pad[1].config(100); pad[2].config(100); } void loop(){ //Espera un tiempo random antes de comenzar delay(random(50, 2000)); //escoge un objetivo aleatorio entre los 3 pads int target = random(0, 3); //enciende el led correspondiente LEDs.on(target); //si el pad corresponde al led if(pad[target].pressed(reactTime)){ LEDs.off(target); //toca musica ganadora int melody[] = { NOTE_GS4, NOTE_C5}; int noteDurations[] = { 8, 8}; int numberOfNotes = 2; piezo.play(numberOfNotes, melody, noteDurations, 1); } else{ //Sino, activa la musica de la funcion gameOver() gameOver(); } } void gameOver(){ //Enciende todos los leds LEDs.fill(pinCount); //Toca la melodia perdedora int melody[] = { NOTE_E2, NOTE_C2}; int noteDurations[] = { 2, 1}; int numberOfNotes = 2; piezo.play(numberOfNotes, melody, noteDurations, 1); LEDs.blinkAll(100, 10); LEDs.fill(0); //apaga todos los leds }
Instrumento musical con capacitores
Aquí tenemos un instrumento musical con Capacitores. Con el mismo circuito anterior cargamos el siguiente sketch:
/* Sigue el led con capacitores */ #include <CapacitiveSensor.h> #include <EducationShield.h> #include <Servo.h> #include "pitches.h" //hay 3 pads para activar CapacitiveSwitch pad[3]; //Define los 3 LEDs int led1=9; int led2=10; int led3=11; // el buzzer conectado al pin digital 8 void setup(){ //Configuracion de pads pad[0] = CapacitiveSwitch(2,3); pad[1] = CapacitiveSwitch(2,4); pad[2] = CapacitiveSwitch(2,5); pad[0].config(100); pad[1].config(100); pad[2].config(100); pinMode(led1, OUTPUT); pinMode(led2, OUTPUT); pinMode(led3, OUTPUT); } void loop(){ //enciende el led correspondiente // led1 nota C5 if(pad[0].pressed(600)){ digitalWrite(led1, HIGH); delay(200); digitalWrite(led1,LOW); tone (8,262,200); } // led2 nota D5 if(pad[1].pressed(600)){ digitalWrite(led2, HIGH); delay(200); digitalWrite(led2,LOW); tone (8,294,200); } // led3 nota E5 if(pad[2].pressed(600)){ digitalWrite(led3, HIGH); delay(200); digitalWrite(led3,LOW); tone (8,330,200); } }
Vemos que utilizamos distintas maneras de escribir notas o encender los LED´s con o sin la libreria EducationShield pero el resultado es igual!
Knock knock
Aquí tenemos dos Zumbadores o Buzzers que funcionan como transductores, uno hace la función de altavoz y el otro la función de micrófono. Al colocar una resistencia de 1Mohm en paralelo con el circuito del micrófono entre tierra y el Pin analógico A0 nuestro sensor piezoeléctrico es bastante sensible y percibe vibraciones en solido permitiendo captar triggers de golpes. El otro Zumbador lo usamos como un altavoz conectado directamente al pin digital 8 y a tierra cerrando el circuito como lo muestra la foto siguiente: En el Siguiente sketch Arduino Aprende el ritmo que haces y lo repite! El Código que cargamos es este:
Sketch Knock Knock
#include <EducationShield.h> #include <Servo.h> //El numero de golpes que pueden ser grabados #define MAX_KNOCKS 30 PiezoKnockSensor sensor=PiezoKnockSensor(A0); int speaker = 8; //una matriz para grabar el patron ritmico long timer[MAX_KNOCKS]; //si comienza a grabar boolean started; //calcula si haz terminado el patron long timeoutBase; //si dejas de golpear por un tiempo determinado entonces //para de grabar long timeout=2000; //va trackeando el numero de golpes que vas haciendo int currentKnock; void setup(){ //define el umbral y el tiempo de activacion para activar el sensor //el umbral define lo fuerte que golpeas, //el tiempo de activacion define el tiempo entre golpes y si haz terminado //para evitar falsos golpes, pero tambien limita la velocidad de los golpes. sensor.config(40,80); //inicializa los valores started=false; timeoutBase=0; currentKnock=0; clearArray(); } void loop(){ //El sensor Knock espera un tiempo corto de tiempo si detecta un golpe //y despues comienza. if(sensor.knocked(10)){ //si hay un primer golpe comienza a grabar if(!started){ started=true; } long currentTime=millis(); //resetea el "timeout" timeoutBase=currentTime; //Guarda el tiempo en milisegundos que han //pasado desde el ultimo golpe timer[currentKnock]=currentTime; currentKnock++; } if(started){ //si la grabacion comienza y paras //de golpear mas del tiempo de "timeout", entonces //para de grabar y lo reproduce. if(millis()-timeoutBase>timeout){ playback(); //reinicia los parametros started=false; clearArray(); currentKnock=0; } } } void clearArray(){ //limpia los valores de la matriz for(int i=0;i<MAX_KNOCKS;i++){ timer[i]=0; } } void playback(){ //reproduce el patron ritmico por el otro piezo
for(int i=0;timer[i]!=0;i++){ //hace un tone de 440 hz durante 30 milisegundos tone(speaker, 440, 30); if(timer[i+1]){ //espera la misma cantidad de tiempo que fue detectado //entre dos golpes delay(timer[i+1]-timer[i]); } } }
Pantalla de led virtual
Conectamos 5 LED´s del pin digital 2 al pin 6 con resistencias en serie de 220ohm o 330ohm.
Circuito:
Cargamos el siguiente código a la placa:
Sketch Pantalla de Led virtual:
#include <EducationShield.h> #include <Servo.h> /* Aqui hacemos una matriz de LED´ que corresponde a los pines digitales del 2 al 6. */ int ledPins[] = {2, 3, 4, 5, 6}; int pinCount = 5; VUMeter vuMeter; int rowCount = 0; // Guarda las filas int rowLength = 22; // El ancho del mensaje, copia este mensaje a la matriz int delayTime = 9; //tiempo que toma para mostrar la fila en milisegundos // si el mensaje es 0 devuelve LOW y si es 1 devuelve HIGH boolean message[5][22]={ // H H H H O O O O L L L L A A A A {0,1,0,0,1,0,0,1,1,0,0,1,0,0,0,0,0,1,1,0,0,0}, {0,1,0,0,1,0,1,0,0,1,0,1,0,0,0,0,1,0,0,1,0,0}, {0,1,1,1,1,0,1,0,0,1,0,1,0,0,0,0,1,1,1,1,0,0}, {0,1,0,0,1,0,1,0,0,1,0,1,0,0,0,0,1,0,0,1,0,0}, {0,1,0,0,1,0,0,1,1,0,0,1,1,1,1,0,1,0,0,1,0,0} }; void setup(){ // aqui se configuran los pines del 2 al 6 (puede modificarse) vuMeter.config(pinCount, ledPins); vuMeter.begin(); //hace el mismo pinMode, configura los LEDs como outputs } void loop(){ // si toda la matriz se ha dibujado if(rowCount == rowLength) { rowCount = 0; // resetea el conteo de la fila } else { // muestra el mensaje for (int i = 0; i < pinCount; i++) { // chequea si el mensaje es High if (message[i][rowCount] == 1) { vuMeter.on(i); } else { vuMeter.off(i); } } rowCount++; } delay(delayTime);//este es el delay por fila }
IMU Digital Combo Board – 6 Degrees of Freedom ITG3200/ADXL345
IMU 6DOF y Processing
sketch:
//////////////////////////////////////////////////////////// // Arduino firmware for use with FreeSixCube processing example //////////////////////////////////////////////////////////// #include <FreeSixIMU.h> #include <FIMU_ADXL345.h> #include <FIMU_ITG3200.h> #define DEBUG #ifdef DEBUG #include "DebugUtils.h" #endif #include "CommunicationUtils.h" #include "FreeSixIMU.h" #include <Wire.h> float q[4]; //hold q values // Set the FreeIMU object FreeSixIMU my3IMU = FreeSixIMU(); void setup() { Serial.begin(115200); Wire.begin(); delay(5); my3IMU.init(); delay(5); } void loop() { my3IMU.getQ(q); serialPrintFloatArr(q, 4); Serial.println(""); //line break delay(60); }
Processing:
Sketch:
import processing.serial.*; Serial myPort; // Create object from Serial class final String serialPort = "/dev/tty.usbmodem411"; // replace this with your serial port. On windows you will need something like "COM1". float [] q = new float [4]; float [] hq = null; float [] Euler = new float [3]; // psi, theta, phi int lf = 10; // 10 is '\n' in ASCII byte[] inBuffer = new byte[22]; // this is the number of chars on each line from the Arduino (including /r/n) PFont font; final int VIEW_SIZE_X = 800, VIEW_SIZE_Y = 600; void setup() { size(VIEW_SIZE_X, VIEW_SIZE_Y, P3D); myPort = new Serial(this, serialPort, 115200); font = createFont("Courier", 32); /* float [] axis = new float[3]; axis[0] = 0.0; axis[1] = 0.0; axis[2] = 1.0; float angle = PI/2.0; hq = quatAxisAngle(axis, angle); hq = new float[4]; hq[0] = 0.0; hq[1] = 0.0; hq[2] = 0.0; hq[3] = 1.0; */ delay(100); myPort.clear(); myPort.write("1"); } float decodeFloat(String inString) { byte [] inData = new byte[4]; if (inString.length() == 8) { inData[0] = (byte) unhex(inString.substring(0, 2)); inData[1] = (byte) unhex(inString.substring(2, 4)); inData[2] = (byte) unhex(inString.substring(4, 6)); inData[3] = (byte) unhex(inString.substring(6, 8)); } int intbits = (inData[3] << 24) | ((inData[2] & 0xff) << 16) | ((inData[1] & 0xff) << 8) | (inData[0] & 0xff); return Float.intBitsToFloat(intbits); } void readQ() { if (myPort.available() >= 18) { String inputString = myPort.readStringUntil('\n'); //print(inputString); if (inputString != null && inputString.length() > 0) { String [] inputStringArr = split(inputString, ","); if (inputStringArr.length >= 5) { // q1,q2,q3,q4,\r\n so we have 5 elements q[0] = decodeFloat(inputStringArr[0]); q[1] = decodeFloat(inputStringArr[1]); q[2] = decodeFloat(inputStringArr[2]); q[3] = decodeFloat(inputStringArr[3]); } } } } void buildBoxShape() { //box(60, 10, 40); noStroke(); beginShape(QUADS); //Z+ (to the drawing area) fill(#00ff00); vertex(-30, -5, 20); vertex(30, -5, 20); vertex(30, 5, 20); vertex(-30, 5, 20); //Z- fill(#0000ff); vertex(-30, -5, -20); vertex(30, -5, -20); vertex(30, 5, -20); vertex(-30, 5, -20); //X- fill(#ff0000); vertex(-30, -5, -20); vertex(-30, -5, 20); vertex(-30, 5, 20); vertex(-30, 5, -20); //X+ fill(#ffff00); vertex(30, -5, -20); vertex(30, -5, 20); vertex(30, 5, 20); vertex(30, 5, -20); //Y- fill(#ff00ff); vertex(-30, -5, -20); vertex(30, -5, -20); vertex(30, -5, 20); vertex(-30, -5, 20); //Y+ fill(#00ffff); vertex(-30, 5, -20); vertex(30, 5, -20); vertex(30, 5, 20); vertex(-30, 5, 20); endShape(); } void drawCube() { pushMatrix(); translate(VIEW_SIZE_X/2, VIEW_SIZE_Y/2 + 50, 0); scale(5, 5, 5); // a demonstration of the following is at // http://www.varesano.net/blog/fabio/ahrs-sensor-fusion-orientation-filter-3d-graphical-rotating-cube rotateZ(-Euler[2]); rotateX(-Euler[1]); rotateY(-Euler[0]); buildBoxShape(); popMatrix(); } void draw() { background(#000000); fill(#ffffff); readQ(); if (hq != null) { // use home quaternion quaternionToEuler(quatProd(hq, q), Euler); text("Disable home position by pressing \"n\"", 20, VIEW_SIZE_Y - 30); } else { quaternionToEuler(q, Euler); text("Point FreeIMU's X axis to your monitor then press \"h\"", 20, VIEW_SIZE_Y - 30); } textFont(font, 20); textAlign(LEFT, TOP); text("Q:\n" + q[0] + "\n" + q[1] + "\n" + q[2] + "\n" + q[3], 20, 20); text("Euler Angles:\nYaw (psi) : " + degrees(Euler[0]) + "\nPitch (theta): " + degrees(Euler[1]) + "\nRoll (phi) : " + degrees(Euler[2]), 200, 20); drawCube(); } void keyPressed() { if (key == 'h') { println("pressed h"); // set hq the home quaternion as the quatnion conjugate coming from the sensor fusion hq = quatConjugate(q); } else if (key == 'n') { println("pressed n"); hq = null; } } // See Sebastian O.H. Madwick report // "An efficient orientation filter for inertial and intertial/magnetic sensor arrays" Chapter 2 Quaternion representation void quaternionToEuler(float [] q, float [] euler) { euler[0] = atan2(2 * q[1] * q[2] - 2 * q[0] * q[3], 2 * q[0]*q[0] + 2 * q[1] * q[1] - 1); // psi euler[1] = -asin(2 * q[1] * q[3] + 2 * q[0] * q[2]); // theta euler[2] = atan2(2 * q[2] * q[3] - 2 * q[0] * q[1], 2 * q[0] * q[0] + 2 * q[3] * q[3] - 1); // phi } float [] quatProd(float [] a, float [] b) { float [] q = new float[4]; q[0] = a[0] * b[0] - a[1] * b[1] - a[2] * b[2] - a[3] * b[3]; q[1] = a[0] * b[1] + a[1] * b[0] + a[2] * b[3] - a[3] * b[2]; q[2] = a[0] * b[2] - a[1] * b[3] + a[2] * b[0] + a[3] * b[1]; q[3] = a[0] * b[3] + a[1] * b[2] - a[2] * b[1] + a[3] * b[0]; return q; } // returns a quaternion from an axis angle representation float [] quatAxisAngle(float [] axis, float angle) { float [] q = new float[4]; float halfAngle = angle / 2.0; float sinHalfAngle = sin(halfAngle); q[0] = cos(halfAngle); q[1] = -axis[0] * sinHalfAngle; q[2] = -axis[1] * sinHalfAngle; q[3] = -axis[2] * sinHalfAngle; return q; } // return the quaternion conjugate of quat float [] quatConjugate(float [] quat) { float [] conj = new float[4]; conj[0] = quat[0]; conj[1] = -quat[1]; conj[2] = -quat[2]; conj[3] = -quat[3]; return conj; }
Para mas información mira el siguiente link:
http://bildr.org/2012/03/stable-orientation-digital-imu-6dof-arduino/
The BYTE keyword is no longer suppoted
La palabra clave BYTE ya no está soportada
Añadir multiples entradas y salidas a tu Arduino.
Arduino Mux:
La puedes comprar en el siguiente link: