Domótica con Netduino y Kinect


Introducción

Hay un montón de mediocre  tecnología sobre el tema de la domotica  en el mercado siendo ademas  los productos  demasiado caros, así que el autor Dan Thyer  decidió construir su  propio sistema domótico. El autor empezó con el microcontrolador Arduino, pero el código rápidamente se convirtió en difícil de mantener porque la programación en Arduino   no esta orientada a objetos,  no puede  hacer multithreading  asi como tampoco permite depuración real con puntos de interrupción tal como se puede hacer con Netduino .Refactorizó entones  su  código para C # y  NET Micro Framework eligiendo  Netduino plus, http://www.netduino.com/netduinoplus/specs.htm , para el microcontrolador  (la versión Plus incluye  un adaptador de Ethernet para la comunicación de red).

Netduino controlado Pistola de agua

El primer proyecto  era una pistola de agua servo controlada para la piscina. El código  para la Netduino controla los servos para rociar el arma de fuego en diferentes patrones en la piscina. Entonces construyo una interfaz de Windows Phone 7 para apuntar los servos a la posición de la pantalla donde se toca. El  autor solía usar IIS Smooth Streaming para transmitir vídeo en el teléfono así que intento apuntar de forma remota a los niños en la piscina desde cualquier lugar. Así consiguió  resultados mixtos con la pieza de video  pero el autor reconoce  que necesitaría perfeccionar lo reduciendo el tiempo de almacenamiento en búfer para que sea más tiempo real.

NetduinoHomeAutomation/LogicalDan_002.jpg

Jardín

El próximo proyecto era controlar el riego del jardín habilitando los  horarios a través  de código los tiempos para regar el jardín y controlar la duración del riego.

NetduinoHomeAutomation/LogicalDan_004.jpg

Kinect

Muchas personas usan  para a hacer proyectos con el Kinect de Microsoft, que tiene una rica SDK completo con los controladores, APIs y un montón de buen código de ejemplo. El Kinect tiene un montón de sensores, incluyendo una cámara RGB, un sensor de profundidad y un micrófono de múltiples matrices. Con Kinect, usted es el controlador! de modo  que el autor tuvo  la idea de usar el Kinect para el controlador de la pistola de agua en la piscina de modo que puede puede apuntar el arma apuntando a donde usted quiera para disparar. El disparador se controla doblando su otro brazo para que la mano está por encima de la articulación del codo. La conexión del Kinect al  controlador fue  muy simple debido a la rica API Kinect y porque el autor había escrito los niveles de back-end para comunicarse con el microcontrolador Netduino.

NetduinoHomeAutomation / VideoSquirtGun.JPG
Vea este vídeo de la pistola de

Reconocimiento de voz en el Kinect

Otra de las características en el Kinect es el micrófono de múltiples matrices con reconocimiento de voz de modo que se puede jugar un poco con los comandos de voz para controlar la pistola de agua y para abrir el garaje.

Vea este vídeo de Simon Says Kinect

Android Puerta de cochera

Es autor ademas   escribio   una aplicación nativa para Android para llamar a un servicio web REST (WCF) que se comunica con el Netduino para abrir el garaje.

Poniendo todo junto

La imagen siguiente muestra la comunicación entre los componentes

NetduinoHomeAutomation/LogicalDan_005.jpg

La imagen de abajo muestra los dispositivos que controla el Netduino

NetduinoHomeAutomation/LogicalDan_006.jpg

NetduinoHomeAutomation / VideoAll.JPG
Vea este vídeo para ver cómo se organiza todo esto

Comunicación Ethernet

La comunicación Ethernet con el Netduino fue la parte más difícil del proyecto.

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;
using Microsoft.SPOT.Net.NetworkInformation;
using System.Threading;

namespace Netduino.Controller
{
    public delegate void MessageEventHandler(string Message);

    class EthernetCommunication
    {
        #region Private Variables
        private string _hostAddress = null;
        private int _port = 80;
        private string _netduinoStaticIPAddress = null;
        private string _subnetMask = null;
        private string _gatewayAddress = null;
        private Thread _listeningThread;
        private Socket _clientSocket = null;
        private static EthernetCommunication _ethernetCommunication;
        #endregion

        #region Constructors
        //This keeps other classes from creating an instance
        private EthernetCommunication()
        {
        }
        #endregion

