Sunteți pe pagina 1din 28

Arduino

Contingut
• 1 Arduino
♦ 1.1 Códigos ejemplo
◊ 1.1.1 Pins E/S
⋅ 1.1.1.1 Salidas básicas Arduino
⋅ 1.1.1.2 Salidas básicas Arduino con ATmega328
⋅ 1.1.1.3 Entradas/Salidas básicas Arduino
⋅ 1.1.1.4 Entradas/Salidas básicas Arduino con ATmega328
◊ 1.1.2 I2C
⋅ 1.1.2.1 I2C: Dispositivo Maestro envía texto y pide respuesta a dos dispositivos esclavos
⋅ 1.1.2.2 I2C: Dispositivo Esclavo recibe texto y responde cuando el máster pide respuesta
◊ 1.1.3 SPI
⋅ 1.1.3.1 SPI: Maestro envía datos por SPI
⋅ 1.1.3.2 SPI: Esclavo recibe datos por SPI e interrupción (y AVR)
⋅ 1.1.3.3 SPI: Esclavo recibe datos por SPI e interrupción y responde un nuevo byte (y AVR)
◊ 1.1.4 Serie USART
⋅ 1.1.4.1 Serie: Modifica el valor de los pins 12 y 13 mediante puerto serie
⋅ 1.1.4.2 Serie: Recibe un carácter por encuesta (polling), incrementa su valor y lo envía
⋅ 1.1.4.3 Serie: (AVR) Recibe un carácter por encuesta (polling), incrementa su valor y lo envía
⋅ 1.1.4.4 Serie: (AVR) Recibe un carácter por interrupción, incrementa su valor y lo envía
⋅ 1.1.4.5 Serie: (AVR) Recibe string por el puerto serie, y lo vuelve a enviar añadiendo CRLF (por polling)
⋅ 1.1.4.6 Serie: (AVR) Recibe string por el puerto serie por interrupción, añade los caracteres a un buffer, y lee el
buffer por partes indicando el número de bytes a leer
◊ 1.1.5 TIMERS
⋅ 1.1.5.1 Timers: (AVR) Utiliza la interrupción del Timer 1 (16bits) para parpadear un Led cada 5 segundos
⋅ 1.1.5.2 Timers: (AVR) Utiliza la interrupción del Timer 2 (8bits) para parpadear un Led cada 1 segundo
◊ 1.1.6 PWM
⋅ 1.1.6.1 Fast PWM mediante analogWrite (Timer2)
⋅ 1.1.6.2 Fast PWM mediante registros ATMega328 (Timer2)
⋅ 1.1.6.3 Fast PWM mediante analogWrite (Timer0)
⋅ 1.1.6.4 Fast PWM mediante registros ATMega328 (Timer0)
⋅ 1.1.6.5 Fast PWM mediante registros ATMega328 (Timer1)
◊ 1.1.7 Contadores ATmega
⋅ 1.1.7.1 Activa un contador por registros. Cuenta hasta 65535 (0xFFFF)
⋅ 1.1.7.2 Activa un contador por registros. Cuenta hasta 15 (0x000F)
⋅ 1.1.7.3 Activa un contador por registros y activa interrupción al sobrepasar 65535 (0xFFFF)
⋅ 1.1.7.4 Activa un contador por registros y activa interrupción al sobrepasar el valor de OCR5A
◊ 1.1.8 Entrada Analógica ADC
⋅ 1.1.8.1 Lee el valor de una entrada analógica y parpadea LED (Arduino)
⋅ 1.1.8.2 Lee el valor de una entrada analógica y parpadea LED (Registros ATmega)
◊ 1.1.9 Guardar datos EEPROM
⋅ 1.1.9.1 Guardar nuevo valor de retardo de delay (Arduino)
◊ 1.1.10 Modo Sleep
⋅ 1.1.10.1 Intermitente de 1 segundo en modo Sleep e interrupción Timer 2 mediante instrucciones Arduino
⋅ 1.1.10.2 Intermitente de 1 segundo en modo Sleep e interrupción Timer 2 mediante AVR y ASM ATmega328
⋅ 1.1.10.3 Modo Sleep y despertar por interrupción 0
♦ 1.2 Funciones y ejemplos
◊ 1.2.1 Strings
⋅ 1.2.1.1 Separa String en dos partes separadas por un carácter
⋅ 1.2.1.2 Separa String en tres partes separadas por dos caracteres
⋅ 1.2.1.3 Separa busca un String dentro de otro String
⋅ 1.2.1.4 Devuelve el tamaño de un string de tipo char array
⋅ 1.2.1.5 Concatena varios char arrays
◊ 1.2.2 Serie
⋅ 1.2.2.1 Lee caracteres del puerto serie 0 hasta que encuentra el carácter indicado
⋅ 1.2.2.2 Lee la cantidad de caracteres indicados del puerto serie 0
⋅ 1.2.2.3 Escribe un string de array de chars por puerto serie 0
♦ 1.3 Hardware
◊ 1.3.1 LCD
⋅ 1.3.1.1 1602 (2x16)
◊ 1.3.2 Ultrasonidos
⋅ 1.3.2.1 HC-SR04
◊ 1.3.3 Distancia Infrarrojos
⋅ 1.3.3.1 Sharp GP2DY120X y similares
◊ 1.3.4 Configurar módulo Bluetooth HC-05 o similar con Arduino
⋅ 1.3.4.1 Módulo Bluetooth v.4 BLE HM-10
⋅ 1.3.4.2 Módulo Bluetooth v.4 BLE HC-10 / AT-09/ CC41A / MLT-BT05
◊ 1.3.5 Módulo WIFI ESP-01 basado en ESP8266
⋅ 1.3.5.1 Actualizar firmware ESP-01
♦ 1.4 Enlaces Arduino

Arduino
Datasheet ATMega328

Códigos ejemplo
Pins E/S
Salidas básicas Arduino

Corresponde al programa blink.ino del ejemplo en que se configura como salida el pin 13 de la placa Arduino, y se cambia el valor de la salida
alternativamente cada segundo. El pin 13 esta conectado a un LED.
void setup() {
pinMode(13,OUTPUT); // Configura el pin 13 como salida. Si no se configura no funcionaria como salida
}
void loop() {
digitalWrite(13, HIGH); // pone el led a 1
delay(1000); // espera un segundo
digitalWrite(13, LOW); // pone el led a 0
delay(1000); // espera un segundo
}

Salidas básicas Arduino con ATmega328

Equivalente al anterior pero modificando los registro del microcontrolador ATmega. (Registros en C:\Program Files
(x86)\Arduino\hardware\tools\avr\avr\include\avr\iom328p.h)
void setup() {
DDRB |= (1<<DDB5); // Pone a 1 el bit DDB5 del registro DDRB, ya que el pin 13 esta conectado a la salida PB5 del ATmega328 (PB7 en ATmega25
}
void loop() {
PORTB |= (1<<PORTB5); // Pone a uno el bit PORTB5 del registro PORTB que corresponde a la salida PB5
delay(1000); // espera un segundo
PORTB &= (0xFE<<PORTB5); // Pone a cero la salida PB5
delay(1000); // espera un segundo
}

Entradas/Salidas básicas Arduino

Lee el estado del pin 7, y lo escribe en el pin 13 que esta conectado a un led.
int a;
void setup() {
pinMode(13,OUTPUT); //Configura pin 13 como salida
pinMode(7,INPUT); //Configura pin 7 como entrada
}
void loop() {
a=digitalRead(7); //Lee el pin 7
digitalWrite(13, a); //Escribe en el pin 13 la lectura del pin 7
}

Entradas/Salidas básicas Arduino con ATmega328

Equivalente al anterior pero modificando los registro del microcontrolador ATmega.


int a;
void setup() {
DDRB |= (1<<DDB5); // Configura como salida el pin PB5 que corresponde al pin 13 de la placa
DDRD &= (0xFE<<DDD7); // Configura como entrada el pin PD7 que corresponde al pin 7 de la placa
}
void loop() {
a=PIND & (1<<PIND7); // Lee el bit PIND7 del registro PIND que corresponde al pin PD7
if (a==0){ // Si el resultado de la lectura es 0, es que
PORTB &= (0<<PORTB5); // Pone a cero el bit PORTB5 del registro PORTB que corresponde a la salida PB5
} // si se ha leido 0 en el pin PD7
else{
PORTB |= (0xFE<<PORTB5); // sino se ha leido 0, es que es uno y pone a uno el pin PB5
}
}

I2C
I2C: Dispositivo Maestro envía texto y pide respuesta a dos dispositivos esclavos
//Placa Arduino UNO y NANO
//I2C SDA: AN4, SCL:AN5
#include <Wire.h>
String rep="";
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("Inici Mestre");
}
void loop() {
Wire.beginTransmission(0b0100); // transmite al dispositivo esclavo 4
Wire.write("M4"); //Envia "M4" al dispositivo esclavo 4 la sin esperar respuesta
Wire.endTransmission(); // para transmisión (no necesario si se sigue transmitiendo al mismo dispositivo)
delay(1000);
rep="";
Wire.requestFrom(4,6); //Solicita 6 bytes al dispositivo esclavo 4
while (Wire.available()) // espera a recibir todos los caracteres
{
char c = Wire.read(); // recibe byte a byte como carácter
rep+=c;
}
Serial.println(rep);
delay(200);
Wire.beginTransmission(8); // transmite al dispositivo esclavo 8
Wire.write("M8"); //Envía "M8" al dispositivo esclavo 8 sin esperar respuesta
Wire.endTransmission(); // para transmisión
delay(1000);
rep="";
Wire.requestFrom(8,6); //Solicita 6 bytes al dispositivo esclavo 8
while (Wire.available()) // Mientra haya datos a recibir
{
char c = Wire.read(); // recibe un byte como carácter
rep+=c;
}
Serial.println(rep);
delay(200);
}
I2C: Dispositivo Esclavo recibe texto y responde cuando el máster pide respuesta
//Placa Arduino UNO y NANO
//I2C SDA: AN4, SCL:AN5
#include <Wire.h>
String rebut="";
int incrM4=0;
void setup() {
Wire.begin(4); // Configura dirección 4 como esclavo I2C
Wire.onReceive(recibeEvento); // configura el evento de recibir texto
Wire.onRequest(requestEvento); // configura el evento de pedir respuesta
Serial.begin(9600);
Serial.println("Inici Esclau 4");
}
void loop() {
// No hace nada en el loop(). Solo por eventos
}
void recibeEvento(int byterecibe)
{ // Lee información recibida del maestro sin enviar respuesta y comprueba si es "M4"
rebut="";
while (Wire.available()>=1)
{
char c=Wire.read();
rebut +=c;
}
if (rebut=="M4")
{
Serial.print(rebut);
Serial.print(" ");
Serial.println(incrM4);
incrM4++;
}
}
void requestEvento()
{ //Responde al requerimiento de respuesta del máster de 6 bytes
Wire.write("hello4");
}

SPI
SPI: Maestro envía datos por SPI
//Arduino Uno
//SPI: 10 (SS), 11 (MOSI), 12 (MISO), 13 (SCK).
#include <SPI.h>
void setup (void)
{
Serial.begin(115200);
digitalWrite(SS, HIGH);
SPI.begin ();
SPI.setClockDivider(SPI_CLOCK_DIV8);
}
void loop (void)
{
char c;
digitalWrite(SS, LOW); // Activa el esclavo SPI destino, SS es pin 10 en Arduino Uno
// Envia "Hola, mundo\n" por SPI, carácter a carácter
for (const char * p = "Hola, mundo!\n" ; c = *p; p++) {
SPI.transfer (c);
Serial.print(c);
}
digitalWrite(SS, HIGH); // Desactiva el esclavo SPI destino
delay (1000);
}

