sábado, 29 de diciembre de 2018

Arduino desbordamiento con millis()

millis(); devuelve el número de milisegundos (ms) desde que la placa Arduino empezó a ejecutar el sketch y tenindo en cuanta esto podemos tener un problema de desbordamiento a la hora de sumar dos variables unsigned long para compararlas.

Una variable  unsigned long no puede superar los 4.294.967.295. Por eso aproximadamente cada 50 días el reloj internos que nos entrega el valor de millis al llegar a 4.294.967.295 se reinicia y nuevamente empieza a contar desde 0.

Imaginemos que nuestro sketch se empezó a ejecutar hace 10 minutos que son 600.000 ms y justo en ese instante le pedimos que encienda un led durante 2 segundos, no tendríamos ningún problema, puesto que cuando millis llegue a 602.000 ms lo apagara.

Entonces que pasaría si nuestro sketch se empezó a ejecutar hace 4.294.967.000 ms pues que 4.294.967.000 + 2.000 son 4.294.969.000 ms,  y como hemos dicho una variable unsigned long solo puede almacenar hasta 4.294.967.295 ms. Esto nos provocaría un desbordamiento y el sketch fallaría.

Para proteger nuestro sketch de un desbordamiento, antes de sumarle al valor obtenido por millis (4.294.967.000ms) los 2 segundos que queremos que mantenga el led encendido (2000ms), tenemos que saber si a esa variable es capad de almacenar esa cifra para no provocar un desbordamiento. Para ello, al valor máximo que puede almacenar un unsigned long 4.294.967.295 le tenemos que restar el valor que nos ha entregado millis(); y el resultado obtenido es el numero máximo de ms que podemos sumarle a los millis(); obtenidos sin que se produzca un desbordamiento.

[valor máximo de un unsigned long] - [valor entregado por millis();] = [Es el numero máximo de ms que podemos sumar]

4.294.967.295 - 4.294.967.000ms = 295ms

Entonces deberíamos de utilizar algún if o algo para asegurarnos de que no se nos va a producir un desbordamiento al usar millis:



unsigned long nUnsignedLong = 4294967295; //Cifra máxima almacenable en un unsigned long
unsigned long Lectura = millis();//valor obtenido desde millis
unsigned long tiempo = 2000; //Cuanto tiempo queremos esperar o lo que sea


if ( (nUnsignedLong - Lectura) > tienpo ){
  
 //No producirá un desbordamiento 
 
}
else {
  //produce un desbordamiento
}



Incluso si ya sabemos que se nos va a desbordar, podemos anticiparnos y calcular cuando se tiene que apagar el led del que halábamos incluso evitando el desbordamiento:

4.294.967.295 - 4.294.967.000ms = 295ms

2000 (tiempo encendido) - 295 = 1705ms

Nuestro led se tendría que apagar cuando millis llegue a los 1705ms

Con lo cual tendría que utilizar algo similar a esto para compensar  el resultado:


unsigned long nUnsignedLong = 4294967295; //Cifra maxima almacenable en un unsigned long
unsigned long Lectura = millis();//valor obtenido desde millis
unsigned long tiempo = 2000; //Cuanto tiempo queremos esperar o lo que sea
unsigned long ApagaLed = 0; //Cuado apagara el led...

if ( (nUnsignedLong - Lectura) > tienpo ){
  
 //No producira un desbordamiento 
 ApagaLed = Lectura + tiempo;
 
 
}
else {
  //Compensamos el desbordamiento
  
  ApagaLed = tiempo - (nUnsignedLong - Lectura);
}



miércoles, 26 de diciembre de 2018

Arduino El dato pasado por serial es Numero o Caracter

Estoy con una parte del programa, en el cual solo podemos recibir números y para distinguir entre numero o letra he realizado este pequeño programa.

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600); //Inicializamos el serial

}

void loop() {
  
  char ch = ' ';       //Variable donde se guarda el char que se recibe por serial.
  bool TodoNumeros = true; //Para saber si todos los caracteres recividos son numeros
  String Mensaje ="" ; //Variable para guardar el texto pasado por serial.
  
  //Lee serial
  if (Serial.available()) //Si el serial esta libre...
    {
    while( ch != '\n') //leemos hasta el retorno de carro o enter.
      { 
      ch = Serial.read(); //Lee un caracter.
   
      TodoNumeros = EsNumero(ch);
      
      if (TodoNumeros == true) Mensaje = Mensaje + ch ; //lo anadimos a la variable mensaje.
 
      delay(25); //Esperamos 25ms para evitar colixiones de datos.
      }//Fin del while
      
     }//Fin del if
     Serial.print ("Leido: ");
  Serial.println (Mensaje);
  delay(2000);
}//Fin Loop