        #region Public Properties
        public string HostAddress
        {
            set { _hostAddress = value; }
            get { return _hostAddress; }
        }
        public int Port
        {
            set { _port = value; }
            get { return _port; }
        }
        public string NetduinoStaticIPAddress
        {
            set 
            { 
                _netduinoStaticIPAddress = value;
                SetNetduinoStaticIPConfiguration();
            }
            get { return _netduinoStaticIPAddress; }
        }
        public string SubnetMask
        {
            set
            {
                _subnetMask = value;
                SetNetduinoStaticIPConfiguration();
            }
            get { return _subnetMask; }
        }
        public string GatewayAddress
        {
            set 
            {
                _gatewayAddress = value;
                SetNetduinoStaticIPConfiguration();
            }
            get { return _gatewayAddress; }
        }
        #endregion

        #region Events
        public static event MessageEventHandler EventHandlerMessageReceived;
        #endregion

        #region Public Methods

        private void StartListening()
        {
            _listeningThread = new Thread(new ThreadStart(ReceiveSocketsInListeningThread));
            _listeningThread.Start();
        }

        private void InitializeConfiguration()
        {
            if (_netduinoStaticIPAddress == null)
                throw new Exception("The netduino Static IP Address nust be set!");

            if (_subnetMask == null)
                throw new Exception("The Subnet Mask must be set!");

            if (_gatewayAddress == null)
                throw new Exception("The Gateway address must be set.");

            SetNetduinoStaticIPConfiguration();
            NetworkInterface networkInterface = NetworkInterface.GetAllNetworkInterfaces()[0];

            if (_netduinoStaticIPAddress != networkInterface.IPAddress)
                throw new Exception("Problem setting the static IP.");

            if (_subnetMask != networkInterface.SubnetMask)
                throw new Exception("Problem setting the subnet mask.");

            if (_gatewayAddress != networkInterface.GatewayAddress)
                throw new Exception("Problem setting the gateway address.");
        }
        #endregion

        #region Public Static Methods
        public static EthernetCommunication GetInstance()
        {
            if (_ethernetCommunication == null)
            {
                _ethernetCommunication = new EthernetCommunication();
                _ethernetCommunication.HostAddress = Config.HostAddress;
                _ethernetCommunication.Port = Config.Port;
                _ethernetCommunication.NetduinoStaticIPAddress = Config.NetduinoStaticIPAddress;
                _ethernetCommunication.SubnetMask = Config.SubnetMask;
                _ethernetCommunication.GatewayAddress = Config.GatewayAddress;
                _ethernetCommunication.InitializeConfiguration();
                _ethernetCommunication.StartListening();
            }
            return _ethernetCommunication;
        }

        public static void SendMessage(string message)
        {
            GetInstance().SendEthernetMessage(message);
        }
        #endregion

        #region Private Methods
        private bool IsSocketConnected(Socket socket)
        {
            bool connectionNotClosedResetOrTerminated = !socket.Poll(1000, SelectMode.SelectRead);
            bool socketHasDataAvailableToRead = (socket.Available != 0);
            return (connectionNotClosedResetOrTerminated || socketHasDataAvailableToRead);
        }