SPI: Esclavo recibe datos por SPI e interrupción (y AVR)

Aprovecha las librerias SPI de Arduino, pero utiliza instrucciones de AVR para modo esclavo
//Arduino Uno
//SPI: 10 (SS), 11 (MOSI), 12 (MISO), 13 (SCK).
#include <SPI.h>
char buf [100];
volatile byte pos;
volatile boolean fin_recepcion;
void setup (void)
{
Serial.begin (9600);
pinMode(SS,INPUT);//Configura pins del esclavo. SS corresponde al pin 10 en Uno
pinMode(MISO, OUTPUT);
//Las instrucciones básicas de Arduino no permiten modo esclavo
SPCR |= _BV(SPE); // Activa el modo esclavo SPI del Atmega328 de Arduino Uno.
pos = 0; // Prepara variables de la interrupción
fin_recepcion = false;
SPI.attachInterrupt(); // Asigna interrupción de recepción del SPI
}
void loop (void)
{
if (fin_recepcion)//Espera fin de recepción para enviar
{
buf [pos] = 0;
Serial.println (buf);
pos = 0;
fin_recepcion = false;
}
}
ISR (SPI_STC_vect)// Rutina de interrupción recepción SPI
{
byte c = SPDR; // lee el byte recibido en el SPI Data Register
if (pos < sizeof buf) // añade el byte al buffer si hay espacio
{
buf [pos++] = c;
if (c == '\n') // Al encontrar '\n' indica fin de recepción
fin_recepcion = true;
}
}

SPI: Esclavo recibe datos por SPI e interrupción y responde un nuevo byte (y AVR)

Aprovecha las librerias SPI de Arduino, pero utiliza instrucciones de AVR para modo esclavo
//Arduino Nano
//SPI: 10 (SS), 11 (MOSI), 12 (MISO), 13 (SCK).
#include "pins_arduino.h"
// what to do with incoming data
byte command = 0;
void setup (void)
{
pinMode(SS,INPUT);
// have to send on master in, *slave out*
pinMode(MISO, OUTPUT);
// turn on SPI in slave mode
SPCR |= _BV(SPE);
// turn on interrupts
SPCR |= _BV(SPIE);
} // end of setup

// SPI interrupt routine


ISR (SPI_STC_vect)
{
byte c = SPDR;
switch (command)
{
// no command? then this is the command
case 0:
command = c;
SPDR = 0;
break;
// add to incoming byte, return result
case 'a':
SPDR = c + 15; // add 15
break;
// subtract from incoming byte, return result
case 's':
SPDR = c - 8; // subtract 8
break;
} // end of switch
} // end of interrupt service routine (ISR) SPI_STC_vect
void loop (void)
{
// if SPI not active, clear current command
if (digitalRead (SS) == HIGH)
command = 0;
} // end of loop

Serie USART
Serie: Modifica el valor de los pins 12 y 13 mediante puerto serie

La función serialEvent() no corresponde a la interrupción del puerto serie, sino que se ejecuta tras cada ciclo de loop()
//Al recibir el texto "led12\n" o "led13\n" cambia el valor de la salida del
//pin correspondiente (0-->1; 1-->0)
String inString = "";
boolean stringComplete=false;
boolean val13=LOW;
void setup() {
Serial.begin(9600);
pinMode (12,OUTPUT);
pinMode (13,OUTPUT);
}
void loop() {
if (stringComplete) {
inString.trim(); // Elimina el carácter '\n' del final y los caracteres no imprimibles
if (inString=="led12"){
digitalWrite(12,!digitalRead(12));
Serial.print("Led12:");
Serial.println(digitalRead(12));
}
if (inString=="led13"){
val13=!val13;
digitalWrite(13,val13);
Serial.print("Led13:");
Serial.println(val13);
}
inString = "";
stringComplete=false;
}
}
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
inString += inChar;
if (inChar == '\n') {
stringComplete = true;
}
}
}

Serie: Recibe un carácter por encuesta (polling), incrementa su valor y lo envía


void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.available()) {
char inChar = (char) Serial.read()+1;
Serial.print(inChar);
}
}

Serie: (AVR) Recibe un carácter por encuesta (polling), incrementa su valor y lo envía

Equivalente al anterior sin utilizar la librería "Serial" y configurando los registros del AVR. Este código utiliza un 20% de la memoria del código en que
se utiliza la librería.
#include <avr/io.h>
#define USART_BAUDRATE 9600
uint8_t TempData;
void setup() {
IniciaUSART0();
}
void loop() {
// Receive data
TempData = USART0RecibeByte();
// Increment received data
TempData++;
//Send back to terminal
USART0EnviaByte(TempData);
}
void IniciaUSART0()
{
// Configura velocidad baudios. F_CPU Arduino Uno = 16000000 (16MHz)
unsigned int UBRR_VALUE = (((F_CPU / (USART_BAUDRATE * 16UL))) - 1);
UBRR0H = (uint8_t)(UBRR_VALUE>>8);
UBRR0L = (uint8_t)UBRR_VALUE;
// UCSR0A= x0xxxx0x, UCSR0B=00011000, UCSR0C=00000110
// Configura la comunicación a 8 data bits (UCSZ02=0,UCSZ01=1, UCSZ00=1
// no parity, 1 stop bit, usart asíncrona
UCSR0A = 0;
UCSR0B = 0;
UCSR0C = 0;
UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);
// Habilita la transmisión y la recepción
UCSR0B |= (1<<RXEN0)|(1<<TXEN0);
}
void USART0EnviaByte(uint8_t Datou8)
{
// Espera a que se haya enviado el byte previo, comprobando la activación del bit
// de registro de datos vacío (UDRE0) del UCSR0A
while(!(UCSR0A&(1<<UDRE0))){};
// Al vaciarse, envia el byte
UDR0 = Datou8;
}
uint8_t USART0RecibeByte()
{
// Espera a que se haya recibido un byte en el registro de datos (UDR0), comprobando
// la activación del bit de recepción completa (RXC0) del UCSR0A
while(!(UCSR0A&(1<<RXC0))){};
// Devuelve la lectura del registro de datos
return UDR0;
}

Serie: (AVR) Recibe un carácter por interrupción, incrementa su valor y lo envía

Equivalente al anterior pero utilizando la interrupción de recepción


#include <avr/io.h>
#define USART_BAUDRATE 9600
uint8_t TempData;
void setup() {
IniciaUSART0();
}
void loop() {
}
void IniciaUSART0()
{
// Configura velocidad baudios. F_CPU Arduino Uno = 16000000 (16MHz)
unsigned int UBRR_VALUE = (((F_CPU / (USART_BAUDRATE * 16UL))) - 1);
UBRR0H = (uint8_t)(UBRR_VALUE>>8);
UBRR0L = (uint8_t)UBRR_VALUE;
// UCSR0A= x0xxxx0x, UCSR0B=10011000, UCSR0C=00000110
// Configura la comuniación a 8 data bits (UCSZ02=0,UCSZ01=1, UCSZ00=1
// no parity, 1 stop bit, usart asíncrona
UCSR0A = 0;
UCSR0B = 0;
UCSR0C = 0;
UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);
// Habilita la transmisión, la recepción y la interrupción de recepción
UCSR0B |= (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);
}
void USART0EnviaByte(uint8_t Datou8)
{
// Espera a que se haya enviado el byte previo, comprobando la activación del bit
// de registro de datos vacio (UDRE0) del UCSR0A
while(!(UCSR0A&(1<<UDRE0))){};
// Al vaciarse, envia el byte
UDR0 = Datou8;
}
uint8_t USART0RecibeByte()
{
// Espera a que se haya recibido un byte en el registro de datos (UDR0), comprobando
// la activación del bit de recepción completa (RXC0) del UCSR0A
while(!(UCSR0A&(1<<RXC0))){};
// Devuelve la lectura del registro de datos
return UDR0;
}
ISR(USART_RX_vect){ // USART Rx Complete UCSR0B=1xx1xxxx
// Receive data
TempData = USART0RecibeByte();
// Increment received data
TempData++;
//Send back to terminal
USART0EnviaByte(TempData);
}
// Otras interrupciones USART
// #define USART_UDRE_vect /* USART, Data Register Empty */UCSR0B=xx111xxx
// #define USART_TX_vect /* USART Tx Complete */ UCSR0B=x1xx1xxx

Serie: (AVR) Recibe string por el puerto serie, y lo vuelve a enviar añadiendo CRLF (por polling)

Lee carácteres por el puerto serie hasta encontrar el carácter indicado o TIMEOUT, lo graba en un string tipo array de char y vuelve a enviarlo por el
puerto serie añadiendo los carácteres de fin de linea CRLF (\r\n)
#include <avr/io.h>
#define USART_BAUDRATE 9600
#define TIMEOUTS 5000
char dadarep[250];
char finlinea[3]={'\r','\n','\0'};
void setup() {
IniciaUSART0();
}
void loop() {
leeseriehasta(dadarep,';');
escribeserie(dadarep);
escribeserie(finlinea);
// Al no usar interrupciones, se podria mezclar el uso del puerto serie mediante los registros con las funciones Serial de Arduino
// Serial.print("Escribe nuevo string:");
}
void IniciaUSART0()
{
unsigned int UBRR_VALUE = (((F_CPU / (USART_BAUDRATE * 16UL))) - 1);
UBRR0H = (uint8_t)(UBRR_VALUE>>8);
UBRR0L = (uint8_t)UBRR_VALUE;
// UCSR0A= x0xxxx0x, UCSR0B=00011000, UCSR0C=00000110
UCSR0A = 0;
UCSR0B = 0;
UCSR0C = 0;
UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);
UCSR0B |= (1<<RXEN0)|(1<<TXEN0);
}
int leeseriehasta(char datolee[], char caracter){
unsigned long toutini;
int ind=0;
char leebyte;
toutini=millis();
do
{
if (UCSR0A&(1<<RXC0)){
leebyte=UDR0;
datolee[ind]=leebyte;
ind++;
}
} while(((millis()-toutini)<TIMEOUTS) & (leebyte!=caracter));
datolee[ind]='\0';
return ind;
}
int escribeserie(char datoescribe[]){
unsigned long toutini;
int ind=0;
toutini=millis();
char lee;
do
{
lee=datoescribe[ind];
if (lee!='\0'){
if (UCSR0A&(1<<UDRE0)){
UDR0=lee;
ind++;
}
}
} while (((millis()-toutini)<TIMEOUTS) & (lee!='\0'));
return ind;
}

Serie: (AVR) Recibe string por el puerto serie por interrupción, añade los caracteres a un buffer, y lee el buffer por partes indicando el
número de bytes a leer

