Autómata celular de una dimensión

En general, los sistemas pueden presentar uno de los siguientes tipos de comportamiento:

  • estabilidad
  • periodicidad
  • caos
  • complejidad

Los autómatas celulares en una dimensión fueron explorados ampliamente en el libro ‘a new kind of science’ de Stephen Wolfram.

El estado de dichos autómatas consiste en un vector de valores binarios. En cada cambio de estado del autómata, cada valor cambia en función de su estado actual y de sus ‘vecinos’. Existen 256 variantes de estos autómatas que se definen cada una por una regla.

Estos autómatas presentan variantes con los cuatro tipos de comportamiento mencionados anteriormente.

estabilidad

periodicidad

caos

complejidad

Programa para representar automatas celulares 1D:

En el programa siguiente puede seleccionar la regla del autómata a representar utilizando el slider. Es necesario javascript y un navegador moderno.


La regla del 72

La regla del 72 permite calcular ‘de cabeza’ cuanto tarda en duplicarse una cantidad rentando a un determinado interés.

La regla consiste en dividir 72 entre el interés para obtener el tiempo que tarda en duplicarse la cantidad. En el caso de interés anual obtendremos directamente el número de años que tardaría en duplicarse la cantidad.

Supongamos que tenemos determinada cantidad al 4% de interés compuesto anual. El número de años que tardará en duplicarse esa cantidad será:

(1)   \begin{equation*} \frac{72}{4} = 18 \end{equation*}

Si lo comparamos con el cálculo exacto observamos que el error es pequeño:

(2)   \begin{equation*} 2={\left(1+\frac{4}{100}\right)}^n \end{equation*}

(3)   \begin{equation*} n\approx17.6 \end{equation*}

El siguiente gráfico compara el valor real con el valor obtenido por la regla del 72 para diferentes tipos de interés.

Aplicación de registro climático basada en ASP.NET MVC 4

En este artículo se describe una aplicación web basada en ASP.NET MVC 4 que obtiene datos de la estación meteorológica usb descrita en el artículo anterior permitiendo su análisis. El resultado se muestra en la siguiente imagen:

En primer lugar se modelan los datos. En este caso se ha definido una tabla con seis campos, el primero para la fecha de obtención de la medición y los cinco restantes para cada uno de los valores que retorna el sistema.

modelo de datos sensor estación meteorológica

Necesitamos obtener datos de los sensores de forma periódica y para ello utilizaremos Quarz.net. La instalación de Quartz.net se realiza utilizando Nuget desde el propio Visual Studio 11. Para representar gráficos instalaremos también DotNet.HighCharts.

Package Manager Console Host Version 1.7.30402.9028
Type 'get-help NuGet' to see all available NuGet commands.
PM> Install-Package Quartz
Attempting to resolve dependency 'Common.Logging (≥ 2.0.0)'.
Successfully installed 'Common.Logging 2.0.0'.
Successfully installed 'Quartz 2.0.0'.
Successfully added 'Common.Logging 2.0.0' to MvcApplication1.
Successfully added 'Quartz 2.0.0' to MvcApplication1.
PM> Install-Package DotNet.Highcharts
Successfully installed 'DotNet.Highcharts 1.2'.
Successfully added 'DotNet.Highcharts 1.2' to MvcApplication1.
PM>

A continuación en el fichero global.asax configuraremos una tarea periódica que se ejecute cada minuto con el objetivo de obtener datos de los sensores y almacenarlos en la base de datos.

void ScheduleReadSensor0()
{
    var schedFact = new StdSchedulerFactory();
    // get a scheduler
    IScheduler sched = schedFact.GetScheduler();
    sched.Start();
    // construct job info
    var schedule = SimpleScheduleBuilder.RepeatSecondlyForever(60);
    var trigger = TriggerBuilder.Create().StartNow().WithSchedule(schedule).Build();
    var job = new JobDetailImpl("Sensors", typeof(Jobs.ReadSensorsJob));
    sched.ScheduleJob(job, trigger);
}

La tarea simplemente obtiene datos del sensor y les almacena.