        private void ReceiveSocketsInListeningThread()
        {
            string receiveMessage = "";
            bool exitProgram = false;

            using (System.Net.Sockets.Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
            {
                socket.Bind(new IPEndPoint(IPAddress.Any, _port));
                socket.Listen(10);

                while (!exitProgram)
                {
                    Debug.Print("Waiting for connection...");
                    _clientSocket = socket.Accept();  //This call is "blocking" and will will wait for a connection, which also means the thread hangs around
                    Debug.Print( "Connection Accepted!");

                    using (_clientSocket)
                    {
                        while (IsSocketConnected(_clientSocket))
                        {
                            int availablebytes = _clientSocket.Available;
                            byte[] buffer = new byte[availablebytes];
                            _clientSocket.Receive(buffer);
                            if (buffer.Length > 0)
                            {
                                receiveMessage = new string(Encoding.UTF8.GetChars(buffer));
                                RaiseMessageReceivedEvent(receiveMessage);
                                if (receiveMessage.ToUpper() == "EXIT")
                                {
                                    exitProgram = true;
                                }
                            }

                        }
                    }
                }
            }
        }

        private void RaiseMessageReceivedEvent(string message)
        {
            // Event will be null if there are no subscribers
            if (EventHandlerMessageReceived != null)
            {
                EventHandlerMessageReceived(message);
            }
        }

        private void SetNetduinoStaticIPConfiguration()
        {
            //Exit if not all of the configuration properties are set
            if (_netduinoStaticIPAddress == null || _subnetMask == null || _gatewayAddress == null)
                return;

            NetworkInterface networkInterface = NetworkInterface.GetAllNetworkInterfaces()[0];

            bool _ipAddressAlreadySet = _netduinoStaticIPAddress == networkInterface.IPAddress;
            bool _subnetMaskAlreadySet = _subnetMask == networkInterface.SubnetMask;
            bool _gatewayAlreadySet = _gatewayAddress == networkInterface.GatewayAddress;

            if (_ipAddressAlreadySet && _subnetMaskAlreadySet && _gatewayAlreadySet)
                return;

            // Set our IP address to a new value
            // This will be saved in the config sector of the netduino and will survive reboots 
            networkInterface.EnableStaticIP(_netduinoStaticIPAddress, _subnetMask, _gatewayAddress);
        }

        private void SendEthernetMessage(string message)
        {
            if (_hostAddress != null && _port > 0)
            {
                using (System.Net.Sockets.Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
                {
                    IPHostEntry entry = Dns.GetHostEntry(_hostAddress);
                    IPAddress address = entry.AddressList[0];
                    IPEndPoint endpoint = new IPEndPoint(address, _port);

                    try
                    {
                        socket.Connect(endpoint);
                        socket.Send(Encoding.UTF8.GetBytes(message));
                        socket.Close();
                        Debug.Print(message);
                    }
                    catch (SocketException se)
                    {
                        Debug.Print("Socket Exception!  Probably no server or bad ip?");
                        Debug.Print(se.StackTrace);
                    }
                }
            }
        }
        #endregion
    }
}

Si desea ver un ejemplo de cómo hablar con una aplicación de escritorio, descargue el código fuente y  estudie  el proyecto Netduino.Desktop.Messenger.

Comunicación Servo

Los servos era divertido para programar. Yo escribí una clase servo que ajuste el ángulo y el Mínimo y Máximo grados. He añadido la propiedad invertida para invertir el ángulo porque  el autor tenia  una versión de interior de la pistola de agua que se monta en el suelo mientras la versión de exterior está montada al revés.

using System;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;

namespace Netduino.Controller
{
    public class Servo : IDisposable
    {
        #region Private Variables
        private PWM _servo;
        private bool _invertAngle = false;
        private int _degreeMin = Config.DegreeMinDefault;
        private int _degreeMax = Config.DegreeMaxDefault;
        private uint _durationMin = Config.DurationMinDefault;
        private uint _durationMax = Config.DurationMaxDefault;
        private uint _angle = Config.HomeDefaultAngle;
        private uint _period = Config.PeriodDefault;
        #endregion

        #region Constructors
        public Servo(Cpu.Pin pin)
        {
            _servo = new PWM(pin);
            _servo.SetDutyCycle(0);
        }
        #endregion

        #region Public Methods
        public void Dispose()
        {
            DisengageServo();
            _servo.Dispose();
        }

        /// <summary> 
        /// Disengage the servo.  
        /// The servo motor will stop trying to maintain an angle 
        ///  
        public void DisengageServo()
        {
            _servo.SetDutyCycle(0);
        }

        public void EngageServo()
        {
            SetPulse();
        }
        #endregion

        #region Private Methods
        private void SetPulse()
        {
            uint angle = _invertAngle ? 180 - _angle: _angle;
            uint duration = (angle) * (_durationMax - _durationMin) / 180 + _durationMin;
            _servo.SetPulse(period: _period, duration: duration);  
        }
        #endregion

        #region Public Properties
        public int Angle
        {
            set
            {
                if (value > _degreeMax)
                    value = _degreeMax;

                if (value < _degreeMin)
                    value = _degreeMin;

                if (value < 0)
                    value = 0;

                _angle = (uint)value;
                SetPulse();
            }
            get
            {
                return (int)_angle;
            }
        }

        public bool InvertAngle
        {
            set {_invertAngle = value;}
            get { return _invertAngle; }
        }

        public int DegreeMin
        {
            set {_degreeMin  = value;}
            get { return _degreeMin; }
        }

        public int DegreeMax
        {
            set { _degreeMax = value; }
            get { return _degreeMax; }
        }

        public uint durationMin
        {
            set { _durationMin = value; }
            get { return _durationMin; }
        }

        public uint durationMax
        {
            set { _durationMax = value; }
            get { return _durationMax; }
        }

        public uint period
        {
            set { _period = value; }
            get { return _period; }
        }
        #endregion
    }
}

Controlar el Jardín

El autor ademas  escribiió  una biblioteca de comandos de tiempo para el NET Micro Framework. Tenga en cuenta que el. NET Micro Framework es muy rico, pero no tiene los genéricos.

using System;
using System.Threading;
using System.Collections;
using Microsoft.SPOT;

namespace Netduino.Controller
{
    public delegate void AlarmCallback();