Lee carácteres por el puerto serie por interrupcion y lee el buffer de 3 en 3 bytes (o menos). La cantidad de bytes de contiene el buffer se puede leer en
la variable indserbuf
#include <avr/io.h>
#define USART_BAUDRATE 9600
#define TIMEOUTS 5000
#define SERBUFSIZE 10 //Tamaño máximo del buffer
char serbuf[SERBUFSIZE + 1]; //Crea el buffer con un carácter más, para el carácter null ('\0') de final
int indserbuf = 0; //Indice del buffer. Corresponde al tamaño acual del buffer
char finlinea[3] = {'\r', '\n', '\0'}; // Array de fin de linea con CR LF
char lectbuf[20];
String a;
char ap[5];
int bt;
void setup() {
IniciaUSART0();
}
void loop() {
escribeserie("buffer_actual: ");
escribeserie(serbuf); // Escribe el contenido del buffer por el puerto serie
escribeserie(finlinea);
escribeserie("tamanyo_buffer: ");
a = String(indserbuf, DEC); //Convierte el tamaño del buffer a String
a.toCharArray(ap, 5); //Convierte el String con el tamaño del buffer a string de array de char
escribeserie(ap); //Escribe el valor del tamaño del buffer por el puerto serie
escribeserie(finlinea);
escribeserie("lectura_buffer: ");
bt = leeseriebuffer(lectbuf, 3); //Lee 3 bytes del buffer y los bytes leidos
escribeserie(lectbuf); //Escribe los carácteres leidos
escribeserie(finlinea);
escribeserie("tamanyo_leido: ");
a = String(bt, DEC); //Convierte el valor de los bytes leidos
a.toCharArray(ap, 5);
escribeserie(ap); //Escribe el valor de los bytes leidos
escribeserie(finlinea);
delay(2000);
}
int leeseriebuffer(char lectura[], int nbytes) {
UCSR0B |= (0 << RXCIE0)| (0 << RXCIE0); // Deshabilita la recepción de nuevos carácteres
int byteslee;
if (nbytes >= indserbuf) { // Si el valor de los bytes pedidos a leer es mayor o igual al tamaño
byteslee = indserbuf; // actual de buffer, devuelve todo el buffer
for (int i = 0; i <= indserbuf; i++) {
lectura[i] = serbuf[i];
}
indserbuf = 0;
serbuf[0] = {0}; //serbuf[0]='\0' // Borra el buffer completo
UCSR0B |= (1 << RXCIE0)| (1 << RXCIE0); // Habilita la recepción de nuevos carácteres
return byteslee;
}
if (nbytes < indserbuf) { // Si el valor de los bytes pedidos a leer es mayor o igual al tamaño
byteslee = nbytes; // actual de buffer, devuelve los carácteres solicitados
for (int i = 0; i < nbytes; i++) {
lectura[i] = serbuf[i];
}
for(int i=nbytes; i<=SERBUFSIZE; i++) { // Elimina los carácteres leidos y desplaza todos
serbuf[i-nbytes] = serbuf[i]; // los carácteres del array el mismo número de bytes leidos
}
indserbuf-=nbytes;
UCSR0B |= (1 << RXCIE0)| (1 << RXCIE0); // Habilita la recepción de nuevos carácteres
return nbytes;
}
UCSR0B |= (1 << RXCIE0)| (1 << RXCIE0); // Habilita la recepción de nuevos carácteres
return 0;
}
ISR(USART_RX_vect) { // Interrupción de recepción por puerto serie. USART Rx Complete UCSR0B=1xx1xxxx
if (indserbuf < SERBUFSIZE) { // Si el buffer no esta lleno, lee y añade el caracter recibido
while (!(UCSR0A & (1 << RXC0))) {};
serbuf[indserbuf] = UDR0;
indserbuf++;
serbuf[indserbuf] = '\0'; // Añade el fin de array de char al final del array
}
}
void IniciaUSART0()
{
// Configura velocidad baudios. F_CPU Arduino Uno = 16000000 (16MHz)
unsigned int UBRR_VALUE = (((F_CPU / (USART_BAUDRATE * 16UL))) - 1);
UBRR0H = (uint8_t)(UBRR_VALUE >> 8);
UBRR0L = (uint8_t)UBRR_VALUE;
// UCSR0A= x0xxxx0x, UCSR0B=10011000, UCSR0C=00000110
// Configura la comuniación a 8 data bits (UCSZ02=0,UCSZ01=1, UCSZ00=1
// no parity, 1 stop bit, usart asíncrona
UCSR0A = 0;
UCSR0B = 0;
UCSR0C = 0;
UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00);
// Habilita la transmisión, la recepción y la interrupción de recepción
UCSR0B |= (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0);
}
int escribeserie(char datoescribe[]) {
unsigned long toutini;
int ind = 0;
toutini = millis();
char lee;
do
{
lee = datoescribe[ind];
if (lee != '\0') {
if (UCSR0A & (1 << UDRE0)) {
UDR0 = lee;
ind++;
}
}
} while (((millis() - toutini) < TIMEOUTS) & (lee != '\0'));
return ind;
}

TIMERS
Timers: (AVR) Utiliza la interrupción del Timer 1 (16bits) para parpadear un Led cada 5 segundos

El Timer 1 es de 16 bits. Cuidado si utilizamos la libreria Servo de Arduino, ya que utiliza el Timer 1. Los timers 3, 4 y 5 del ATmega2560 funcionan de
forma similar al Timer 1
// Arduino Atmega328 interrupción timer1. www.engblaze.com
// Añadir librerias propias de microntroladores AVR como el Atmega328
// Valores de registros en: ...\Arduino\hardware\tools\avr\avr\include\avr\io.h
// y ...\Arduino\hardware\tools\avr\avr\include\avr\iom328p
// Limites teoricos tiempo para 16MHz:
// min. 62.5ns OCR1A=1, prescaler=1 (teorico); max. 4.194304s OCR1A=65535, prescaler=1024
#include <avr/io.h>
#include <avr/interrupt.h>
#define LEDPIN 13
int segundos;
void setup()
{
pinMode(LEDPIN, OUTPUT);
// Initialización Timer1 (16bits) (Cuidado, utilizado por libreria Arduino de servo)
cli(); // deshabilita interrupcions
TCCR1A = 0; // WGM11=0, WGM10=0, resto a 0
TCCR1B = 0; // Inicializamos el registro de control B
// Activa modo CTC(Clear Timer on Compare); WGM13=0,WGM12=1,WGM11=0, WGM10=0, para comparar con OCR1A
TCCR1B |= (1 << WGM12);
// CS12=1, CS11=0, CS10=1 para 1024 prescaler
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS12);
// indica el valor del contador del timer para comparar y conseguir un timer de 1 segundo
// F=16MHz --> T=0.0000000625s= 625x10^-10
// resolución timer = prescaler * T =1024*625x10^-10 = 64x10-6
// tiempo = resolución timer * (valor contador timer + 1)
// valor contador timer = (tiempo/resolución timer)-1 = (1s/64x10-6)-1= 15624
OCR1A = 15624; //OCR1A del timer 1 es de 16 bits ==> valor máx. 65535
// Activa la interrupción para que se ejecute al comparar con el valor del registro OCR1A
TIMSK1 |= (1 << OCIE1A);
// Activa interrupciones
sei();
}
void loop()
{
// No hace nada ya que solo funciona la interrupción
}
ISR(TIMER1_COMPA_vect)
{
segundos++;
if (segundos == 5)
{
segundos = 0;
digitalWrite(LEDPIN, !digitalRead(LEDPIN));
}
}

Timers: (AVR) Utiliza la interrupción del Timer 2 (8bits) para parpadear un Led cada 1 segundo

Similar al timer1, pero el timer 2 es de 8 bits. El timer 2 es utilizado en la función tone() de Arduino. El timer 0, también de 8 bits, funciona igual que el
timer 2 pero las instrucciones delay(), millis(), micros() utilizan el timer 0 para su funcionamiento.
// Arduino Atmega328 interrupción timer2.
// Añadir librerias propias de microntroladores AVR como el Atmega328
// Valores de registros en: ...\Arduino\hardware\tools\avr\avr\include\avr\io.h
// y ...\Arduino\hardware\tools\avr\avr\include\avr\iom328p
// Limites teoricos timer 2 tiempo para 16MHz:
// min. 62.5ns OCR2A=1, prescaler=1 (teórico); max. 16.384ms OCR2A=255, prescaler=1024
#include <avr/io.h>
#include <avr/interrupt.h>
#define LEDPIN 13
int ms8;
void setup()
{
pinMode(LEDPIN, OUTPUT);
// Inicialización Timer1 (16bits) (Cuidado, utilizado por librería Arduino de servo)
cli(); // deshabilita interrupciones
TCCR2A = 0; // Inicializamos el registro de control A
TCCR2B = 0; // Inicializamos el registro de control B
// Activa modo CTC(Clear Timer on Compare); WGM22=0,WGM21=1,WGM20=0, para comparar con OCR2A
TCCR2A |= (1 << WGM21);
// CS22=1, CS21=1, CS20=1 para 1024 prescaler
TCCR2B |= (1 << CS20);
TCCR2B |= (1 << CS21);
TCCR2B |= (1 << CS22);
// indica el valor del contador del timer para comparar y conseguir un timer de 8ms (8ms*125=1s)
// F=16MHz --> T=0.0000000625s= 625x10^-10
// resolución timer = prescaler * T =1024*625x10^-10 = 64x10-6
// tiempo = resolución timer * (valor contador timer + 1)
// valor contador timer = (tiempo/resolución timer)-1 = (8ms/64x10-6)-1= 124
OCR2A = 124; //OCR2A del timer 2 es de 8 bits
// Activa la interrupción para que se ejecute al comparar con el valor del registro OCR2A
TIMSK2 |= (1 << OCIE2A);
// Activa interrupciones
sei();
}
void loop()
{
// No hace nada ya que solo se utiliza la interrupción
}
ISR(TIMER2_COMPA_vect)
{
ms8++;
if (ms8 == 125) //(8ms*125=1s)
{
ms8 = 0;
digitalWrite(LEDPIN, !digitalRead(LEDPIN));
}
}

PWM
Los Timers de Arduino tienen la posibilidad de trabajar como PWM. Cada timer en ATmega328 tiene 2 salidas PWM, y en ATmega2560 los timers 0 y 2
tienen 2 salidas PWM, y los timers 1,3,4 y 5 tienen 3 salidas PWM. Hay que tener en cuenta que si utilizamos el modo PWM, y queremos utilizar el
timer simultáneamente, la frecuencia del PWM dependerá del valor del timer configurado, además de otras limitaciones.

http://www.righto.com/2009/07/secrets-of-arduino-pwm.html

Fast PWM mediante analogWrite (Timer2)


// Este programa utiliza 1.062 bytes de memoria
void setup() {
pinMode(3,OUTPUT);
//Configura el duty cycle al 50% y la frecuencia es de 490Hz en el pin3 del Arduino Uno
analogWrite(3,127);
}
void loop() {
}

Fast PWM mediante registros ATMega328 (Timer2)

El programa equivalente a utilizar analogWrite pero mediante registros de ATmega328 es el siguiente:


// Este programa utiliza 650 bytes. Menos que utilizando analogWrite()
//Utilizaremos la salida OC2B del Timer 2 (pin3), y dejaremos la salida OC2A
//libre ya que corresponde al pin MOSI de comunicaciones SPI (pin11). Timer2 8bits (MAX=256=0xFF)
void setup() {
pinMode(3,OUTPUT); //Configura salida OC2B
// Deshabilita OC2A (COM2A1=0, COM2A0=0), configura OC2B modo no invertido (HIGH antes, LOW despues comparacion OCR2B)
// (COM2B1=1, COM2B0=0), Configura Fast PWM (TOP=0xFF=256) (WGM22=0,WGM21=1,WGM20=1)
TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); //_BV(COM2B1) es equivalente a (1 << COM2B1)
// Configura prescaler 128 (CS22=1,CS21=0,CS20=1) --> Frecuencia PWM= 16MHZ/128/256=490Hz
TCCR2B = _BV(CS22)|_BV(CS20);
//Valor OCR2B=((256*duty)/100)-1. Ej. Duty cycle=50%-->OCR2B=127
OCR2B=(uint8_t)(((256*50)/100)-1);
}
void loop() {
}

Fast PWM mediante analogWrite (Timer0)

Como que el Timer0 lo utiliza Arduino para calcular el tiempo de las instrucciones de tiempo como delay(), utilizando las instrucciones de referencia de
Arduino no hay problema para utilizar las salidas PWM del timer 0, como son los pines 5 y 6, simultaneamente con las instrucciones como delay(),
millis(), etc...
void setup() {
pinMode(6,OUTPUT);
pinMode(13,OUTPUT);
analogWrite(6,127); //La frecuencia del PWM en el pin6 de Arduino Uno es de 980Hz
}
void loop() {
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);
delay(1000);
}