void ReadSensors()
{
    var portName = Properties.Settings.Default.Sensor0PortName;
    //Si el puerto serie existe leemos
    if (SerialPort.GetPortNames().Contains(portName))
    {
        using (SerialPort port = new SerialPort(portName, 9600))
        {
            port.ReadTimeout = 5000;
            port.DtrEnable = false;
            port.Open();
            port.Write("g");
            var line = port.ReadLine();
            var vals = (from v in line.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
                        select XmlConvert.ToSingle(v)).ToArray();
            //Si todo ha ido bien almacenamos los datos
            Store(vals[0], vals[1], vals[2], vals[3], vals[4]);
        }
    }
}

void Store(Single t1, Single h, Single l, Single p, Single t2)
{
    //Almacenamos los datos en la base de datos
    using (var db= new SantuarioEntities())
    {
        var sr = new Sensor0Readings()
        {
            TimeStamp = DateTime.Now,
            Temperature1 = t1,
            Temperature2 = t2,
            Pressure = p,
            Humidity = h,
            Luminance = l
        };
        db.Sensor0Readings.AddObject(sr);
        db.SaveChanges();
    }
}

Por defecto los procesos ASP.NET se detienen pasados 20 minutos de inactividad por lo que un primer paso para que el registro se produzca de manera continua es aumentar ese límite. Para ello en la configuración de IIS, dentro de la configuración avanzada del grupo de aplicaciones establecemos el valor a 0, que significa indefinido.

Aun así, por diversas razones el proceso se podría detener por lo que es recomendable configurar una tarea periódica que solicite una página al servidor provocando su arranque en caso de que esté detenido.

Ya solo queda implementar un controlador y una vista de ASP.NET MVC que se encargue de obtener los datos y representarles.

Se ha implementado una acción de controlador cuya lógica se divide en obtener los datos y generar un gráfico a partir de ellos.

La parte de obtención de los datos:

//span define el tamaño en minutos de cada intervalo
//nSpan el número de intervalos a representar
if (endTime == null)
    endTime = DateTime.Now;

var lastMeasure = (from m in db.Sensor0Readings
                    where m.TimeStamp <= endTime.Value
                    orderby m.TimeStamp descending
                    select m).FirstOrDefault();
//Si no hay medidas o la medida es muy vieja
if (lastMeasure == null || (endTime.Value - lastMeasure.TimeStamp) > TimeSpan.FromMinutes(60))
{
    ViewBag.Temperature = "Desconocida";
}
else
{
    ViewBag.ElapsedMinutes = (endTime.Value - lastMeasure.TimeStamp).TotalMinutes.ToString("F0");
    ViewBag.Temperature = string.Format("{0:F1} ºC", lastMeasure.Temperature1);
    ViewBag.Humidity = string.Format("{0:F1} %", lastMeasure.Humidity);
    ViewBag.Pressure = string.Format("{0:F1} mb", lastMeasure.Pressure);
}
//Obtenemos datos de los ultimos n mins
List<DateTime> limitPoints = new List<DateTime>();
List<DateTime> intervals = new List<DateTime>();
List<object> pressures = new List<object>();
List<object> temperatures = new List<object>();
List<object> humidities = new List<object>();
//Establecemos los puntos limite
for (int i = 0; i < nSpan + 1; i++)
{
    limitPoints.Add(endTime.Value - TimeSpan.FromMinutes(i * span));
}
//Obtenemos los datos para los intervalos
for (int i = 0; i < nSpan; i++)
{
    var upperLimit = limitPoints[i];
    var lowerLimit = limitPoints[i + 1];
    var measurements = (from m in db.Sensor0Readings
                        where m.TimeStamp < upperLimit
                        where m.TimeStamp >= lowerLimit
                        select m).ToArray();
    double pAverage = 0;
    double tAverage = 0;
    double hAverage = 0;
    if (measurements.Length > 0)
    {
        pAverage = (from p in measurements
                    select p.Pressure).Average();
        tAverage = (from p in measurements
                    select p.Temperature1).Average();

        hAverage = (from p in measurements
                    select p.Humidity).Average();
    }
    var intervalLabel = limitPoints[i] - TimeSpan.FromMinutes(0.5 * span);
    intervals.Add(intervalLabel);
    pressures.Add(pAverage);
    temperatures.Add(tAverage);
    humidities.Add(hAverage);
}
intervals.Reverse();
pressures.Reverse();
temperatures.Reverse();
humidities.Reverse();

Y el código de la parte de generación del gráfico:

Highcharts chart = new Highcharts("chart")
    .InitChart(new Chart { ZoomType = ZoomTypes.Xy })
    .SetTitle(new Title { Text = "Registro climático" })
    .SetSubtitle(new Subtitle { Text = "citadel labs" })
    .SetXAxis(new XAxis
    {
        Type = AxisTypes.Datetime,
    })
    .SetYAxis(new[]
                            {
                                new YAxis
                                {
                                    Labels = new YAxisLabels
                                            {
                                                Formatter = "function() { return this.value +'°C'; }",
                                                Style = "color: '#89A54E'"
                                            },
                                    Title = new XAxisTitle
                                            {
                                                Text = "Temperatura",
                                                Style = "color: '#89A54E'"
                                            },
                                    Opposite = true,
                                },
                                new YAxis
                                {
                                    Labels = new YAxisLabels
                                            {
                                                Formatter = "function() { return this.value +' %'; }",
                                                Style = "color: '#4572A7'"
                                            },
                                    Title = new XAxisTitle { Text = "Humedad relativa", Style = "color: '#4572A7'" },
                                    GridLineWidth = 0
                                },
                                new YAxis
                                {
                                    Labels = new YAxisLabels
                                            {
                                                Formatter = "function() { return this.value +' mb'; }",
                                                Style = "color: '#AA4643'"
                                            },
                                    Title = new XAxisTitle
                                            {
                                                Text = "Presión",
                                                Style = "color: '#AA4643'"
                                            },
                                    GridLineWidth = 0,
                                    Opposite = true
                                }
                            })
    .SetTooltip(new Tooltip { Formatter = "TooltipFormatter" })
    .AddJavascripFunction("TooltipFormatter",
                            @"var unit = {
                    'Humedad relativa': '%',
                    'Temperatura': '°C',
                    'Presión': 'mb'
                    }[this.series.name];
                    var xDate = new Date(this.x);
                return ''+
                    xDate+': '+ this.y.toFixed(2) +' '+ unit;")
    .SetPlotOptions(new PlotOptions
    {
        Spline = new PlotOptionsSpline
        {
            Marker = new PlotOptionsLineMarker { Enabled = false },
            LineWidth = 3,
            DashStyle = DashStyles.ShortDot,
            PointInterval = 3600000,
            PointStart = new PointStart(intervals[0])
        },
        Column = new PlotOptionsColumn
        {
            PointInterval = 3600000,
            PointStart = new PointStart(intervals[0])
        }
    })
    .SetSeries(new[]
                            {
                                new Series
                                {
                                    Name = "Humedad relativa",
                                    Color = ColorTranslator.FromHtml("#804572A7"),
                                    Type = ChartTypes.Column,
                                    YAxis = 1,
                                    Data = new Data(humidities.ToArray())
                                },
                                new Series
                                {
                                    Name = "Presión",
                                    Color = ColorTranslator.FromHtml("#AA4643"),
                                    Type = ChartTypes.Spline,
                                    YAxis = 2,
                                    Data = new Data(pressures.ToArray()),
                                },
                                new Series
                                {
                                    Name = "Temperatura",
                                    Color = ColorTranslator.FromHtml("#89A54E"),
                                    Type = ChartTypes.Spline,
                                    PlotOptionsSpline = new PlotOptionsSpline
                                        {
                                            Marker = new PlotOptionsLineMarker { Enabled = false },
                                            LineWidth = 3,
                                            DashStyle = DashStyles.Solid,
                                            PointInterval = 3600000,
                                            PointStart = new PointStart(intervals[0])
                                        },
                                    Data = new Data(temperatures.ToArray())
                                }
                            });

Por último, la vista quedaría:

@{
    ViewBag.Title = "Sensor0";
}

@model DotNet.Highcharts.Highcharts</pre>
<h2>Estado (Actualizado hace @ViewBag.ElapsedMinutes minutos)</h2>
<dl><dt>Temperatura actual:</dt><dd>@ViewBag.Temperature</dd><dt>Humedad:</dt><dd>@ViewBag.Humidity</dd><dt>Presión atmosférica:</dt><dd>@ViewBag.Pressure</dd></dl>
<pre>
@Model

Estación meteorológica digital usb con arduino

Utilizando un arduino nano, un módulo basado en el sensor SHT15 (temperatura y humedad), un módulo basado en el sensor MM1616 (iluminancia), y un módulo basado basado en el sensor MS5561C (presión y temperatura) se ha construido un sistema que permite registrar las siguientes variables del entorno:

  • Temperatura
  • Presión atmosférica
  • Humedad
  • iluminancia