//Esta funcion analiza si el CHAR pasado es numero o un caracter
bool EsNumero(char n){
  
  bool Numero = false;// Valor por defecto false para que entregue un error
 
  switch ( n ){
    
   case '0':  Numero = true;  break;
   case '1':  Numero = true;  break;
   case '2':  Numero = true;  break;
   case '3':  Numero = true;  break;
   case '4':  Numero = true;  break;
   case '5':  Numero = true;  break;
   case '6':  Numero = true;  break;
   case '7':  Numero = true;  break;
   case '8':  Numero = true;  break;
   case '9':  Numero = true;  break;
  
   default:
     Numero = false;// Si no es numero da error
   break;
   
  }
  /*
  Serial.print ("");
  Serial.print ("Caracter: ");
  Serial.println (n);
  Serial.print ("Bolean: ");
  Serial.println (Numero);*/
  delay(200);
  
  return Numero;
}// Fin EsNumero

Arduino Reloj en tiempo real DS1307

Esta mañana cuando he querido utilizar el Tiny RTC que tenía por casa los códigos que tenía no me compilaban bien. Para solucionarlo me he bajado la libreria time de:  https://github.com/PaulStoffregen

Mientras aprendía de nuevo a utilizar el RTC he programado este pequeño código gracias al cual he comprendido como se utiliza el RTC y ahora tengo todo lo que necesito para implementarlo al programa con el que estoy trabajando.


#include <Wire.h> // Comunicacion I2C 
#include "RTClib.h" // libreria ADAFRUIT para DS3231 
RTC_DS1307 RTC;

int input; //Para ller el numero pasado por serial

void setup() {
  Wire.begin(); // Inicia el puerto I2C
  RTC.begin(); // Inicia la comunicación con el RTC 
  
  Serial.begin(9600); // Establece la velocidad de datos del puerto serie
  
  Datos(); //Imprimimos el menu de comandos
  
}// Fin de Setup

void loop() {

if (Serial.available()>0){
  
  input=Serial.read(); //Leemos el serial
  
  if (input=='1') {
    PrintFecha(); //Si el caracter leido es 1 imprime la fecha
    }
    else if (input=='2'){
      PrintHora(); //Si el caracter leido es 1 imprime la fecha
    }
    else if (input=='5'){
      PonerNuevaFecha(); //Si el caracter leido es 5 Cambia la fecha y la hora
    }
    else if (input=='9'){
       Sincronizar(); //Si el caracter leido es 1 imprime la fecha
    }
    
  }//Fin if serial
}

void PrintFecha() {
 
 DateTime ahora = RTC.now(); // captura valores del tiempo
 
 Serial.print("Fecha:  ");
 Serial.print(ahora.day(), DEC);
 Serial.print('/');
 Serial.print(ahora.month(), DEC);
 Serial.print('/');
 Serial.print(ahora.year(), DEC);
 Serial.println();
}//Fin PrinFecha

void PrintHora() {
 
 DateTime ahora = RTC.now(); // captura valores del tiempo

 Serial.print("Hora:  ");
 Serial.print(ahora.hour(), DEC);
 Serial.print(':');
 Serial.print(ahora.minute(), DEC);
 Serial.print(':');
 Serial.print(ahora.second(), DEC);
 Serial.println();
 
}//Fin PrintHora

void Sincronizar (){
  Serial.println();
  Serial.println ("  Nueva Fecha Sincronizada: ");
  Serial.println ("-----------------------------");
  RTC.adjust(DateTime(__DATE__, __TIME__)); // Establece la fecha y hora del sistema
  PrintFecha();
  PrintHora();
  Serial.println ("-----------------------------");
}//Fin Sincronizar


void PonerNuevaFecha  (){
  RTC.adjust(DateTime(2010, 07, 10, 12, 05, 00));//DateTime(ano, mes, dia, hora, min, seg)
  Serial.println();
  Serial.println ("  Nueva Fecha actualizada: ");
  Serial.println ("----------------------------");
  PrintFecha();
  PrintHora();
  Serial.println("---------------------------");
  Serial.println();
}//Fin Sincronizar

void Datos(){
  Serial.println();
  Serial.println();
  Serial.println ("  Introduce un comando: ");
  Serial.println ("----------------------------");
  Serial.println (" 1 : Imrime la Fecha del RTC ");
  Serial.println (" 2 : Imrime la Hora del RTC ");
  Serial.println (" 5 : Modifica e imprime la Fecha yla Hora del RTC ");
  Serial.println (" 9 : Sincroniza e imprime la Hora y la Fecha del RTC ");
  Serial.println();
  Serial.println();
}//Fin Datos

Arduino Función que pasa el valor leído en un puerto analógico a Byte

En un programa que estoy desarrollando, necesitaba almacenar el valor leído por una LDR en una EEprom y no necesitaba mucha precisión, por eso he decidido hacer esta función que calcula una regla de tres para pasar los valores leídos por la LDR a Byte y así poder trabajar con ellos más cómodamente cuando necesite hacer uso de ellos.