Fast PWM mediante registros ATMega328 (Timer0)

Si utilizamos el PWM del Timer0, hay que tener en cuenta de que para que funcionen correctamente las instrucciones de tiempo de Arduino como
delay(), el Timer0 está configurado con un preescalado por defecto de 64, con lo que si modificamos el preescalado en el registro TCCR0B, el tiempo
de las instrucciones delay(), millis(),etc... quedará modificado.
//Utilizaremos la salida OC0A del Timer 0 (pin6)
void setup() {
pinMode(13,OUTPUT);
pinMode(6,OUTPUT); //Configura salida OC0A
// Deshabilita OC0B (COM0B1=0, COM0B0=0), configura OC0A modo no invertido (HIGH antes, LOW después comparación OCR0A)
// (COM0A1=1, COM0A0=0), Configura Fast PWM (TOP=0xFF=256) (WGM02=0,WGM01=1,WGM00=1)
TCCR0A = _BV(COM0A1) | _BV(WGM01) | _BV(WGM00);
// Configura prescaler 64 (CS02=0,CS01=1,CS00=1) --> Frecuencia PWM= 16MHZ/64/256=980Hz
TCCR0B = _BV(CS01)|_BV(CS20);
//Si modificaramos el preescaler a otro valor, por ejemplo 256 (CS02=1,CS01=0,CS00=0),
//la frecuencia disminuiria a 16MHZ/256/256=245Hz, con lo que delay(1000) en lugar de realizar
//un retardo de 1 segundo, realizaria un retardo de 4 segundos.(TCCR0B = _BV(CS02);)
//Valor OCR0A=((256*duty)/100)-1. Ej. Duty cycle=50%-->OCR0A=127
OCR0A=(uint8_t)(((256*50)/100)-1);
}
void loop() {
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);
delay(1000);
}

Fast PWM mediante registros ATMega328 (Timer1)

El timer 1 es de 16 bits y tiene más modos de funcionamiento que los timers 0 y 2 de 8 bits.
//El Timer1 es de 16 bits, pero como Fast PWM puede trabajar con 8, 9 o 10 bits.
//Utilizaremos la salida OC1A del Timer 1 (pin9)
void setup() {
pinMode(9,OUTPUT); //Configura salida OC1A
// Deshabilita OC1B (COM1B1=0, COM1B0=0), configura OC1A modo no invertido (HIGH antes, LOW después comparación OCR0A)
// (COM1A1=1, COM1A0=0), Configura Fast PWM 10bits (TOP=0x03FF=1024) (WGM13=0,WGM12=1,WGM11=1,WGM10=1)
TCCR1A = _BV(COM1A1) | _BV(WGM11)| _BV(WGM10);
// Configura prescaler 64 (CS12=0,CS11=1,CS10=1) --> Frecuencia PWM= 16MHZ/64/1024=244Hz
TCCR1B = _BV(CS10) |_BV(CS11) | _BV(WGM12);
//Valor OCR1A=((1024*duty)/100)-1. Ej. Duty cycle=50%-->OCR1A=512
OCR1A=(uint16_t)((((uint16_t)1024*50)/100)-1);
}
void loop() {
}

Contadores ATmega
Arduino no dispone de funciones para utilizar los contadores, ya que al ser comunes con loos temporizadores, no se podrían utilizar instrucciones como
delay() o el PWM, ya que utilizan el temporizador 0. Además la placas Arduino no proporcionan acceso a todos los pines de contaje externo, con lo que
estamos limitados en el uso de contadores. Por ejemplo, la placa Arduino UNO, si que tiene acceso a la entrada de contador T0 del pin del ATmega328
PD4 que corresponde al pin de la placa 4, y a T1 del pin del ATmega328 PD5 que corresponde a la entrada 5 de la placa. Sin embargo, en la placa
Arduino Mega 2560 solo podemos acceder a T0 del pin del ATmega2560 PD7 de la entrada de la placa 38, y a T5 del pin del ATmega2560 PL2 de la
entrada de la placa 47. No hay acceso ni a T1-PD6, ni T3-PE6, ni T4-PH7.

Activa un contador por registros. Cuenta hasta 65535 (0xFFFF)

Utilizaremos la placa Arduino Mega 2560 y el contador 5 de 16 bits, dejando libre el temporizador 0 para el uso de la instrucción delay(). La entrada T5
corresponde a la entrada 47 de la placa y al pin PL2. Es importante tener en cuenta que el contador es muy sensible a los rebotes ya que los cuenta,
con lo que es recomendable poner un circuito antirrebotes.
void setup() {
Serial.begin(9600);
// Configura como entrada PL2 - T5
DDRL &= ~(1<<DDL2); // Pone a 0 el bit DDL2 del registro DDRL, ya que T5 corresponde al pin 47 que esta conectado a la entrada PL2 en ATmega
PORTL |= (1 << PORTL2); // Pone a 1 el pin PL2 para activar pull-up y evitar cortocircuitos internos.
// WGM53=0, WGM52=0, WGM51=0, WGM50=0. Normal mode TOP 0xFFFF
TCCR5A = 0x0000; //WGM51=0, WGM50=0
// Activa el contador 5 con contaje por flanco de subida del pin externo T5 (PL2, entrada 47)
TCCR5B |= (1 << CS52) | (1 << CS51) | (1 << CS50); // WGM53=0, WGM52=0
}
void loop() {
Serial.println(TCNT5); //Envia por el puerto serie el valor del contador.
delay(1000);
}

Activa un contador por registros. Cuenta hasta 15 (0x000F)

Como el anterior, pero cuenta de 0 a 15 y al sobrepasar 15 vuelve a 0.


void setup() {
Serial.begin(9600);
DDRL &= ~(1<<DDL2); // Configura como entrada PL2 - T5
PORTL |= (1 << PORTL2);
// WGM53=0, WGM52=1, WGM51=0, WGM50=0. Modo CTC(Clear Timer on Compare Match) valor TOP en OCR5A
TCCR5A = 0x0000; //WGM51=0, WGM50=0
TCCR5B |= (1<<WGM52)|(1 << CS52) | (1 << CS51) | (1 << CS50); // WMGM53=0, WGM52=1
OCR5A=0x000F; //Valor TOP de contaje 15 (0x000F);
}
void loop() {
Serial.println(TCNT5);
delay(1000);
}

Activa un contador por registros y activa interrupción al sobrepasar 65535 (0xFFFF)

La interrupción por sobrepasamiento TOI, se activa solo al sobrepasar del máximo 65535, con lo que no se puede activar limitando el valor de contaje a
OCRnA. En este ejemplo se configura para que salte la interrupción al sobrepasar 65535
void setup() {
Serial.begin(9600);
// Configura como entrada PL2 - T5
DDRL &= ~(1<<DDL2);
PORTL |= (1 << PORTL2);
// WGM53=0, WGM52=0, WGM51=0, WGM50=0. Normal mode TOP 0xFFFF
TCCR5A = 0x0000; //WGM51=0, WGM50=0
// Activa el contador 5 con contaje por flanco de subida del pin externo T5 (PL2, entrada 47)
TCCR5B |= (1 << CS52) | (1 << CS51) | (1 << CS50); // WGM53=0, WGM52=0
cli(); // Deshabilita interrupciones;
TIMSK5 |= (1 << TOIE5); // Habilita interrupción de Timer por sobrepasamiento
sei(); // Habilita interrupciones;
}
void loop() {
if (TCNT5<65520)
{
TCNT5=65520; //Para no esperar, ponemos a 65520 (0xFFF0) para solo contar 15 hasta sobrepasar
}
Serial.println(TCNT5); //Envia por el puerto serie el valor del contador.
delay(1000);
}
Activa un contador por registros y activa interrupción al sobrepasar el valor de OCR5A

La interrupción por comparación (Output Compare A Match Interrupt), se activa solo al sobrepasar el valor TOP indicado en el registro OCRnA. En este
ejemplo se configura para que salte la interrupción al sobrepasar de 15 (0x000F) el contador 5.
int sobre=0;
void setup() {
Serial.begin(9600);
DDRL &= ~(1<<DDL2);
PORTL |= (1 << PORTL2);
// WGM53=0, WGM52=1, WGM51=0, WGM50=0. CTC modo TOP OCR5A
TCCR5A = 0x0000; //WGM51=0, WMM50=0
// Activa el contador 5 con contaje por flanco de subida del pin externo T5 (PL2, entrada 47)
TCCR5B |= (1<<WGM52)|(1 << CS52) | (1 << CS51) | (1 << CS50); // WMGM53=0, WGM52=1
OCR5A=0x000F; // Valor de comparación
cli(); // Deshabilita interrupciones;
TIMSK5 |= (1 << OCIE5A); // Habilita interrupción de Timer por sobrepasamiento de OCR5A
sei(); // Habilita interrupciones;
TCNT5=0;
}
void loop() {
Serial.println(TCNT5);
delay(1000);
}
ISR (TIMER5_COMPA_vect) //Cuando sobrepasa OCR5A salta la interrupción
{
sobre++;
Serial.print("Ha sobrepasado: ");
Serial.print(sobre,DEC);
Serial.println(" veces");
}

Entrada Analógica ADC


Lee el valor de una entrada analógica y parpadea LED (Arduino)
int sensorPin = A5; // pin entrada analógica donde se encuentra el sensor
int ledPin = 13; // pin del LED
int sensorValue = 0; // variable para el valor del sensor
void setup() {
analogReference(DEFAULT); //Elige como referencia AVCC que esta conectado a 5V normalmente en Arduino
pinMode(ledPin, OUTPUT); //Configura el pin del LED como salida
}
void loop() {
sensorValue = analogRead(sensorPin); // lee el valor del sensor
digitalWrite(ledPin, HIGH); // enciende el LED
delay(sensorValue); // espera el valor del sensor en milisegundos
digitalWrite(ledPin, LOW); // apaga el LED
delay(sensorValue); // espera el valor del sensor en milisegundos
}

Lee el valor de una entrada analógica y parpadea LED (Registros ATmega)

El mismo que el anterior pero utilizando registros de ATmega


int ledPin = 13; // pin del LED
int sensorValue = 0; // variable para el valor del sensor
void setup() {
ADMUX=(1<<REFS0); //Elige como referencia AVCC que esta conectado a 5V normalmente en Arduino
//Activa ADC, prescaler 128 --> 16MHz/128=125KHz.
//Como que una conversión tarda 13 ciclos --> 125KHz/13= 9615Hz --> 0.104ms por conversión
ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
pinMode(ledPin, OUTPUT); //Configura el pin del LED como salida
}
void loop() {
ADMUX|=5; //Selecciona canal A5
ADCSRA|=(1<<ADSC); //Inicia conversion simple
while(!(ADCSRA&(1<<ADIF))); //Espera fin de conversion
ADCSRA|=(1<<ADIF); //Borra indicador de fin de conversion. Escribiendo 1 se pone a 0
sensorValue = ADC; // lee el valor del sensor
digitalWrite(ledPin, HIGH); // enciende el LED
delay(sensorValue); // espera el valor del sensor en milisegundos
digitalWrite(ledPin, LOW); // apaga el LED
delay(sensorValue); // espera el valor del sensor en milisegundos
}

Guardar datos EEPROM