ensamblaje estación meteorológica digital usb con arduino

ensamblaje estación meteorológica digital usb con arduino

El programa desarrollado para arduino proporciona un interface a través del puerto serie (9600 baudios). Al recibir el caracter ASCII ‘g’ el sistema obtiene medidas de cada uno de los sensores escribiendo sus valores en el puerto serie con el formato que se muestra en la siguiente figura:

lectura estacion meteorologica digital usb arduino

El orden de las mediciones el es siguiente:

  1. Temperatura (ºC, SHT15)
  2. Humedad (%)
  3. Iluminancia (lux)
  4. Presión atmosférica (mb)
  5. Temperatura (MS5561C)

Código fuente: Sketch Arduino para estación meteorológica

 

 

Sensor de humedad y temperatura con Arduino. Cuarta parte: Gadget para Windows 7

Este artículo es una continuación de los siguientes:

Utilizando la base desarrollada en artículos anteriores, se construyó un gadget para el escritorio de Windows 7 que nos muestra información sobre la temperatura y la humedad y cuyo aspecto se puede ver en la figura siguiente.

Gadget para Windows 7 para medición de Humedad y Temperatura con Arduino y sensor SHT15

Además del manifiesto que le define, el gadget está compuesto de Html/CSS, JavaScript e imágenes que definen su background. El siguiente artículo que explica como desarrollar un gadget para Windows resultó muy útil.

Toda la lógica ha sido implementada en JavaScript utilizando Jquery y las APIs de visualización de Google.

El gadget establece un timer que ejecutará una medición de forma periódica cada minuto utilizando ajax. Si la petición tiene éxito se ejecutará la función measureReceived.

petición de medicion de temperatura y humedad ajax

En la función measureReceived, ejecutada después de cada medición, se actualizarán los valores de las medidas, así como del gráfico que representa su historia. También se calcularán y actualizarán los estadísticos valor medio, mínimo y máximo de la humedad y temperatura.

El código fuente se adjunta al artículo.

Sensor de humedad y temperatura con Arduino. Tercera parte: HttpRelay.

Este artículo es una continuación de los siguientes:

El acceso a través del puerto serie resulta inconveniente para el propósito de crear un gadget para el escritorio de Windows 7, por lo que se hace necesario escribir un pequeño programa que nos permita trasladar peticiones HTTP convencionales a peticiones a través del puerto serie. Se ha escrito dicho programa y se ha denominado HttpRelay.

Diagrama de bloques de HttpRelay

Dicho programa se queda residente como un icono en la barra de notificaciones encargándose de trasladar las peticiones HTTP GET a comandos a través del puerto serie que se haya configurado. De esta forma, podemos comunicarnos con el Arduino a través de HTTP.

pantalla principal del programa HttpRelay

El programa se configura a través de un archivo xml que indica la URI en la que se debe escuchar y el puerto serie COM al que se delegará la respuesta, así como, la velocidad en bits por segundo a la que se debe utilizar el puerto.

configuración xml del programa HttpRelay

El programa traslada al puerto serie el parámetro de query llamado ‘q’ por lo que si deseamos obtener mediciones de temperatura y humedad con la configuración anterior, utilizando el comando ‘g’ definido en el firmware, deberemos utilizar la URL: ‘http://localhost:8080/?q=g’

image

El programa HttpRelay, escrito en c#, utiliza la clase HttpListener para realizar la escucha en una URL determinada. Su código fuente se adjunta en este artículo.

Control de led RGB con arduino

El modelo de led RGB utilizado teóricamente soporta hasta 5W y proporciona 70 lúmenes, sin embargo, en el circuito implementado, se controlará a una potencia mucho menor con una corriente máxima aproximada por color de 60ma. Aún así, esa corriente es demasiada para los puertos del arduino, por lo que se hace necesario intercalar un circuito controlador que soporte corrientes de esa magnitud.

El circuito controlador elegido es el chip ULN2003AN que utilizamos a modo de relé para encender y apagar cada led.

El led tiene cuatro puertos, un ánodo común y un cátodo para cada uno de los colores: rojo, verde y azul. Al combinarse estos colores en distintas proporciones podemos crear la sensación de estar viendo casi cualquier color.

El esquema de circuito se muestra en la siguiente figura:

esquema para el control de led rgb utilizando arduino

foto control led rgb con arduino