/* Funcion que entrega una lectura analogica como Byte */
/* By Aitor Martin */

const int LDRPin = A0;   //Pin del LDR
const int ValorEntrada = 1023; //Valor maximo que lee la entrada analogica
 
void setup() 
{
   Serial.begin(9600);  //inicializamos el serial
}// Fin setup
 
void loop()
{
  long Valor = 0;
 
  Valor = analogRead(LDRPin);// Leemos el pin de la LDR

  Serial.print("Valor entrada: "); // Imprimimos varios datos
  Serial.println(ValorEntrada);
  
  Serial.print("Leido: ");
  Serial.println(Valor);
  
  Serial.print ("Vlor en Byte: ");
  Serial.println (AnalogicoByte(Valor)); // Calculamos y imprimimos el valor leido en Bytes
  
  delay(10000);
}// Fin loop

/* La funcion que hace la regla de 3 para calcular la proporcion */

byte AnalogicoByte (int Valor){
  
  long ValorL; //Variable para los calculos matematicos
  
  ValorL = (Valor * 255) / ValorEntrada; // Regla de 3 para caldular la proporcion
  
  return ValorL; // Entrega el resultado
  
}//Final Valor leido

domingo, 16 de diciembre de 2018

Arduino funcion para leer puerto serial

Generalmente los códigos que he visto en internet para leer el puerto serial lo hacían directamente en el void loop(), pero a mi esto no me parece nada práctico, o por lo menos a mi me gusta trabajar separando un poco las cosas...

Parte de este código lo saque de internet hace años y no recuerdo de donde, tal vez en el post anterior sobre el puerto serial lo indeque...



/* 
/* Funciones del puerto Serial
/*
/* *********************************************************************** */

// Leer puerto Serial
String LeerSerial(){
  
  char ch = ' ';       //Variable donde se guarda el char que se recibe por serial.
  String mensaje ="" ; //Variable para guardar el texto pasado por serial.
  
  //Lee serial
  if (Serial.available()) //Si el serial esta libre...
    {
    while( ch != '\n') //leemos hasta el retorno de carro o enter.
      { 
      ch = Serial.read(); //Lee un caracter.
      
      mensaje = mensaje + ch ; //lo anadimos a la variable mensaje.
      
      delay(25); //Esperamos 25ms para evitar colixiones de datos.
      }
      
    //Serial.println( mensaje); //Imprimimos el mensaje
    
    return mensaje; //Pasamos el valor leido
    mensaje = "" ; //Borramos el string
     }
}

Escribir y leer datos de la Eeprom con arduino

El otro día aprendi a leer y escribir datos de la Eeprom de Arduino, y este es el código que estoy utilizando en mi programa...


// Escribimos en la Eprom la eprom el valor de sensivilida de la LDR
byte WValorLDR(byte ValorLDR){
  EEPROM.put(4, ValorLDR);//Escribimos en la Eprom Reg.4 el valor de la LDR que abre y cierra la puerta
}//Fin introducir datos LDR

/* ******* */


// Leemos datos almacenados en la Eeprom
void LDatosEprom(){
  
  int valor; //Variable para almacenar el dato ovtenido de la eprom
  int Direccion = 0;//Reseteamos el contador
 
  while(Direccion < 5){ //Recorremos los 5 registros de ls Eprom

    EEPROM.get(Direccion, valor); //leemos un registro y lo guardamos en la variable Valor
    
    DatosEprom[Direccion] = valor; //Guardamos la variable Valor en un Array
    
    Direccion++;
  }//Fin while
}//Fin LDatosEprom

Arduino determinar si es un número entre 0 y 255

A la hora de recuperar un número de una eeprom puede darse el caso de que ese contenido que hemos leído no sea un número ya sea por que no la hemos limpiado correctamente o pr que ahun no la hemos escrito. Esta función nos regresa true o false dependiendo si es número  entre 0 y 255...



/* Detectamos si los datos de la Eeprom son un numero entre 0 y 255 */
bool ValidarDatosEprom(){
  
  int valor; //Variable para almacenar el dato ovtenido de la eprom
  int Direccion = 0;//Reseteamos el contador
  
  bool DatosInCorrectos = false; //hasta que no se demuestre los datos de la Eeprom son correctos
  
  while(Direccion < 5){ //Recorremos los 5 registros del Array


    valor = DatosEprom[Direccion]; //Leemos el valor
    
    
    if ((valor >= 0) and (valor <= 255))
      {
        //si los datos leidos son un numero entre 0 y 255 no hace nada
      }
      else
      {
        DatosInCorrectos = true; //Si es diferente indica que hay un error
      }
      
    
    Direccion++;
  }//Fin while
  
  return DatosInCorrectos; //Entregamos si hay errores o no
  
}//Fin validar Datos de la Eprom