Guardar nuevo valor de retardo de delay (Arduino)
//Lee un numero de 32 bits de la EEPROM que esta separado en grupos de 8 bits, lo une en un long y lo
//asigna a un retardo que se utilizará en unas instrucciones delay. Si se envia un número por el puerto
//serie, lo asigna al retardo y lo graba en la EEPROM para que la proxima vez que se inicie la placa Arduino
//o se realice un reset, se recupere el ultimo valor de retardo.
#include <EEPROM.h>
String inString = "";
boolean stringComplete=false;
boolean sobrepasa=false;
uint32_t retardo, nouret, tmp;
uint8_t rt0,rt1,rt2,rt3;
void setup() {
Serial.begin(9600);
pinMode(13, OUTPUT);
rt0=EEPROM.read(0); //Lee la EEPROM el valor de 32 bits que esta separado en grupos de 8 bits.
rt1=EEPROM.read(1);
rt2=EEPROM.read(2);
rt3=EEPROM.read(3);
tmp=0x0000FFFF&((rt3<<8)|(rt2)); //Guarda los 16 bits de la parte alta del numero en tmp, borrando los 16 bits altos de tmp
retardo=0x0000FFFF&((rt1<<8)|rt0); //Guarda los 16 bits bajos en la parte baja de retardo, borrando los 16 bits mas altos
retardo|=(uint32_t)(tmp<<16); //Pone los 16 bits guardados en tmp de la parte alta, en la parte alta de retardo
Serial.print("Retardo Inicial EEPROM: ");
Serial.println(retardo);
}
void loop() {
if (stringComplete) {
inString.trim(); // Elimina el caracter '\n' del final y los caracteres no imprimibles
//Comprueba que el valor no sea mayor de 32 bits (4294967296), ya que sino sobrepasaria
if (((inString.length()==10)&(inString.substring(0,1)=="4")&(nouret<1000000000))|((inString.length()==10)&(inString.substring(0,1).toInt
sobrepasa=true;
}
else
{
sobrepasa=false;
}
nouret=inString.toInt(); //Devuelve 0 si no encuentra un numero al principio
//Solo tomara como validos nnumeros positivos, que no sobrepase de 32 bits, ni superen los rangos
if ((nouret>0)&(nouret<4294967296)&(inString.substring(0,1)!="-")&(!sobrepasa)){ //Podemos limitar el valor del retardo
retardo=(uint32_t)nouret;
Serial.print("Nuevo retardo: ");
Serial.println(retardo);
rt0=retardo&0xFF; //Separa en grupos de 8 bits
rt1=(retardo>>8)&0xFF;
rt2=(retardo>>16)&0xFF;
rt3=(retardo>>24)&0xFF;
EEPROM.write(0,rt0); //Guarda los grupos en la EEPROM
EEPROM.write(1,rt1);
EEPROM.write(2,rt2);
EEPROM.write(3,rt3);
}
else {
Serial.print("Retardo erroneo o fuera de rango: ");
Serial.println(inString);
}
inString = "";
stringComplete=false;
}
digitalWrite(13, HIGH);
delay(retardo);
digitalWrite(13, LOW);
delay(retardo);
}
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
inString += inChar;
if (inChar == '\n') {
stringComplete = true;
}
}
}

Modo Sleep
El modo Sleep o modo de bajo consumo, es el modo que permite disminuir el consumo en dispositivos Arduino y así conseguir que las baterias duren
más tiempo. En este modo podemos apagar varios periféricos que no utilizemos, y enviamos a Arduino a "dormir" con lo que reducimos el consumo. En
función del modo Sleep configurado, existen varias formas de volver a despertar. En la siguiente tabla se muestra en cada modo, los relojes y
osciladores que se pueden mantener activos, y los métodos de despertar:
Los modos Sleep en función del consumo aproximado de más a menos son:

• SLEEP_MODE_IDLE: 15 mA (ahorra poco debido a que puede mantener muchas funciones de los periféricos)
• SLEEP_MODE_ADC: 6.5 mA
• SLEEP_MODE_PWR_SAVE: 1.62 mA
• SLEEP_MODE_EXT_STANDBY: 1.62 mA
• SLEEP_MODE_STANDBY : 0.84 mA
• SLEEP_MODE_PWR_DOWN : 0.84 mA

Las instrucciones que proporciona Arduino mediante la libreria sleep.h (#include <avr/sleep.h>) (C:\Program Files
(x86)\Arduino\hardware\tools\avr\avr\include\avr\sleep.h) son:

• set_sleep_mode(modo): esta funciona establece el modo de bajo consumo, el parámetro modo puede tomar el valor de los nombres de los
modos que hemos indicado anteriormente.
• sleep_enable(): habilita el sistema para ponerse en modo bajo consumo pero sin activarlo.
• sleep_cpu(): activa el modo de bajo consumo
• sleep_disable(): primera instrucción que debe aparecer cuando se vuelve del modo bajo consumo. deshabilita el modo bajo consumo.
• sleep_mode(): activa el modo bajo consumo pero incluye de una sola vez sleep_enable() + sleep_cpu() + sleep_disable(). En algunos casos
puede dar problemas y es mejor ejecutar las tres instrucciones de forma separada en lugar de sleep_mode().

Intermitente de 1 segundo en modo Sleep e interrupción Timer 2 mediante instrucciones Arduino

Realiza intermitencias de 1 segundo en el led conectado al pin 13 (similar al Blink), pero utilizando el modo Sleep. Apaga todos los periferidos excepto
el Timer2, y activa el modo Sleep Power-Save. El Timer 2 despierta el microcontrolador cada 8ms y vuelve a dormir, excepto cada segundos (8ms*125)
en que cambia el estado del led y vuelve a dormir.

Excepto en la interrupción temporizada 2 que no la proporcionan las instrucciones básicas de Arduino, se intentan utilizar el máximo de instrucciones y
librerias propias de Arduino.
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/power.h>
int ms8;
void setup() {
pinMode(13,OUTPUT);
configura_timer2_8ms();
ADCSRA=0; //Apagamos todo el ADC
//Apagamos todos los periféricos poniendo el bit correspondiente a 1 en PRR, excepto el Timer2
power_twi_disable(); //PRR|=(1<<PRTWI);
power_timer0_disable(); //PRR|=(1<<PRTIM0);
power_timer1_disable(); //PRR|=(1<<PRTIM1);
power_spi_disable(); //PRR|=(1<<PRSPI);
power_usart0_disable(); //PRR|=(1<<PRUSART0);
power_adc_disable(); //PRR|=(1<<PRADC);
power_timer2_enable(); //Activa Timer2
set_sleep_mode(SLEEP_MODE_PWR_SAVE); //Modo Sleep Power-Save (registro SMCR atmega SM2=0,SM1=1,SM0=1)
}
void loop() {
// Activa interrupciones
sei();
sleep_enable(); //Habilita modo Sleep SE=1
while (ms8<125) //Mientras ms8<125 no habrá pasado el tiempo, y estará durmiendo y despertandose.
{
sleep_cpu(); //Activa Sleep
//Tras volver del modo Sleep, continua desde aqui
sleep_disable();//Deshabilita modo Sleep SE=0
}
cli(); //Deshabilita interrupciones
ms8=0;
digitalWrite(13,!digitalRead(13));
}
ISR(TIMER2_COMPA_vect)
{
ms8++;
}
void configura_timer2_8ms()
{
TCCR2A = 0; // Inicializamos el registro de control A
TCCR2B = 0; // Inicializamos el registro de control B
// Activa modo CTC(Clear Timer on Compare); WGM22=0,WGM21=1,WGM20=0, para comparar con OCR2A
TCCR2A |= (1 << WGM21);
// CS22=1, CS21=1, CS20=1 para 1024 prescaler
TCCR2B |= (1 << CS20);
TCCR2B |= (1 << CS21);
TCCR2B |= (1 << CS22);
// indica el valor del contador del timer para comparar y conseguir un timer de 8ms (8ms*125=1s)
// F=16MHz --> T=0.0000000625s= 625x10^-10
// resolución timer = prescaler * T =1024*625x10^-10 = 64x10-6
// tiempo = resolución timer * (valor contador timer + 1)
// valor contador timer = (tiempo/resolución timer)-1 = (8ms/64x10-6)-1= 124
OCR2A = 124; //OCR2A del timer 2 es de 8 bits
// Activa la interrupción para que se ejecute al comparar con el valor del registro OCR2A
TIMSK2 |= (1 << OCIE2A);
}

Intermitente de 1 segundo en modo Sleep e interrupción Timer 2 mediante AVR y ASM ATmega328

Equivalente al anterior pero reduciendo al mínimo las instrucciones y librerias propias de Arduino, y utilizando instrucciones en ensamblador
directamente.
int ms8;
void setup() {
pinMode(13,OUTPUT);
configura_timer2_8ms();
ADCSRA=0; //Apagamos todo el ADC
//Apagamos todos los periféricos poniendo el bit correspondiente a 1 en PRR, excepto el Timer2
//PRR-> 7:PRTWI | 6:PRTIM2 | 5:PRTIM0 | 4:RESERV | 3:PRTIM1 | 2:PRSPI | 1:PRUSART0 | 0:PRADC
PRR=0;
PRR|=(1<<PRTWI);
PRR|=(1<<PRTIM0);
PRR|=(1<<PRTIM1);
PRR|=(1<<PRSPI);
PRR|=(1<<PRUSART0);
PRR|=(1<<PRADC);
//Modo Sleep Power-Save (registro SMCR atmega SM2=0,SM1=1,SM0=1)
SMCR=0;
SMCR|=(1<<SM1);
SMCR|=(1<<SM0);
}
void loop() {
__asm__ __volatile__ ("sei" ::: "memory"); //sei(); // Activa interrupciones
SMCR|=(1<<SE); //sleep_enable(); Habilita modo Sleep SE=1
while (ms8<125) //Mientras ms8<125 no habrá pasado el tiempo, y estará durmiendo y despertandose.
{
__asm__ __volatile__ ( "sleep" "\n\t" :: ); //sleep_cpu(); //Activa modo Sleep
//Tras volver del modo Sleep, continua desde aquí
SMCR &= 0xFE; //sleep_disable(); Deshabilita modo Sleep SE=0
}
__asm__ __volatile__ ("cli" ::: "memory");// cli(); //Deshabilita interrupciones
ms8=0;
digitalWrite(13,!digitalRead(13));
}
ISR(TIMER2_COMPA_vect)
{
ms8++;
}
void configura_timer2_8ms()
{
TCCR2A = 0; // Inicializamos el registro de control A
TCCR2B = 0; // Inicializamos el registro de control B
// Activa modo CTC(Clear Timer on Compare); WGM22=0,WGM21=1,WGM20=0, para comparar con OCR2A
TCCR2A |= (1 << WGM21);
// CS22=1, CS21=1, CS20=1 para 1024 prescaler
TCCR2B |= (1 << CS20);
TCCR2B |= (1 << CS21);
TCCR2B |= (1 << CS22);
// indica el valor del contador del timer para comparar y conseguir un timer de 8ms (8ms*125=1s)
// F=16MHz --> T=0.0000000625s= 625x10^-10
// resolución timer = prescaler * T =1024*625x10^-10 = 64x10-6
// tiempo = resolución timer * (valor contador timer + 1)
// valor contador timer = (tiempo/resolución timer)-1 = (8ms/64x10-6)-1= 124
OCR2A = 124; //OCR2A del timer 2 es de 8 bits
// Activa la interrupción para que se ejecute al comparar con el valor del registro OCR2A
TIMSK2 |= (1 << OCIE2A);
}

Modo Sleep y despertar por interrupción 0

En este ejemplo, Arduino está dormido y cada vez que la entrada 2 (INT0) cambia de 0 a 1, se detecta medienta la interrupción 0 y tras 250ms el led
cambia de estado. La entrada 2 se configura como entrada con resistencia Pull-up, con lo que por defecto la entrada está a 1 y hay que poner la
entrada a 0 y quitar el 0 para que realice el cambio. El modo SLEEP_MODE_PWR_DOWN es el que menos energia consume y solo se puede
despertar mediante interrupción externa (INT0 o INT1) por cambio de nivel, o Two-wire Serial Interface (TWI).