El sketch Arduino le programa para recibir mensajes de cambio de color del led vía el puerto serie en un formato ‘COLOR R G B’. Una vez recibido el mensaje utiliza la función analogWrite para controlar la corriente que circulara cada led vía modulación de anchura de pulso (PWM).

sketch en arduino para el control de leds rgb

Posteriormente se realizo un programa en .NET que permite especificar el color del led de una forma amigable. El siguiente video muestra el resultado:

Se adjunta un archivo con el código del sketch arduino y el programa de .NET.

Control de servomotores con Arduino y WPF

Los servomotores son motores que integran una circuitería de control que permiten posicionar su eje dentro de un rango. Típicamente los servomotores permiten posicionar el eje con precisión dentro de un rango angular de 0 a 180 grados aproximadamente.

Constan de tres señales de entrada: alimentación, referencia, y señal de control. Utilizando la señal de control se puede posicionar el motor dentro del rango. El control se realiza a través de pulsos de duración determinada de la señal de control. Por ejemplo, para posicionar un servomotor controlado con pulsos de 1msg a 2msg de rango 0-180 grados a 90 grados necesitaríamos suministrar a la señal de control un pulso de 1.5msg de duración.

Se conecta el control del servomotor a la salida digital número 9 del Arduino que programaremos para recibir mensajes de posicionamiento a través del puerto serie, interpretarles y posicionar el servomotor. Para ello utilizaremos las librerías de control de servos (Servo) y de delimitación de mensajes del puerto serie (Messenger).

La inicialización del programa del Arduino se muestra en la siguiente figura donde simplemente indicamos a la librería de control de servos que se utilizará el pin 9 para el control, inicializamos el puerto serie a 115200 baudios e inicializamos la librería de procesado de mensajes.

inicialización control servomotor arduino

Dentro del bucle principal simplemente se leerá el puerto serie esperando mensajes de la forma ‘SERVO n’ donde n es un número de 0 a 178 que indica el ángulo en grados donde se desea posicionar el motor. Una vez interpretado el mensaje satisfactoriamente se posicionara el servomotor y se añadirá un pequeño retraso de 15msg que permita al servomotor posicionarse antes de recibir otra orden.

bucle principal control servomotor arduino 

Para la aplicación WPF diseñaremos una clase con una única propiedad que permita establecer la posición del servo. Haremos que dicha propiedad sea ‘Bindable’ para poderla enlazar de forma natural con los controles de interface gráfico.

La parte principal de dicha clase se muestra en la figura siguiente:

clase controladora de servomotor en .net

y se creará un interface gráfico para el control en XAML enlazando a dicha propiedad de la siguiente manera:

xaml para el interface gráfico del control de servomotores con arduino

El resultado se puede ver en el siguiente video. El código fuente de ambos programas se adjunta en el artículo:

Termómetro con Arduino

Utilizando un Arduino y el chip sensor de temperatura TMP36 se construye un sencillo termómetro tal y como se propone en el circuito diez de la guía de experimentación de adafruit.

El circuito es extremadamente sencillo, basta conectar los pins de alimentación y referencia del sensor para alimentarle, y que de esta forma nos proporcione en su otro pin un voltaje proporcional a la temperatura, el cual leeremos a través de la entrada analógica número 0 del Arduino, cuyo ADC nos proporciona una resolución de 10 bits.

termometro-arduino-montaje

El programa cargado en el Arduino es simplemente un bucle que cada segundo lee el voltaje de la entrada analógica y le convierte en un valor de temperatura utilizando las indicaciones de la hoja de características del sensor, enviando posteriormente este valor por el puerto serie.

El programa de PC leerá de forma continua los valores de temperatura que le vayan llegando por el puerto serie virtual (USB) y representándoles en pantalla. Su código principal es el siguiente (Aplicacion WPF en C#):

image

Con lo cual podremos leer en la pantalla del PC el valor de temperatura procedente del sensor, el cual se actualizará cada segundo.

termometro-pc

Sincronización en la escala pentatónica

En el siguiente extracto de la conferencia ‘Notes & Neurons: a la busqueda de un coro común’ Bobby McFerring demuestra la capacidad de la gente del auditorio de sincronizarse en la escala pentatónica a través de una metáfora espacial.

World Science Festival 2009: Bobby McFerrin Demonstrates the Power of the Pentatonic Scale from World Science Festival on Vimeo.