    class AlarmData
    {
        public int Key { get; set; }
        public ExtendedTimer ExtendedTimer { get; set; }
        public bool RemoveAfterRun { get; set; }
        public AlarmCallback Callback { get; set; }
    }

    class Time
    {
        #region Private Variables
        private static Hashtable _alarmHashtable;
        private static int _key;
        #endregion

        #region Constructors
        //This keeps other classes from creating an instance
        private Time()
        {
        }
        #endregion

        #region Public Static Methods
        public static void SetTime(int year, int month, int day, int hour,int minute, int second, int millisecond )
        {
            DateTime presentTime = new DateTime( year, month, day, hour, minute, second, millisecond);
            Microsoft.SPOT.Hardware.Utility.SetLocalTime(presentTime);
        }

        public static void RunDaily(AlarmCallback alarmCallback, int hour, int minute, int second)
        {
            DateTime alarmTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, hour, minute, second, 0);

            //If we already missed today then tomorrow is the first day to run
            if(alarmTime<DateTime.Now)
            {
                alarmTime = alarmTime.AddDays(1);
            }

            TimeSpan dailyTimeSpan = new TimeSpan(24, 0, 0);
            CreateAlarm(alarmCallback, alarmTime, dailyTimeSpan, false);
        }

        public static void RunOnDelay(AlarmCallback alarmCallback, int runInMilliseconds)
        {
            DateTime alarmTime = DateTime.Now.AddMilliseconds(runInMilliseconds);
            CreateAlarm(alarmCallback, alarmTime, TimeSpan.Zero, true);
        }

        public static void RunRepetitively(AlarmCallback alarmCallback, int repeatMilliseconds)
        {
            DateTime alarmTime = DateTime.Now.AddMilliseconds(repeatMilliseconds);
            TimeSpan repeatTimeSpan = new TimeSpan(0, 0, 0, 0, repeatMilliseconds);
            CreateAlarm(alarmCallback, alarmTime, repeatTimeSpan, false);
        }
        #endregion

        #region Private Methods
        private static void CreateAlarm(AlarmCallback alarmCallback, DateTime alarmTime, TimeSpan timeSpan, bool removeAfterRun)
        {
            if (_alarmHashtable == null)
                _alarmHashtable = new Hashtable();

            _key=_key+1;

            AlarmData alarmData = new AlarmData();
            alarmData.Key = _key;
            alarmData.Callback = alarmCallback;
            alarmData.ExtendedTimer = new ExtendedTimer(OnExecuteAlarm, alarmData, alarmTime, timeSpan);
            alarmData.RemoveAfterRun = removeAfterRun;

            _alarmHashtable.Add(_key, alarmData);
        }

        private static void OnExecuteAlarm(object target)
        {
            AlarmData alarmData = (AlarmData)target;

            if (alarmData.RemoveAfterRun)
                _alarmHashtable.Remove(alarmData.Key);

            alarmData.Callback.Invoke();
        }
        #endregion
    }
}

El Programa Principal. NET Micro Framework

Hay un controlador de eventos para la comunicación Ethernet que se ejecuta cuando se recibe un mensaje.

EthernetCommunication.EventHandlerMessageReceived += new MessageEventHandler(OnMessageReceived);
El método OnMessageReceived analiza el mensaje y llama a los métodos para ejecutar los comandos. El siguiente fragmento de código es sólo parcial, pero la fuente completa está disponible para su descarga aqui (necesita previamente darse de alta en en el site de  code project si no lo estaba con anterioridad)
private static void OnMessageReceived(string message)
        {
            string[] parts = message.Split(' ');
            switch(parts[0].ToUpper()) 
            {
                case "M":
                case "MOVE":
                    if (parts.Length != 3)
                    {
                        EthernetCommunication.SendMessage("The move command takes 3 arguments.");
                        break;
                    }
                    int leftRightAngle = int.Parse(parts[1]);
                    int upDownAngle = int.Parse(parts[2]);
                    _squirtGun.MoveToPosition(leftRightAngle, upDownAngle);
                    break;

                case "U":
                case "UP":
                    int upDelta = parts.Length > 1 ? int.Parse(parts[1]) : 1;
                    _squirtGun.UpDownAngle = _squirtGun.UpDownAngle + upDelta;
                    break;
Fuente aqui
Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s