#include <avr/sleep.h>
void setup() {
pinMode(2,INPUT_PULLUP);
pinMode(13,OUTPUT);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
attachInterrupt(0,rutinainterrupcion,HIGH);
}
void loop() {
delay(250);
digitalWrite(13,!digitalRead(13));
sleep_enable();
sleep_cpu();
}
void rutinainterrupcion(){
sleep_disable();
}

Funciones y ejemplos
Strings
Separa String en dos partes separadas por un carácter
int separaDos(String dato,char caracter,String &pres,String &posts){
//Busca el primer caracter en el dato y separa en dos el dato.
//Antes de el caracter lo devuelve en pres y despues del caracter en post
//Si no lo encuentra, devuelve -1, si lo encuentra devuelve la posición del caracter buscado
int i;
i=dato.indexOf(caracter);
if (i>-1){
pres=dato.substring(0,i);
posts=dato.substring(i+1);
}
return i;
}
//Uso: Separar en dos el dato "a10*20b"
// String inString = ""; String pre,post =""; int err;
// err=separaDos(inString,'*',pre,post);
// Quedaria: err=0, pre=a10, post=20b

Separa String en tres partes separadas por dos caracteres


int separaTres(String dato,char caracter1,char caracter2, String &pre1, String &medio, String &post2){
//Busca en el dato primero el caracter1 desde el principio y después el carácter2 desde la posición
//del caracter 1, y separa en tres el dato siendo pre1 el string de antes del primer caracter,
//medio el string entre el primer y segundo caracter, y post2 el string tras el segundo caracter
//Devuelve 0 si encuentra los dos carácteres en el orden correcto, además de modificar los valores,
//y -1 si no los encuentra
int i1,i2;
String previo1,post1;
i1=dato.indexOf(caracter1);
if (i1>-1){
previo1=dato.substring(0,i1);
post1=dato.substring(i1+1);
i2=post1.indexOf(caracter2);
if (i2>-1){
pre1=previo1;
medio=post1.substring(0,i2);
post2=post1.substring(i2+1);
return 0;
}
}
return -1;
}
//Uso: Separar en tres el dato "10*20;30"
// String inString = ""; String ant="",med="",fin=""; int err;
// err=separaTres(inString,'*',';',ant,med,fin);
// Quedaria: err=0, ant=10, med=20, fin=30

Separa busca un String dentro de otro String


//Busca un String dentro de otro y devuelve la posición del carácter inicial
//si no lo encuentra devuelve -1
int buscaString(String dato, String strbusca){
int lendato, lenstr, i;
lendato=dato.length();
lenstr=strbusca.length();
if (lenstr>lendato) return -1;
else {
for (i=0;i<=lendato-lenstr;i++){
if(dato.substring(i,i+lenstr)==strbusca){
return i;
};
}
return -1;
}
}

Devuelve el tamaño de un string de tipo char array


// Cuenta hasta encontrar el carácter nulo '\0'
int lenchararray(char array[]){
int in=0;
do{
lee=dadarep[in];
in++;
} while (lee!='\0');
return in-1;
}

Concatena varios char arrays

Este ejemplo envia por el puerto serie unos numeros tipo float, convirtiendolos a char arrays de tamaño 7 con 3 decimales (eee.ddd) y con caracteres
(;) de separacion además del caracter inicial (:). Lo realiza enviando por separado cada caracter y número, y posteriormente los une en un solo char
array utilizando las funciones strcat() y strcpy(), y envia todo con una sola transmisión.
int width=7; int precision=3; //Datos configuracion conversion dtostrf()
float v=2.60 ; float ref=0.5; float u=12.893;
char refchar[8]; char uchar[8]; char vchar[8]; char unionchar[28];
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println("Sin unir:");
dtostrf(ref,width,precision,refchar);
dtostrf(u,width,precision,uchar);
dtostrf(v,width,precision,vchar);
Serial.print(":");Serial.print(refchar);
Serial.print(";");Serial.print(uchar);
Serial.print(";");Serial.println(vchar);
delay(1000);
Serial.println("Unidos:");
strcpy(unionchar,":");
strcat(unionchar,refchar);
strcat(unionchar,";");
strcat(unionchar,uchar);
strcat(unionchar,";");
strcat(unionchar,vchar);
Serial.println(unionchar);
delay(1000);
}
Serie
Lee caracteres del puerto serie 0 hasta que encuentra el carácter indicado

Para evitar bloqueos, en la variable TIMEOUTS se indica el tiempo máximo en milisegundos para leer del puerto.
// UCSR0A= x0xxxx0x, UCSR0B=00011000, UCSR0C=00000110
#define TIMEOUTS 5000
int leeseriehasta(char datolee[], char caracter){
unsigned long toutini;
int ind=0;
char leebyte;
toutini=millis(); //Tiempo inicial
do
{
if (UCSR0A&(1<<RXC0)){ // Si hay byte en el registro de entrada lee el byte y lo añade al array
leebyte=UDR0;
datolee[ind]=leebyte;
ind++;
}
} while(((millis()-toutini)<TIMEOUTS) & (leebyte!=caracter)); // Lee el puerto serie hasta que encuentre el carácter o pase el tiempo
datolee[ind]='\0'; // Añade el terminador nulo para indicar final de cadena de carácteres '\0'
return ind; // Devuelve el número de caracteres leídos, incluido el indicado
}

Lee la cantidad de caracteres indicados del puerto serie 0

Para evitar bloqueos, en la variable TIMEOUTS se indica el tiempo máximo en milisegundos para leer del puerto
// UCSR0A= x0xxxx0x, UCSR0B=00011000, UCSR0C=00000110
#define TIMEOUTS 5000
int leeseriebytes(char datolee[], int bytes){
unsigned long toutini;
int ind=0;
char leebyte;
toutini=millis();
do
{
if (UCSR0A&(1<<RXC0)){
leebyte=UDR0;
datolee[ind]=leebyte;
ind++;
}
} while(((millis()-toutini)<TIMEOUTS) & (ind<bytes)); // Lee el puerto serie hasta leer la cantidad indicada de caracteres o pase el tiemp
datolee[ind]='\0';
return ind;
}

Escribe un string de array de chars por puerto serie 0

Para evitar bloqueos, en la variable TIMEOUTS se indica el tiempo máximo en milisegundos para escribir en el puerto
// UCSR0A= x0xxxx0x, UCSR0B=00011000, UCSR0C=00000110
#define TIMEOUTS 5000
int escribeserie(char datoescribe[]){
unsigned long toutini;
int ind=0;
toutini=millis();
char lee;
do
{
lee=datoescribe[ind];
if (lee!='\0'){ // Si el siguiente carácter no es el nulo de fin, envia el carácter
if (UCSR0A&(1<<UDRE0)){
UDR0=lee;
ind++;
}
}
} while (((millis()-toutini)<TIMEOUTS) & (lee!='\0')); // Escribe en el puerto serie hasta detectar el caracter nulo o pase el tiempo
return ind; //Devuelve el número de bytes enviados
}

Hardware
LCD
1602 (2x16)

Lee caracteres del puerto serie hasta encontrar el salto de linea ('\n') y lo envía al LCD. Si se reciben hasta 32 caracteres los muestra separados en dos
lineas de hasta 16 caracteres.
#include <LiquidCrystal.h>
//LCD RW:0v, Vo=0v
//LCD Pins--> RS:12, En:11, D4:5, D5:4, D6:3, D7:2
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
String inString = "";
boolean stringComplete=false;
String primer16="";
String segon16="";
void setup() {
lcd.begin (16,2);
lcd.print("Esperando texto");
Serial.begin(9600);
Serial.println("\n\nEscribe texto con un máximo de 32 carácteres:");
Serial.println();
}
void loop() {
if (stringComplete) {
Serial.print("Has escrito:");
Serial.println(inString);
inString.trim();
lcd.clear();
lcd.setCursor(0,0);
if (inString.length()<=32)
{
if(inString.length()<=16)
{
lcd.print(inString);
}
else
{
primer16=inString.substring(0,16);
segon16=inString.substring(16);
lcd.setCursor(0,0);
lcd.print(primer16);
lcd.setCursor(0,1);
lcd.print(segon16);
}
}
else
{
lcd.print("Demasiados caracteres");
}
inString = "";
stringComplete=false;
}
}
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
inString += inChar;
if (inChar == '\n') {
stringComplete = true;
}
}
}

Ultrasonidos
HC-SR04

Uso del sensor de ultrasonidos HC-SR04. Para leer la distancia, se envia un pulso de 10us por el pin de trigger, tras los 10us el sensor envia 8 pulsos a
una frecuencia de 40kHz, y tras los pulsos se activa el pin de echo durante el tiempo que tarda en recibir el rebote de los pulsos enviados. El tiempo es
proporcional al doble de la distancia ya que el sonido ha de rebotar y la velocidad del sonido es de 343m/s.
#define HCSR04trigPin 13
#define HCSR04echoPin 12
void setup() {
Serial.begin (9600);
configuraHCSR04();
Serial.println("Sensor ultrasonidos HC-SR04 (2cm-400cm)");
}
void loop() {
long duracion;
float distancia;
duracion=leeHCSR04();
Serial.print("Duracion: ");
Serial.println(duracion);
//En este ejemplo la distancia correcta corresponde a: distancia=duración/50 --> distancia=duracion*0.02 que corresponde
//a un entorno con una velocidad del sonido de 400m/s.
//duración=250 -->5cm, 500-->10cm, 1000-->20cm, 20000-->4m
distancia= duracion*0.02;
if (distancia > 400 || distancia < 2){
Serial.println("Fuera de rango");
}
else {
Serial.print(distancia,4);
Serial.println(" cm");
}
delay(500);
}
void configuraHCSR04(){
pinMode(HCSR04trigPin, OUTPUT);
pinMode(HCSR04echoPin, INPUT);
}
long leeHCSR04(){
//Devuelve el tiempo de respuesta en us
//Distancia teorica corresponde a la mitad de la duración ya que corresponde al tiempo de ida y vuelta que es dos veces la distancia
//La velocidad del sonido es de 343 m/s (a 20 °C de temperatura, con 50 % de humedad y a nivel del mar y ello
//corresponde a 29.155us/cm --> distancia = (duracion/2) / 29.155 --> distancia=duracion/58.31;
//Tiempo de activación 2us+10us+200us=212us
//Distancia máxima 4m --> 20000us duracion
//Tiempo mínimo de adquisición 21ms para 4m, 1.3ms para 20cm, ...
digitalWrite(HCSR04trigPin, LOW);
delayMicroseconds(2);
digitalWrite(HCSR04trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(HCSR04trigPin, LOW);
return (pulseIn(HCSR04echoPin, HIGH));
}

Distancia Infrarrojos
Sharp GP2DY120X y similares

Para la detección de distancia también disponemos de sensores de distancia ópticos, y los más utilizados son de la marca Sharp. Para detectar la
distancia utiliza un emisor y un receptor infrarrojos utilizando un método de triangulación qu consiste en medir el ángulo formado por el triángulo
emisor-objeto-receptor, donde el receptor es un PDS (Position Sensitive Detector) que detecta el punto de incidencia de la luz infrarroja recibida, el cual
depende del ángulo y a su vez de la distancia del objeto. Existen varios modelos, en función del rango de detección. Por ejemplo:

• GP2DY120X de 4 a 30cm
• GP2DY0A21 de 10 a 80cm
• GP2DY0A02 de 20 a 150cm
• GP2DY0A710 de 100 a 550cm

Para el uso con Arduino, utilizaremos una entrada analógica para convertir el voltaje a un valor digital y poder trabajar con él.

La salida del sensor Sharp no es lineal, sino que tiene forma de potencia negativa a partir del rango mínimo del sensor:
Realizamos una prueba con un GP2DY120X, adquiriendo muestras cada centímetro entre 0 y 26 cm (teóricamente 30cm, pero devolvía el mismo valor
entre 26 y 30cm), y adquirimos los siguientes valores:

cm Valor cm valor cm valor cm valor


0 215 7 330 14 178 21 120
1 340 8 294 15 166 22 114
2 399 9 266 16 156 23 111
3 477 10 240 17 150 24 107
4 478 11 221 18 140 25 103
5 430 12 205 19 133 26 100
6 376 13 190 20 126
De forma gráfica vemos que se parece a la proporcionada por el fabricante, donde la forma exponencial decreciente comienza a partir de valor inicial
del rango del sensor, que en este caso son 4cm:

Al no ser lineal, necesitamos linealizar el valor recibido del sensor, y así tener un valor en centímetros. Para ello tenemos dos posibilidades. Una
aproximación para la zona de mayor pendiente seria mediante la fórmula siguiente formula basada en la ecuación de una recta
(https://acroname.com/articles/linearizing-sharp-ranger-data):

Para una aproximación que abarque la mayoría del rango, utilizamos un modelo potencial del tipo:

Necesitamos calcular los parámetros de las fórmulas mediante un sistema de ecuaciones. Para ahorrarnos el tener de calcular, podemos utilizar un
programa matemático como el wxMaxima y utilizar la opción de resolución de sistemas algebraicos. Para el cálculo de los parámetros de la fórmula de
la división, en wxMaxima utilizaríamos:
algsys([6=(a/(376-b))+c, 15=a/(166-b)+c,10=a/(240-b)+c], [a,b,c]),numer;

En cambio para el modelo de potencia, hay que adaptar la formula mediante propiedades de logaritmos, donde:

En este caso el sistema devolvería los valores de y , con lo que para calcular a habría que calcular . En
wxMaxima utilizaríamos:
algsys([log(6)=al+b*log(376), log(15)=al+b*log(166)], [al,b]),numer;
a:exp(rhs(%[1][1]));
Calculados los parámetros, las formulas quedarían:

Tras adquirir los parámetros de las fórmulas, realizamos el cálculo para cada uno de los datos adquiridos, y comparamos las rectas. Podemos ver
como con las dos fórmulas el cálculo se aproxima al valor real, pero el cálculo mediante la división parece más preciso, seguramente a que se
necesitan 3 parámetro en lugar de 2 para el cálculo.

Si adquirimos directamente valores del sensor, aparecen unos picos de ruido que molestan para utilizar el valor en futuras aplicaciones.Por ello se
recomienda filtrar el ruido, por ejemplo mediante el cálculo del valor medio de las 10 últimos valores del sensor leídos. En la gráfica podemos ver en
azul el valor del sensor sin filtrar, y en rojo el valor filtrado.
En el siguiente ejemplo se muestra un ejemplo de la lectura del sensor aplicando dicho filtrado, y se muestra el valor instantáneo leído, el valor medio
calculado, y los cálculos de la distancia mediante las dos formulas:
int lecturas[10]={0,0,0,0,0,0,0,0,0,0};
int sensorPin = A0; // Entrada del sensor
int sensorValue = 0; // Valor instantáneo leído del sensor
int sensor=0; // Valor filtrado del sensor
float distancia=0; // Distancia calculada
void setup() {
Serial.begin(9600);
}
void loop() {
sensorValue = analogRead(sensorPin); // lectura del valor del sensor
Serial.print(sensorValue);
Serial.print(",");
sensor=filtra(sensorValue,lecturas); // Filtra mediante la media de los 10 últimos valores
Serial.print(sensor);
distancia=(float)(2580/(sensor-4.125))-0.9375; // Calcula distancia mediante división
Serial.print(",");
Serial.print(distancia);
distancia=(float)4615*pow(sensor,-1.1207); // Calcula distancia mediante la potencia
Serial.print(",");
Serial.println(distancia);
}
int filtra(int sensorValue, int vector[]){
int valormedio=0;
vector[9]=sensorValue; // Añade la última lectura del sensor al final del vector
for (int i=0;i<10;i=i+1){ // Calcula la media del vector
valormedio=valormedio+vector[i];
};
valormedio=valormedio/10;
// Desplaza el vector para después añadir la última lectura del sensor al final
for (int i=0;i<9;i=i+1){
vector[i]=vector[i+1];
};
return valormedio;
}

https://naylampmechatronics.com/blog/55_tutorial-sensor-de-distancia-sharp.html

Configurar módulo Bluetooth HC-05 o similar con Arduino


El módulo HC-05 tiene dos modos AT de configuracion (AT1 y AT2). Para poder acceder al modo de configuración (modo AT) del módulo HC-05 con
Arduino, utilizaremos dos puertos series. El puerto serie propio de Arduino accesible mediante USB, y en el caso de Arduino Uno un puerto serie virtual
mediante software, utilizando la libreria SoftwareSerial, al cual conectaremos el módulo Bluetooth. En el ejemplo configuraremos en Arduino, mediante
la libreria SoftwareSerial, los pines 3 como transmisión(TX) y 2 como recepción(RX). Por ello conectaremos el pin de TXD del HC-05 al pin 2 de
Arduino (TXD->RX), y el pin RXD del HC05 al pin 3 de Arduino (RXD<-TX):
Utilizaremos el siguiente código de Arduino, donde los datos recibidos por el puerto serie USB se enviarán al puerto serie del módulo Bluetooth, y los
datos recibidos por el puerto serie del módulo Bluetooth serén enviados al puerto serie USB. En caso de querer acceder a modo AT2, es importante
configurar el puerto del módulo Bluetooth a 38400 bauds, ya que es la velocidad de comunicación que utiliza el modo AT2 de configuración. El modo
AT2 es util para cuando desconocemos la velocidad de comunicación configurada en el módulo.
#include <SoftwareSerial.h>
SoftwareSerial SerieBT(2, 3); // RX, TX
void setup() {
Serial.begin(9600);
Serial.println("Introducir comandos AT:");
SerieBT.begin(38400); //Importante 38600 bauds para modo AT
}
void loop()
{
if (SerieBT.available())
Serial.write(SerieBT.read());
if (Serial.available())
SerieBT.write(Serial.read());
}

Una vez hemos traspasado el código al Arduino, activaremos el modo AT2 del módulo HC-05. Para ello, teniendo desconectada la alimentación del
módulo, pulsamos el botón cercano al pin KEY o EN del módulo, y manteniendolo pulsado conectamos la alimentación del módulo. Al cabo de unos
segundos, el led del módulo comenzará a parpadear de forma lenta con pulsos de unos 3 segundos, y esto indicará que se encuentra en modo AT de
configuración. El botón conecta el pin 34 del módulo HC-05 a nivel alto (5 o 3.3v)

En caso de querer activar el modo AT1, la velocidad será la configurada en el módulo HC-05 (por defecto es 9600bauds), y simplemente hay que
pulsar, y mantener pulsado, el botón en cualquier momento y cambiará a modo AT1, aunque en este modo el led no modificará su velocidad de
parpadeo.

Una vez en modo AT, podemos escribir los diferentes comandos AT para leer o configurar parámetros del módulo. Todos los comandos finalizan con
\r\n, y en algún caso se puede añadir el interrogante al solicitar parámetros. Normalmente se puede enviar ?AT+VERSION?\r\n?, de la misma forma
que ?AT+VERSION\r\n?.

Algunos de los comandos que se pueden utilizar para leer los parámetros són:

AT : Comprueba la conexión y devuelve OK.

AT+NAME=XXXX : Cambia el nombre del módulo a XXXX

AT+NAME : Devuelve el nombre del módulo (*)

AT+ADDR : Devuelve la dirección del módulo

AT+VERSION : Devuelve la versión del módulo

AT+UART : Devuelve la velocidad (baudrate)

AT+ROLE : Devuelve el rol del módulo (1=master/0=slave/2=slave-loop)

Slave: Conexión pasiva. Espera que se conecte un dispositivo maestro.


Master: Busca e intenta conectarse a un dispositivo esclavo con SPP, y al conectarse permite una tranmisión transparente de datos entre el
maestro y el esclavo.
Slave-Loop: Conexión pasiva. Al conectarse con un dispositivo maestro, los datos recibidos del maestro son retornados al mismo dispositivo
maestro.

AT+RESET : Resetea el módulo y sale del modo AT de configuración

AT+ORGL : Reinicia parámetros a valores de fábrica

AT+PSWD: Devuelve el password por defecto

(*)Algunos comandos necesitan que esté pulsado el botón para que devuelva algún tipo de resultado, como por ejemplo AT+NAME.

Listado de comandos: https://www.itead.cc/wiki/Serial_Port_Bluetooth_Module_(Master/Slave)_:_HC-05

Referencias Bluetooth

Para más información, mirar en los manuales u otras webs como:

http://wiki.pinguino.cc/index.php/SPP_Bluetooth_Modules

http://www.martyncurrey.com/arduino-with-hc-05-bluetooth-module-at-mode/#comment-4042.

http://www.instructables.com/id/AT-command-mode-of-HC-05-Bluetooth-module/?ALLSTEPSç

En el caso de Bluetooth v.4 LE (Low Energy), la compañia JNHuaMao Technology Company creó el módulo HM-10, pero otras compañias como
Bolutek han realizado una copia similar que se puede encontrar con nombres como CC41A, HC-10, MLT-BT05 o AT-09. En el siguiente enlace se
puede encontrar más información, así como el enlace a un sketch para Arduino que permite identificar automáticamente el tipo de módulo.

http://blog.yavilevich.com/2016/12/hm-10-or-cc41-a-module-automatic-arduino-ble-module-identification/

Módulo Bluetooth v.4 BLE HM-10

El módulo HM-10 se encuentra en modo AT por defecto excepto cuando esté conectado a algún dispositivo. Para poder enviar comandos AT, NO hay
que enviar ningún retorno de carro (CR) ni fin de línea (LF), sinó directamente el comando AT.

La documentación se puede encontrar en:

http://www.jnhuamao.cn/Bluetooth40_en.zip

Información sobre el servicio iBeacon:

http://www.ibeacon.com/what-is-ibeacon-a-guide-to-beacons/
* Por algún problema de seguridad, los navegadores detectan la web como sitio atacante.

Para consultar el estado de la mayoria de comandos AT, se añade un interrogante (?) tras el comando (ej.: AT+IBEA?). En la documentación se
encuentran todos los comandos, pero algunos de los comandos interesantes son:

• AT simplemente devuelve OK y nos informa de que el módulo está activo y a la espera de nuevos comandos
• AT+ADDR? devuelve la dirección MAC del módulo HM-10
• AT+VERR? devuelve la versión actual del firmware
• AT+RENEW revierte el módulo a la configuración de fábrica
• AT+RESET reinicializa el módulo
• AT+MODEx cambia el funcionamiento del módulo de cara al procesamiento de comandos AT
• AT+MODE0 (valor por defecto). Sólo acepta comandos AT vía la conexión serie (TXD/RXD) hasta que se conecta un dispositivo BLE central
al módulo
• AT+MODE2 Igual que el MODE0 pero,una vez establecida la conexión, se pueden enviar comandos AT desde el dispositivo BLE central. Por
ejemplo se puede cambiar el valor de uno de los pines GPIO del módulo HM-10 sin necesidad de un micro adicional.
• AT+UUID0xnnnn cambia el UUID del servicio utilizado por el módulo HM-10 para encapsular la conexión serie (el valor por defecto es
0xFFE1)
• AT+CHAR0xnnnn cambia el UUID de la característica utilizada por el módulo HM-10 para encapsular la conexión serie (el valor por defecto
es 0xFFE1)
• AT+PIOxy (x: 2-B ; y: 0-1) Permite activar el pin GPIO x del módulo HM-10 con el valor y (0 Off; 1 On). Por ejemplo para encender el GPIO 2
hay que enviar AT+PIO21
• AT+BEFCxyz define el valor de los pines GPIO 2 a B cuando el módulo es alimentado (valores permitidos 000 a 3FF). Por ejemplo para que
configurar a alto el GPIO2 nada más alimentar el módulo, hay que utilizar el comando AT+BEF200
• AT+NAMExxxxxxxxx permite cambiar el nombre del módulo
• AT+ROLEx (x: 0-1; 0 periférico; 1 - central; valor por defecto 0) permite definir el rol del módulo HM-10. En el caso de configurar el módulo
como un dispositivo central, se pueden utilizar los comandos AT+DISC?, AT+CON y AT+CONN para conectarlo a otro dispositivo periférico
• AT+IBEAx Activa o desactiva el modo iBeacon (0 Off; 1 On). Una vez configurado como iBeacon, el módulo deja de encender el led de la
placa y está dormido la mayorparte del tiempo con lo que no responde a los comandos AT Para despertar el módulo y poder mandarle
nuevos comandos AT, hay dos opciones: Enviarle una cadena larga (más de 80 bytes) de caracteres sin la cadena AT (repetir varias veces
hasta que recibamos de respuesta OK+WAKE; Conectar el pin PIO0/System Key (pin BRK en la placa breakout) a tierra/GND durante más
de un segundo.
• AT+ADCx? Query module address (4<x<B)
• AT+ADVI? Query/Set Advertising interval
• AT+ADTY? Query/Set Advertising Type
• AT+ANCS? Query/Set ANCS switch
• AT+ALLO? Query/Set whitelist switch ; Set whitelist mac address
• AT+ADx?? Query whitelist mac address (1<x<3)
• AT+BEFC? Query/Set Module pin output state, After power supplied
• AT+AFTC? Query/Set Module pin output state, After connection is established
• AT+BATC? Query/Set battery monitor switch
• AT+BATT? Query battery information (Android onLeScan() Data format is 0x02, 0x16, 0x00, 0xB0, [FLAG], [temperature], [ humidity],
[battery].)
• AT+BAUD? Query/Set baud rate (0-->9600)
• AT+COMI? Query/Set Minimum Link Layer connection interval
Más informacion: http://wiki.makespacemadrid.org/index.php?title=M%C3%B3dulo_HM-10

Módulo Bluetooth v.4 BLE HC-10 / AT-09/ CC41A / MLT-BT05

El módulo CC-41A se encuentra en modo AT por defecto. Para poder enviar comandos AT, hay que enviar los carácteres de retorno de carro y fin de
línea (CR&LF) tras el comando AT.

Al enviar AT+HELP, aparece el listado de comandos disponibles:


********************************************************************
* Command Description *
* ---------------------------------------------------------------- *
* AT Check if the command terminal work normally *
* AT+RESET Software reboot *
* AT+VERSION Get firmware, bluetooth, HCI and LMP version *
* AT+HELP List all the commands *
* AT+NAME Get/Set local device name *
* AT+PIN Get/Set pin code for pairing *
* AT+PASS Get/Set pin code for pairing *
* AT+BAUD Get/Set baud rate *
* AT+LADDR Get local bluetooth address *
* AT+ADDR Get local bluetooth address *
* AT+DEFAULT Restore factory default *
* AT+RENEW Restore factory default *
* AT+STATE Get current state *
* AT+PWRM Get/Set power on mode(low power) *
* AT+POWE Get/Set RF transmit power *
* AT+SLEEP Sleep mode *
* AT+ROLE Get/Set current role. *
* AT+PARI Get/Set UART parity bit. *
* AT+STOP Get/Set UART stop bit. *
* AT+START System start working. *
* AT+IMME System wait for command when power on. *
* AT+IBEA Switch iBeacon mode. *
* AT+IBE0 Set iBeacon UUID 0. *
* AT+IBE1 Set iBeacon UUID 1. *
* AT+IBE2 Set iBeacon UUID 2. *
* AT+IBE3 Set iBeacon UUID 3. *
* AT+MARJ Set iBeacon MARJ . *
* AT+MINO Set iBeacon MINO . *
* AT+MEA Set iBeacon MEA . *
* AT+NOTI Notify connection event . *
* AT+UUID Get/Set system SERVER_UUID . *
* AT+CHAR Get/Set system CHAR_UUID . *
* -----------------------------------------------------------------*
* Note: (M) = The command support slave mode only. *
* For more information, please visit http://www.bolutek.com *
* Copyright@2013 www.bolutek.com. All rights reserved. *
********************************************************************

Los otros modelos devuelven una ayuda similar.

Módulo WIFI ESP-01 basado en ESP8266


El módulo ESP-01 es un dispositivo de comunicaciones WIFI basado en ESP8266, de bajo coste. Puede conectarse mediante puerto UART a Arduino
u otro sistema, además de poder programarse directamente mediante Arduino IDE aunque en este caso solo dispondremos de 4 GPIO, o 2 GPIO y un
puerto UART.

Existen varias versiones en función de la memoria: ESP-01 placa azul con 512Mb, ESP-01S placa negra con 1Mb, o placa negra con 8Mb.

Los pines del ESP-01 son los siguientes:

PIN Identificador Descripción


1 GND Masa
2 GPIO2 Entrada de propósito general pin 2
3 GPIO0 Entrada de propósito general pin 0
4 RXD Recepción de puerto serie (3.3v). Entrada de propósito general GPIO3 pin 3
5 TXD Transmisión de puerto serie (3.3v). Entrada de propósito general GPIO1 pin 1
6 CH_PD Enciende o apaga el ESP-01: 0v (LOW) apagado, 3.3v (HIGH) encendido
7 RESET Reset del módulo: 0v(LOW) resetea
8 Vcc Alimentación ESP-01. Funciona entre 3.3v y 3.6v. Necesita una corriente mayor a 200mA
Es importante tener en cuenta que la alimentación es de 3.3v, y necesita una corriente mayor a 200mA. Por ello no se recomienda conectar a la
alimentación de 3.3v de Arduino, ya que esta solo proporciona hasta 50mA. En caso de transmisiones esporádicas y cortas, no es necesario tanta
corriente y Arduino puede ser capaz de proporcionar la necesaria, aunque no se asegura.

Actualizar firmware ESP-01

- Si no se sabe que firmware incorpora el módulo, se recomienda actualizarlo a una versión que esté actualizada. Para ello se realiza la siguiente
conexión, en que se conecta el GPIO0 a GND para indicar que se actualizará el firmware:

Como el módulo trabaja a 3.3v, no es recomendable conectar directamente los pines a salidas a 5v, aunque no suele haber ningún problema si las
comunicaciones con el módulo no se realizan de forma continua.

- Una vez conectado, cargamos en Arduino IDE un Sketch vacio, como el que encontramos en: Archivos --> Ejemplos --> 01.Basics --> BareMinimum
void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}

- Bajamos el siguiente archivo que contiene todo lo necesario para la actualización: ESP8266_flasher.zip.

- Ejecutamos el archivo esp8266_flasher.exe, pulsamos el botón BIN, y cargamos el firmware de actualización v0.9.2.4 AT Firmware-ESPFIX.bin, o
el que queramos en función de la versión. - Seleccionamos el puerto donde está conectado Arduino, y pulsamos download. Al final puede aparecer un
error, que podemos ignorar.
Ya tenemos actualizado el módulo ESP-01. Desconectamos el pin GPIO0 y ya podemos utilizar el módulo con Arduino:

Con el esquema anterior no podríamos utilizar la aplicación de monitor incluida en Arduino IDE, ya que utiliza los mismos pines del puerto serie.

Por ello se recomienda realizar las siguientes conexiones y utilizar la libreria SoftwareSerial, y poder utilizar el monitor de Arduino.
Debido a que la librería crea un puerto serie virtual por software, puede haber problemas en función de la velocidad de comunicación. Por ello se
recomienda cambiar la velocidad del módulo a un valor inferior o igual a 19600bps. Para cambiar la velocidad a 9600, por ejemplo, se enviaría al
módulo ESP-01 el comando:

AT+CIOBAUD=9600

Código ejemplo servidor Web, directamente mediante comandos AT.


#include <SoftwareSerial.h>
SoftwareSerial esp8266(2,3); // RX, TX
void setup() {
Serial.begin(9600); //Inicializa puerto serie para el monitor
esp8266.begin(9600); //Inicializa el puerto serie de comunicación con el ESP8266
Serial.println("Iniciando...");
enviarESP("AT+RST\r\n",2000); // Reinicia el módulo
//Conecta al router o AP, indicando SSID y el Password
enviarESP("AT+CWJAP=\"BaixaMar\",\"EresUnHacker?Bienvenido:-)\"\r\n", 2000);
delay(5000); // Tiempo de espera extra para conectar con el router o AP
enviarESP("AT+CWMODE=3\r\n",1000); // Configura modo cliente y servidor
enviarESP("AT+CIFSR\r\n",1000); // Muestra la IP de cliente y servidor
enviarESP("AT+CIPMUX=1\r\n",1000); // Multiples conexiones.
enviarESP("AT+CIPSERVER=1,80\r\n",1000); // Crea un servidor en el puerto 80
}
void loop() {
if(esp8266.available()) // Consulta si el módulo está enviando algún mensaje
{
//cuando el módulo recibe datos por red, devuelve los datos mediante un comando +IPD
// Si +CIPMUX=1, envia +IPD=<id>,<longitud>:<datos>
if(esp8266.find("+IPD,")) //Lee hasta encontrar el comando +IPD,
{
delay(1000); //Espera recibir todos los datos
int conexionId = esp8266.read()-48; //Resta 30h para convertir de ASCII a int
Serial.println("Identificador "+String(conexionId));
//Crea la pagina web en formato HTML para que la refresque cada 5 segundos:
// <head><meta http-equiv="refresh" content="5"</head>
// <h1> ESAII-EPSEVG </h1>
// Tiempo Arduino: XXX
// <br>Estado entrada 7: X
String paginaweb="<head><meta http-equiv=\"refresh\" content=\"5\"></head>";
paginaweb+="<h1> ESAII-EPSEVG </h1>Tiempo Arduino: ";
paginaweb+=String(millis());
paginaweb+="<br>Estado entrada 7: "+String(digitalRead(7));
//Cread el comando AT+CIPSEND=id,longitudDatos
String envioCip = "AT+CIPSEND="+String(conexionId)+","+String(paginaweb.length())+"\r\n";
//Envia la página de respuesta mediante el CIPSEND
Serial.println("Envia CIP");
enviarESP(envioCip,1000);
Serial.println("Envia Pagina");
enviarESP(paginaweb,1000);
// Cierra la conexión
String cierraCip = "AT+CIPCLOSE="+String(conexionId)+"\r\n";
Serial.println("Cierra conexion "+String(conexionId));
enviarESP(cierraCip,3000);
}
}
}
//Envia comandos y/o datos al esp8266, espera un tiempo, y devuelve la respuesta.
String enviarESP(String comando, int timeout) {
String respuesta=""; //Inicializa respuesta
esp8266.print(comando); //Envia el comando al módulo
long int timini=millis(); //Mira tiempo actual
while ((timini+timeout)>millis()) //Espera mientras no pase el timeout
{
while (esp8266.available()) // Mientras existan datos recibidos en el módulo
{
respuesta+=(char) esp8266.read(); //Lee los datos recibidos
//char c=esp8266.read();
//respuesta+=c;
}
}
Serial.print(respuesta); //Muestra la respuesta recibida
return respuesta; //Devuelve la respuesta de la función
}

Comandos AT de la última versión de ESP8266: https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf

Referencia: http://kio4.com/arduino/57modulowifi_2.htm

Enlaces Arduino
Enlaces a diferentes páginas sobre Arduino

https://www.arduino.cc/

http://portalarduino.com/index

http://garretlab.web.fc2.com/en/arduino/inside/index.html

S-ar putea să vă placă și