Un monitor con Netduino


Las ratas se comen cualquier cosa

Hace unos años stewlg  cometío el error de almacenar alimentos en el sótano de una manera que era inferior a salvo de roedores. Un auge de la población pequeña en ratas se produjo antes de que entendíamos lo que estaba sucediendo. Cuando quitamos todo el acceso a la comida en el final del invierno, introdujimos un hambre cuya pánico después nos leemos en los plásticos roídos sobre todo encerrando alimento.

Rat-vs

Todo esto era malo. Caca de rata y orinar son desagradables. Pero lo peor fue que las ratas desesperadas comiendo el tubo de goma y las juntas dentro de nuestra lavadora. Una helada mañana me vino a encontrar jabonosa hemorragia agua fría de la lavadora y que cubre el suelo de nuestro sótano. De repente comprendí por qué alguien querría un monitor de inundación.

Empezó con un acuerdo con pilas de nueve voltios con una alarma audible.

IMG_5217

Esto es mejor que nada, pero no iba a ser capaz de decirle acerca de los problemas cuando estaba fuera de la ciudad, o, para el caso, cuando  stewlg  estaba en el trabajo. Quería más. En concreto,  quería que se conectara a Nagios(un proveedor de Iot) , que comenzó el seguimiento de unas cuantas unidades de disco duro para mí, pero ha crecido a lo largo de los años en algo que una empresa de tamaño medio que podría estar usando para monitorear su red. Nagios se había convertido en la forma en que llevaba la cuenta de los problemas en la casa. Ya paginado y me envió por correo electrónico, y que mantendrá las métricas y datos históricos.

Lo primero que hizo   stewlg   fue buscar un producto comercial que hizo esto – seguramente alguien más necesitado la misma cosa. Y, de hecho, se ha hecho :

SensorMetrix_225Pound

Mientras escribía este post   costaba  alrededor de 425 dólares de Estados Unidos, que todavía parece ridículo, no importa lo industrial y prueba de balas el dispositivo de seguridad es. Eencontró otra empresa que realizaba este también, pero te cobran un precio similar si no mayor para la misma funcionalidad.

¿No podría hacer todo esto con un Arduino ? Y los Internets de cosas?

¿Por qué Netduino

Miró a Arduino, pero una alternativa menos popular  a  stewlg  le  atrajo: Netduino .

* Ya utilizba .NET para el trabajo.  C # es un ambiente verdaderamente excelente.
* Hilos, eventos, temporizadores
Y, probablemente, lo mejor de todo:
* En el circuito de depuración

 stewlg  hizo lo suficiente programación de objetos incrustados para es apreciado tambien  la posibilidad de desplazarse por el código en un depurador o pausando el  programa de modo que  stewlg  estaba dispuesto a pagar un poco más por esto, y de tolerar algunos compromisos en el medio ambiente para evitar ReSharper withdrawl.

También significó mucho para  stewlg   que, si bien la placa de  netduino  cuesta  $ 60 en lugar de ~ $ 30 para un Arduino, pero Netduino +  venía con Ethernet incorporado y no tendría que futz por ahí con un escudo en el primer día. Esto parecía una introducción más suave más amable con el trabajo Arduino-esque.

Portar NRPE a C # y .NET Micro

Como agradable como C # generalmente es que se necesita trabajar en una versión más pequeña de él para recordarle cuánto hay en las bibliotecas, y no en el idioma adecuado. Trabajando en el marco Micro es como trabajar en una cocina cocina en un pequeño velero cuando estás acostumbrado a una cocina de casa decente .Plantillas? Nop. El formato de cadenas? Nop. LINQ? Hah – que es rico.

Afortunadamente, NRPE es un formato muy simple:

  [2 Byte int16_t] - Número de versión
 [2 Byte int16_t] - Tipo (Consulta / Respuesta)
 [4 Byte u_int32_t] - CRC32 Checksum
 [2 int16_t Byte] - código de resultado (OK, WARNING, ERROR DESCONOCIDO)
 [1024 Char Byte] Buffer

Aun así, tubo problemas con el orden de bytes y el relleno por un poco de tiempoantes de averiguarlo. El mayor problema era conseguir un CRC compatible de trabajo, y al final me terminó portar el código a C # . Hubo otro código por ahí que podría haber funcionado, pero casi todo otro código C # hace buen uso viril, sano, de plantillas y LINQ y todo lo que no se puede tener en 192kb de memoria.

¿Cómo funciona

Usted puede tener una mirada en el código mismo, pero los que no tienen una instalación de Nagios puede gustarte una vista previa de cómo funcionan las cosas, una vez que haya instalado las cosas.

He aquí cómo llamar a los diferentes controles manualmente. Aquí nos estamos quedando check_nrpe (el cliente en nuestro servidor Netduino) de Ubuntu:

  # ./check_nrpe -n -H Noah.doodle.local -c check_temp

 OK - Temperatura = 78.1F 25.6C Humedad relativa = 33,6% | temp_celsius = 25.6000004; 35; 38; 0; 100, relative_humidity = 33,6000023%; 70; 80; 0; 100, temp_fahrenheit = 78,080000686645519; 95; 100,40000000000001; 32; 212
  # ./check_nrpe -n -H Noah.doodle.local -c check_flood

 OK - No detectado agua | water_detected = 0
  # ./check_nrpe -n -H Noah.doodle.local -c check_uptime

 OK - Uptime: 03: 42: 51.2740000 memoria libre: 101364 | uptime_in_seconds = 13371, uptime_in_hours = 3, uptime_in_minutes = 222, free_memory = 101364

Estos son los tres servicios que he escrito hasta ahora. Hay tres partes principales visible aquí:

  • OKAY

El código de resultado. En este caso, el servicio se considera estar en un buen estado.

  • Temperatura = 78.1F 25.6C Humedad relativa = 33,6%

Este es el texto de estado legible que aparecerá en Nagios.

  • temp_celsius = 25.6000004; 35; 38; 0; 100, relative_humidity = 33,6000023%; 70; 80; 0; 100, temp_fahrenheit = 78,080000686645519; 95; 100.40000000000001; 32; 212:

Los valores después de la tubería es de datos de rendimiento que es todo lo registra, y puede ser graficada retrospectiva con diversos plug-ins.

Así que aquí está cómo aparece el servicio de Nagios. Aquí al parecer las ratas están de vuelta y han encontrado su camino más allá de las placas de metal que atornilladas a la parte inferior de la lavadora, o tal vez nuestro primer inodoro piso se ha desbordado y se echan por el conducto de calefacción en el sótano de nuevo (no hay ratas la culpa de eso , a menos que mi trasero puede ser considerada como un barco que se hunde) Y algo ha ido mal con el sensor de temperatura -. tal vez los cables han sido comido por las ratas.

NagiosStatusSlice2

Esto es lo que la salida de línea de comandos sería buscar estos dos casos problemáticos:

  ADVERTENCIA - No se puede leer la temperatura.  |
  CRÍTICA - Agua detecta!  | Water_detected = 1

Aquí hay una captura de pantalla de lo que se puede hacer con los datos de rendimiento recogidos a través del tiempo, desde un tiempo sin emergencias o fallas. Podemos ver un tiempo de actividad en constante aumento, y un uso de la memoria constante (no hay fugas aparentemente):

NagiosUptimeInMinutesAndFreeMemoyDemo

Alguna variación en la temperatura y una humedad casi constante:

NagiosTempAndHumidityGraphDemo

Limitaciones

Es importante señalar que la -n aquí es esencial * *:

  ./check_nrpe -n -H noah.doodle.local -c check_uptime

Esto inhabilita SSL para NRPE. Hay al parecer hay espacio en el Netduino para una gran biblioteca tales . Si esto es esencial para que supongo que se podría envolver dentro de un túnel VPN, etc.

Esto es lo que verás si intenta llamar NRPE sin SSL:

  # ./check_nrpe -H Noah.doodle.local -c check_flood
 Check_nrpe: tiempo de espera de socket después de 10 segundos.

También hay algo de cordura comprobar en el código; Si el tipo de consulta no se reconoce TinyNrpeServer no será capaz de responder a la consulta y no intentará.Un mensaje de depuración dará una pista sobre SSL si está conectado a la consola.

Estabilidad

Si se mira a través del código, puede que le resulte bastante paranoico acerca de los errores y accidentes, con dos reinicios duros independientes en el código. Esto se debe a que he probado el código bastante duro y yo estaba esforzándome por evitar que el dispositivo nunca deje de responder y que necesitan un reinicio.

El dispositivo ahora hace un reinicio duro en dos casos:

1) Cuando se hace una excepción

Hay algunas excepciones que descubrí que se producen normalmente, tales como errores de desconexión de socket, que a menudo pueden ser reclamadas a sin recurrir a reiniciar el dispositivo. Por desgracia, no todos ellos parecían ser recuperable, o al menos no de manera consistente, y en lugar de distinguir, me eligieron para reiniciar el dispositivo. En condiciones normales de funcionamiento son bastante raros.

2) Cuando no se haya recibido una consulta para un intervalo de tiempo configurable

En las pruebas, he probado usando condiciones muy abusivos, pero estas condiciones podría conseguir fácilmente el Netduino en un estado donde no sólo mi código no escuchar las conexiones de red entrantes (que podría decirse que tal vez podría ser mi culpa), pila de red del dispositivo se estrellaría, y sería dejar de responder a un ping, que me pareció mucho menos responsables.

En mi código tengo este tiempo de espera configurado así:

  /// <Summary>
 /// Número de milisegundos antes de que la junta se reiniciará.
 /// </ Summary>
 int const público InactivityTimeout = 60 * UpTimeCheck.SecondsPerMinute * UpTimeCheck.MillisecondsPerSecond;

En otras palabras, si no ha tenido un mensaje entrante en una hora, que va a reinicie por sí mismo. Si usted necesita para poner a punto este intervalo, me ponga esto en al menos dos veces el intervalo de control mínimo. Así que si usted comprueba el dispositivo cada 5 minutos, ajusta esto a por lo menos 10 minutos.

Elegí establecer este mucho más alto que yo pueda ver a través de la gráfica el tiempo de actividad, si esto ocurre en realidad nunca – una hora de inactividad debe ser inequívocamente visible.

Para ser justos con la plataforma Netduino, tengo serias dudas de que nadie va a colgar este servidor bajo una carga de red típica. Una encuesta discreta desde un único servidor Nagios cada minuto o dos no va a gravar seriamente nada. Tal vez en un azul-moon, una excepción a la red se producirá y el dispositivo se reinicie en silencio – si lo hace, usted debe ver que se refleja en los datos de rendimiento de tiempo de actividad, pero aún así disfrutar disponibilidad esencialmente ininterrumpida. No espero a nadie a hacer realidad el dispositivo no responde, y activar el reinicio de la vigilancia. Pero por favor dígame cómo va para usted.

Si tuviera que tomar esto más lejos a  stewlg   le gustaría que un temporizador de vigilancia adecuado en hardware .

Expansión

 stewlg   realmente no necesita nada más que un monitor de inundación, pero  pensó en tirar en al menos otra métrica para obtener el código listo para manejar múltiples cheques, por lo tanto, la comprobación de la temperatura.

Aquí es lo menos que había necesidad de hacer para poner en práctica un cheque:

  /// <Summary>
  /// Un ejemplo de lo mínimo que hay que hacer para poner en práctica un cheque
  /// </ Summary>
  DemoCheck clase pública: NrpeCheck
  {
      public override NrpeMessage.NrpeResultState GetStatus (fuera cadena statusString, fuera Hashtable performanceData)
      {
         performanceData = new Hashtable (); var demoMetric = 20;
         performanceData.Add ("demo_metric", demoMetric);
         statusString = "Demo métrica:" + demoMetric.ToString ();
         // Siempre Ok.
         volver NrpeMessage.NrpeResultState.Ok;
      }
  }

Usted probablemente querrá algo de código condicional para el ResultState, y cualquier métrica que tiene probablemente variar. Pero la adición de ningún tipo de supervisión debe ser fácil, al menos desde el punto de vista TinyNrpeServer.

Si usted sube un sensor que desea añadir, envíeme un correo electrónico con un parche o una solicitud de extracción. Me encantaría este servidor NRPE tienen más supervisión que ofrecer fuera de la caja.

Costo

Así que, si no es de $ 425, ¿qué dedico? Probablemente menos de $ 120.Hubiera sido mucho más barato si supiera  lo que estaba haciendo – esto erael primer proyecto de tipo Arduino y los conceptos básicos eran un misterio para  stewlg  . Terminó probando un montón de cosas que no funcionaron antes de encontrar cosas que funcionaron bien pasablemente.

Circuito

Noé V2

Instalación

Prueba inicial de monitor de inundación

Protoshield cableada

Sensor de Inundación comprobación final

Sensor sujeta con cintas para el pelo

Entre las máquinas

Brilla muy prominente en la penumbra

Primeros DHT-22 cableado

DHT-22 en su lugar

Instalado en la caja de ProyectoIMG_5271

Bibliografía y Apreciación

Para ayuda inestimable, indirectos con el protocolo NRPE núcleo, gracias sobre todo a Andreas Marschke y Sadris . Para la clase DhtSensor, Stanislav «CW» Simicek y todo el mundo en este hilo . Para  cluing en aproximadamente perros guardianes y reinicios duras, las personas en estas discusiones . Chris Walker para la clase Cronómetro (y todo lo demás en Netduino, por supuesto).

El Código

https://github.com/StewLG/NetduinoNrpe

 

 

 

Fuente aqui

Proyecto alarma casera con Netduino


En este original   proyecto  se trata de proteger una habitación de la bomba de sumidero  potencialmente peligrosa para los niños  asegurándose  de que las estancias quedan las  puertas cerradas y las personas (principalmente niños) no entran  en esta.

Las soluciones «alarma de la puerta» disponibles en el mercado parecen dirigidas a congeladores y simplemente no se adaptan a  necesidades mas complejas del autor   que pensaba mas bien  en una solución  con  Netduino.

La idea  original por tanto era algo que hiciera sonar  una alarma corta cuando se abriera la puerta y luego alertara a intervalos (cada pocos minutos), similar a un detector de humo con una batería que funcionase hasta agotar la batería o  hasta que la puerta se cerrase

esquema_alarma

La unidad controla la puerta a través de un interruptor magnético. Cuando se abre la puerta varias cosas suceden:

  1.  La luz del techo se enciende con un relé que controla una carga de baja  tensión.
  2.  Un servicio web se llama el cual envía un MMS a un teléfono.
  3. Un contador de tiempo que  se activa qeu al concluir  producirá una alerta sonora y hara una  llamada webservice (MMS) si la puerta permanece abierta durante un periodo de tiempo determinado.

Cuando se cierra la puerta a la luz se apaga y el temporizador de la alarma se detiene. El proyecto también incorpora una pantalla LCD de 2 líneas para mensajes, 2 LEDs (1 para la energía, 1 para la indicación de modo de problemas / override) y algunos botones para interactuar con el sistema.

Las funcionalidades de esta alarma de puerta basada en Netduino pues son las siguientes :

  •  Apaga/enciende  la luz : la enciende automáticamente cuando se abre la puerta y la apaga cuando la puerta está cerrada
  •  Alarma del MMS al abrir la puerta (a través de llamadas de servicio web)
  •  Alerta audible y alerta MMS adicional si la puerta se deja abierta más tiempo que  el  valor del temporizador
  •  Alerta que se repiten a intervalos especificados si la puerta sigue dejándose abierta
  • 4 botones :
    •  Botón 1: Manual de luz de encendido / apagado de anulación
    • – Botón 2: Regreso al control de forma automática la luz
    • – Botón 3: mensajes de error Revisión (principalmente para conexiones de red)
    • – Botón 4: Encender / apagar el sensor de la puerta (permite prolongado puerta abierta sin alerta)

Respecto al software para Netduino  se utilizan  cuatro clases :

Una clase para  dar soporte  a la comunicación de red:

public static class Network
{
public static bool NetworkAvail = false;
public static NetworkInterface ni = NetworkInterface.GetAllNetworkInterfaces()[0];

static NoteClass[] networkUpTune = {
new NoteClass(5,NoteClass.C, NoteClass.ThirtySecond),
new NoteClass(5,NoteClass.Rest, NoteClass.ThirtySecond),
new NoteClass(5,NoteClass.C, NoteClass.ThirtySecond),
new NoteClass(5,NoteClass.Rest, NoteClass.ThirtySecond),
new NoteClass(5,NoteClass.C, NoteClass.ThirtySecond)
};

static NoteClass[] networkDownTune = {
new NoteClass(3,NoteClass.C, NoteClass.ThirtySecond),
new NoteClass(3,NoteClass.Rest, NoteClass.ThirtySecond),
new NoteClass(3,NoteClass.C, NoteClass.ThirtySecond),
new NoteClass(3,NoteClass.Rest, NoteClass.ThirtySecond),
new NoteClass(3,NoteClass.C, NoteClass.ThirtySecond)
};

static NoteClass[] alertTune = {
new NoteClass(5,NoteClass.C, NoteClass.Eighth),
new NoteClass(6,NoteClass.C, NoteClass.Eighth),
new NoteClass(5,NoteClass.C, NoteClass.Eighth),
new NoteClass(6,NoteClass.C, NoteClass.Eighth)
};

public static void InitNetwork()
{
ParallaxLCD.Print(«Network…»);
int count = 0;
//DHCP can take a few seconds to finish so wait (up to 10 seconds) for it to complete
while (true)
{
count++;
ni = NetworkInterface.GetAllNetworkInterfaces()[0];
//only try for 10 seconds
if (ni.IPAddress != «0.0.0.0» || count > 10) break;
Thread.Sleep(1000);
}
if (ni.IPAddress == «0.0.0.0»)
{
ParallaxLCD.Print(«Network…\rError: Failed»);
}
else
{
ParallaxLCD.Print(«Network… IP:\r» + ni.IPAddress);
NetworkAvail = true;
}
//wire in event handler to keep track of the current network up/down state
NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged);
}

static void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
NetworkAvail = e.IsAvailable;
if (Program.initializing)
return;
if (NetworkAvail)
{
ParallaxLCD.Play(networkUpTune);
}
else
{
ParallaxLCD.Play(networkDownTune);
}
}

public static void SendAlert()
{
if (NetworkAvail)
{
try
{
//HttpWebRequest
HttpWebRequest alertRequest = (HttpWebRequest)WebRequest.Create(Program.AlertWebService);
alertRequest.Method = «GET»;
HttpWebResponse alertResponse = (HttpWebResponse)alertRequest.GetResponse();
if (alertResponse.StatusCode != HttpStatusCode.OK)
{
Program.LastError = «(SendAlert) » + alertResponse.StatusCode.ToString() + «: » + alertResponse.StatusDescription;
ParallaxLCD.Play(alertTune);
}
}
catch (Exception e)
{
Program.LastError = «(SendAlert) » + e.Message;
ParallaxLCD.Play(alertTune);
}
}
else
{
Program.LastError = «(SendAlert) Network unavailable»;
ParallaxLCD.Play(alertTune);
}
}

}
}

La clase  para controlar el display LCD

//Non-visible characters (Decimal ASCII) used by the Parallax LCD as commands
public enum LCD_COMMAND_CHARS
{
PrintCustomChar0 = 0,
PrintCustomChar1 = 1,
PrintCustomChar2 = 2,
PrintCustomChar3 = 3,
PrintCustomChar4 = 4,
PrintCustomChar5 = 5,
PrintCustomChar6 = 6,
PrintCustomChar7 = 7,
Backspace = 8,
Right = 9,
LineFeed = 10,
FormFeed = 12, //clears display and sets cursor to row 0 column 0
CR = 13,
BacklightOn = 17,
BacklightOff = 18,
DisplayOff = 21,
DisplayOn_NoCursor_NoBlink = 22,
DisplayOn_NoCursor_CharBlink = 23,
DisplayOn_Cursor_NoBlink = 24,
DisplayOn_Cursor_CharBlink = 25,
//Sound control
NoteLength_1_64 = 208,
NoteLength_1_32 = 209,
NoteLength_1_16 = 210,
NoteLength_1_8 = 211,
NoteLength_1_4 = 212,
NoteLength_1_2 = 213,
NoteLength_Whole = 214,
NoteScale_3 = 215,
NoteScale_4 = 216,
NoteScale_5 = 217,
NoteScale_6 = 218,
NoteScale_7 = 219,
Note_A = 220,
Note_A_Sharp = 221,
Note_B = 222,
Note_C = 223,
Note_C_Sharp = 224,
Note_D = 225,
Note_D_Sharp = 226,
Note_E = 227,
Note_F = 228,
Note_F_Sharp = 229,
Note_G = 230,
Note_G_Sharp = 231,
Note_Rest = 232,
LoadCustomChar0 = 248,
LoadCustomChar1 = 249,
LoadCustomChar2 = 250,
LoadCustomChar3 = 251,
LoadCustomChar4 = 252,
LoadCustomChar5 = 253,
LoadCustomChar6 = 254,
LoadCustomChar7 = 255
}

//This class assumes the Parallax LCD/speaker combo unit is connected to Netduino Plus Digital I/O PIN 3
//which is reserved for COM2 in the Netduino framework. Also assumes the Parallax LCD has been set to
//9600 baud using it’s onboard dip switches.
public static class ParallaxLCD
{
private static SerialPort LCD_PORT;
private static string COM_PORT = «COM2»; //Netduino Plus Digital I/O PIN 3
private static int COM_BAUD = 9600;

private static bool _LCD_state = false;

public static bool LCD
{
get
{
return _LCD_state;
}
set
{
if (value == _LCD_state)
{
//LCD already in specified state, don’t do anything
return;
}
if (value)
{
LCDOn();
BacklightOn();
}
else
{
BacklightOff();
LCDOff();
}
_LCD_state = value;
}
}
public static void Print(string inputstring)
{
//if the first line is >= 16 characters then remove the line break for a more natural behavior
int cr = inputstring.IndexOf(‘\r’, 0);
if (cr >= 16)
{
inputstring = inputstring.Substring(0, cr) + inputstring.Substring(cr + 1);
}
//this routine assumes all output will be «full screen», meaning no other data is left on the screen
ClearLCD();
byte[] bytes = UTF8Encoding.UTF8.GetBytes(inputstring);
LCD_PORT.Write(bytes, 0, bytes.Length);
}

public static void StartSerial()
{
LCD_PORT = new SerialPort(COM_PORT, COM_BAUD, Parity.None, 8, StopBits.One);
LCD_PORT.Open();
}

public static void BacklightOn()
{
LCD_PORT.Write(new byte[] { (int)LCD_COMMAND_CHARS.BacklightOn }, 0, 1);
}

public static void BacklightOff()
{
LCD_PORT.Write(new byte[] { (int)LCD_COMMAND_CHARS.BacklightOff }, 0, 1);
}

public static void ClearLCD()
{
LCD_PORT.Write(new byte[] { (int)LCD_COMMAND_CHARS.FormFeed }, 0, 1);
}

public static void LCDOff()
{
LCD_PORT.Write(new byte[] { (int)LCD_COMMAND_CHARS.DisplayOff }, 0, 1);
}

public static void LCDOn()
{
LCD_PORT.Write(new byte[] { (int)LCD_COMMAND_CHARS.DisplayOn_NoCursor_NoBlink }, 0, 1);
}

public static void LCDCR()
{
LCD_PORT.Write(new byte[] { (int)LCD_COMMAND_CHARS.CR }, 0, 1);
}

//Short, high-pitched sound to ack as notification that an action was detected (like a button press)
public static void Acknowledge()
{
Play(new NoteClass(7, NoteClass.A, NoteClass.ThirtySecond));
}

//Sound a traditional computer beep
public static void Beep()
{
Play(new NoteClass(5, NoteClass.A, NoteClass.Quarter));
}

public static void Play(NoteClass note)
{
byte[] sequence = new byte[3];
sequence[0] = (byte)note.ScaleChar;
sequence[1] = (byte)note.LengthChar;
sequence[2] = (byte)note.NoteChar;
LCD_PORT.Write(sequence, 0, 3);
}
public static void Play(NoteClass[] notes)
{
int currentScale = 0;
int currentLength = 0;
int count = 0;
if (notes != null && notes.Length > 0)
{
byte[] sequence = new byte[notes.Length * 3];
foreach (NoteClass note in notes)
{
if (note.ScaleChar != currentScale)
{
currentScale = note.ScaleChar;
sequence[count++] = (byte)currentScale;
}
if (note.LengthChar != currentLength)
{
currentLength = note.LengthChar;
sequence[count++] = (byte)currentLength;
}
sequence[count++] = (byte)note.NoteChar;
}
LCD_PORT.Write(sequence, 0, count);
}
}

public static byte[] CharsToBytes(char[] Input)
{
byte[] ReturnValue = new byte[Input.Length];

for (int Counter = 0; Counter < Input.Length; ++Counter)
{
ReturnValue[Counter] = (byte)Input[Counter];
}
return ReturnValue;
}

}
public class NoteClass
{
//Helper constants
public const int A = 0;
public const int ASharp = 1;
public const int B = 2;
public const int C = 3;
public const int CSharp = 4;
public const int D = 5;
public const int DSharp = 6;
public const int E = 7;
public const int F = 8;
public const int FSharp = 9;
public const int G = 10;
public const int GSharp = 11;
public const int Rest = 12;

public const int SixtyQuarter = (int)LCD_COMMAND_CHARS.NoteLength_1_64;
public const int ThirtySecond = (int)LCD_COMMAND_CHARS.NoteLength_1_32;
public const int Sixteenth = (int)LCD_COMMAND_CHARS.NoteLength_1_16;
public const int Eighth = (int)LCD_COMMAND_CHARS.NoteLength_1_8;
public const int Quarter = (int)LCD_COMMAND_CHARS.NoteLength_1_4;
public const int Half = (int)LCD_COMMAND_CHARS.NoteLength_1_2;
public const int Whole = (int)LCD_COMMAND_CHARS.NoteLength_Whole;

private int _scale = 5;
private int _note = A;
private int _length = Quarter;

public int Scale
{
get
{
return _scale;
}
set
{
if (value >= 3 && value <= 7)
{
_scale = value;
}
}
}
public int ScaleChar
{
get
{
return ((int)LCD_COMMAND_CHARS.NoteScale_3 – 3) + _scale;
}
}
public int Note
{
get
{
return _note;
}
set
{
if (value >= A && value <= Rest)
{
_note = value;
}
}
}
public int NoteChar
{
get
{
return (int)LCD_COMMAND_CHARS.Note_A + _note;
}
}
public int Length
{
get
{
return _length;
}
set
{
if (value >= SixtyQuarter && value <= Whole)
{
_length = value;
}
}
}
public int LengthChar
{
get
{
return _length;
}
}

public NoteClass(int scale, int note, int length)
{
Scale = scale;
Note = note;
Length = length;
}
}

Una clase para controlar los pulsadores

public static class Utility
{
public static string PadLeft(string val, int maxlen, string padchar)
{
string newval = val;
while (newval.Length < maxlen)
{
newval = padchar + newval;
}
return newval;
}
public static string PadRight(string val, int maxlen, string padchar)
{
string newval = val;
while (newval.Length < maxlen)
{
newval += padchar;
}
return newval;
}
}

Y por último la clase principal:

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.NetduinoPlus;

namespace DoorManager
{
public enum LightModes { Auto, ManualOn, ManualOff }

public class Program
{
#region User Settings & Constants
public static Cpu.Pin DoorSensorPin = Pins.GPIO_PIN_D11; //Magnetic switch to monitor door open/close state
public static Cpu.Pin LEDPin = Pins.GPIO_PIN_D13; //Status indicator (blinks if error or override on)
public static Cpu.Pin LightPin = Pins.GPIO_PIN_D12; //Connection to high voltage relay to control room light
public static Cpu.Pin Button1Pin = Pins.GPIO_PIN_D7; //Manually turns light on/off
public static Cpu.Pin Button2Pin = Pins.GPIO_PIN_D6; //Returns light control to automatic
public static Cpu.Pin Button3Pin = Pins.GPIO_PIN_D5; //Display error messages
public static Cpu.Pin Button4Pin = Pins.GPIO_PIN_D4; //Toggles door sensor (when off, no alerts are created)
public const int InitialDoorOpenAlertDelay = 30; //in minutes
public const int OngoingDoorOpenSoundAlertDelay = 1; //in minutes
public const int OngoingDoorOpenMessageAlertDelay = 30; //in minutes
public const int LCDOffDelay = 10; //in seconds
public const string AlertWebService = «http://192.168.1.2/WS/SendAlert.aspx?a=Door+Manager&e=Door+Opened»;
#endregion

#region Fields
public static string LastError = «»;
public static InterruptPort DoorSensor;
public static OutputPort LED;
public static OutputPort Light;
public static InterruptPort Button1;
public static InterruptPort Button2;
public static InterruptPort Button3;
public static InterruptPort Button4;
private static bool IsDoorSensorProcessing = false;
private static bool IsDoorSensorIgnored = false;
public static bool IsDoorOpen = false;
private static bool _LightState = false;
public static bool initializing = true;
public static bool IgnoreButtons = true;
public static bool IgnoreAlerts = false;
public static bool AlertScheduled = false;
public static Timer DoorTimer = new Timer(new TimerCallback(DoorTimerHandler), null, Timeout.Infinite, Timeout.Infinite);
public static Timer LCDTimer = new Timer(new TimerCallback(LCDTimerHandler), null, Timeout.Infinite, Timeout.Infinite);
public static Timer LEDTimer = new Timer(new TimerCallback(LEDTimerHandler), null, Timeout.Infinite, Timeout.Infinite);
public static Timer ButtonTimer = new Timer(new TimerCallback(ButtonTimerHandler), null, Timeout.Infinite, Timeout.Infinite);
public static Timer SendAlertTimer = new Timer(new TimerCallback(SendAlertTimerHandler), null, Timeout.Infinite, Timeout.Infinite);
public static Timer DoorCloseTimer = new Timer(new TimerCallback(DoorCloseTimerHandler), null, Timeout.Infinite, Timeout.Infinite);
public static int CurrentMessageAlertDelayTimeout = 0;
public static int CurrentSoundAlertDelayTimeout = 0;
public static int CurrentAlertDelayTime = 0;
public static int CurrentLCDDelayTime = 0;
public static LightModes LightMode = LightModes.Auto;
private static int _LEDBlinkCount = 0;
public static bool _LEDBlink = false;

static NoteClass[] initTune = {
new NoteClass(5,NoteClass.C, NoteClass.Sixteenth),
new NoteClass(5,NoteClass.D, NoteClass.Sixteenth),
new NoteClass(5,NoteClass.E, NoteClass.Sixteenth),
new NoteClass(5,NoteClass.F, NoteClass.Sixteenth),
new NoteClass(5,NoteClass.G, NoteClass.Sixteenth),
new NoteClass(6,NoteClass.A, NoteClass.Sixteenth),
new NoteClass(6,NoteClass.B, NoteClass.Sixteenth),
new NoteClass(6,NoteClass.C, NoteClass.Sixteenth)
};
#endregion

#region Properties
public static bool LEDBlink
{
set
{
if (value)
_LEDBlinkCount++;
else
_LEDBlinkCount–;
if (_LEDBlinkCount > 0 && !_LEDBlink)
{
_LEDBlink = true;
LEDTimer.Change(250, Timeout.Infinite); //start timer
}
else if (_LEDBlinkCount == 0 && _LEDBlink)
{
LEDTimer.Change(Timeout.Infinite, Timeout.Infinite); //cancel timer
_LEDBlink = false;
LED.Write(false);
}
}
}
#endregion

#region Program Start
public static void Main()
{
Initialize();
ShowDoorSensorState();

Thread.Sleep(Timeout.Infinite); //keep running forever
}
#endregion

#region Methods
static void IgnoreButton()
{
IgnoreButtons = true;
ButtonTimer.Change(500, Timeout.Infinite); //restart timer
}

public static bool SetLightState()
{
bool expectedState = IsDoorOpen;
switch (LightMode)
{
case LightModes.ManualOn:
expectedState = true;
break;
case LightModes.ManualOff:
expectedState = false;
break;
}
if (expectedState != _LightState)
{
_LightState = expectedState;
Light.Write(expectedState);
}
return _LightState;
}

public static void SetIgnoreDoorSensor(bool ignore)
{
if (ignore != IsDoorSensorIgnored)
{
IsDoorSensorIgnored = ignore;
if (ignore)
{
LEDBlink = true;
//stop door sensor
DoorTimer.Change(Timeout.Infinite, Timeout.Infinite);
ParallaxLCD.LCD = true;
ParallaxLCD.Print(«Door Sensor\rDisabled»);
SetLCDOffTimer();
}
else
{
LEDBlink = false;
ParallaxLCD.LCD = true;
ParallaxLCD.Print(«Door Sensor\rEnabled»);
Thread.Sleep(2000);
//Check door
ShowDoorSensorState();
}
}
}

static void ShowDoorSensorState()
{
if (IsDoorSensorIgnored)
return;
ParallaxLCD.LCD = true; //ensure LCD is on
ParallaxLCD.Print(«Door » + (IsDoorOpen ? «Open» : «Closed»));
if (IsDoorOpen)
{
//start door timer
CurrentSoundAlertDelayTimeout = InitialDoorOpenAlertDelay * 60 * 1000; //convert minutes to milliseconds
CurrentAlertDelayTime = 0;
DoorTimer.Change(0, Timeout.Infinite);
}
else
{
//clear door timer
DoorTimer.Change(Timeout.Infinite, Timeout.Infinite);
//set LCD timer
SetLCDOffTimer();
}
SetLightState();
}

static void SetLCDOffTimer()
{
CurrentLCDDelayTime = LCDOffDelay * 1000;
LCDTimer.Change(1000, Timeout.Infinite);
}

static void Initialize()
{
ParallaxLCD.StartSerial();
ParallaxLCD.LCD = true;
ParallaxLCD.ClearLCD();
Thread.Sleep(1000);
ParallaxLCD.Print(«Initializing…»);
ParallaxLCD.Beep();
Thread.Sleep(1000);

//set LED
LED = new OutputPort(LEDPin, false);
LEDBlink = true;

//setup door sensor
DoorSensor = new InterruptPort(DoorSensorPin, true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeBoth);
DoorSensor.OnInterrupt += new NativeEventHandler(DoorSensor_OnInterrupt);
IsDoorOpen = DoorSensor.Read();

//setup light control
Light = new OutputPort(LightPin, false);
Light.Write(_LightState);

//setup buttons
Button1 = new InterruptPort(Button1Pin, true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeLow);
Button1.OnInterrupt += new NativeEventHandler(Button1_OnInterrupt);
Button2 = new InterruptPort(Button2Pin, true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeLow);
Button2.OnInterrupt += new NativeEventHandler(Button2_OnInterrupt);
Button3 = new InterruptPort(Button3Pin, true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeLow);
Button3.OnInterrupt += new NativeEventHandler(Button3_OnInterrupt);
Button4 = new InterruptPort(Button4Pin, true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeLow);
Button4.OnInterrupt += new NativeEventHandler(Button4_OnInterrupt);

//setup network
Network.InitNetwork();

Thread.Sleep(5000);

ParallaxLCD.Print(«Ready»);
ParallaxLCD.Play(initTune);

Thread.Sleep(2000);

LEDBlink = false;

initializing = false;
IgnoreButtons = false;
}
#endregion

#region Timer Handlers
static void DoorCloseTimerHandler(object sender)
{
//this timer turns off sending alerts for a period of time after the door has been closed; this is another «bounce» preventative measure
DoorCloseTimer.Change(Timeout.Infinite, Timeout.Infinite); //cancel timer
IgnoreAlerts = false;
}

static void SendAlertTimerHandler(object sender)
{
//performing this in a timer ensures switch «bounce» doesn’t send multiple alerts
SendAlertTimer.Change(Timeout.Infinite, Timeout.Infinite); //cancel timer
Network.SendAlert();
AlertScheduled = false;
}

static void ButtonTimerHandler(object sender)
{
//this routine is to delay reaction to button presses to eliminate «bounce» and false presses
ButtonTimer.Change(Timeout.Infinite, Timeout.Infinite); //cancel timer
IgnoreButtons = false;
}

static void LEDTimerHandler(object sender)
{
if (_LEDBlink)
{
LED.Write(!LED.Read());
LEDTimer.Change(250, Timeout.Infinite); //restart timer
}
else
{
LED.Write(false);
}
}

static void LCDTimerHandler(object sender)
{
if (!IsDoorOpen || IsDoorSensorIgnored)
{
CurrentLCDDelayTime -= 1000;
if (CurrentLCDDelayTime <= 0)
{
CurrentLCDDelayTime = 0;
ParallaxLCD.LCD = false; //turn off LCD
//clear LCD timer
LCDTimer.Change(Timeout.Infinite, Timeout.Infinite);
}
else
{
LCDTimer.Change(1000, Timeout.Infinite);
}
}
else
{
//clear LCD timer
LCDTimer.Change(Timeout.Infinite, Timeout.Infinite);
}
}

static void DoorTimerHandler(object sender)
{
if (IsDoorOpen && !IsDoorSensorIgnored)
{
if (CurrentSoundAlertDelayTimeout > 0)
{
CurrentSoundAlertDelayTimeout -= CurrentAlertDelayTime;
}
if (CurrentMessageAlertDelayTimeout > 0)
{
CurrentMessageAlertDelayTimeout -= CurrentAlertDelayTime;
}
if (CurrentMessageAlertDelayTimeout <= 0)
{
//alert
SendAlertTimer.Change(5000, Timeout.Infinite); //send alert after 5 seconds
//restart delay
CurrentMessageAlertDelayTimeout = OngoingDoorOpenMessageAlertDelay * 60 * 1000; //convert minutes to milliseconds
}
if (CurrentSoundAlertDelayTimeout > 0)
{
int mins = CurrentSoundAlertDelayTimeout / 60000;
int secs = (CurrentSoundAlertDelayTimeout – (mins * 60000)) / 1000;
ParallaxLCD.Print(«Door Open\rAlert in » + (mins > 1 ? mins.ToString() + » min» : secs.ToString() + » sec»));
if (mins > 1)
{
CurrentAlertDelayTime = 60000;
}
else
{
CurrentAlertDelayTime = 1000;
}
}
else
{
//alert
ParallaxLCD.Play(new NoteClass(7, NoteClass.A, NoteClass.Half));
//restart delay
CurrentSoundAlertDelayTimeout = OngoingDoorOpenSoundAlertDelay * 60 * 1000; //convert minutes to milliseconds
CurrentAlertDelayTime = 0;
}
DoorTimer.Change(CurrentAlertDelayTime, Timeout.Infinite);
}
}
#endregion

#region Event Handlers
static void DoorSensor_OnInterrupt(uint data1, uint data2, DateTime time)
{
if (initializing || IsDoorSensorProcessing)
return;
IsDoorSensorProcessing = true;
//magnetic switch wired to normally open / onboard switch normally high
//therefore when the door is closed (magnetic switch open) the onboard switch (normally high) is on
IsDoorOpen = (data2 == 1);
if (IsDoorOpen && !IgnoreAlerts)
{
AlertScheduled = true;
SendAlertTimer.Change(5000, Timeout.Infinite); //send alert after 5 seconds
}
else if (!IsDoorOpen && !AlertScheduled)
{
IgnoreAlerts = true;
DoorCloseTimer.Change(5000, Timeout.Infinite); //prevent alerts for 5 seconds
}
ShowDoorSensorState();
IsDoorSensorProcessing = false;
}

/// <summary>
/// Button 1 turns the light on/off manually.
/// </summary>
static void Button1_OnInterrupt(uint data1, uint data2, DateTime time)
{
if (initializing)
return;
if (!IgnoreButtons)
{
IgnoreButtons = true;
ParallaxLCD.Acknowledge();
LightModes oldMode = LightMode;
if (_LightState)
LightMode = LightModes.ManualOff;
else
LightMode = LightModes.ManualOn;
SetLightState();
//if we’re moving off auto mode start the led blinking for awareness
if (oldMode == LightModes.Auto)
LEDBlink = true;
IgnoreButton();
}
}

/// <summary>
/// Button 2 sets the light back to automatic (door sensor controlled).
/// </summary>
static void Button2_OnInterrupt(uint data1, uint data2, DateTime time)
{
if (initializing)
return;
if (!IgnoreButtons)
{
IgnoreButtons = true;
if (LightMode != LightModes.Auto)
{
ParallaxLCD.Acknowledge();
LightMode = LightModes.Auto;
SetLightState();
LEDBlink = false;
}
IgnoreButton();
}
}

static void Button3_OnInterrupt(uint data1, uint data2, DateTime time)
{
if (initializing)
return;
if (!IgnoreButtons && (!IsDoorOpen || IsDoorSensorIgnored))
{
IgnoreButtons = true;
ParallaxLCD.Acknowledge();
if (LastError != null && LastError.Length > 0)
{
string msg = LastError;
//show error in byte size pieces
ParallaxLCD.LCD = true;
while (msg.Length > 0)
{
if (msg.Length > 32)
{
ParallaxLCD.Print(msg.Substring(0,32));
msg = msg.Substring(32);
}
else
{
ParallaxLCD.Print(msg);
msg = «»;
}
Thread.Sleep(5000);
}
ParallaxLCD.Acknowledge();
SetLCDOffTimer();
}
else
{
ParallaxLCD.LCD = true;
ParallaxLCD.Print(«No Errors»);
SetLCDOffTimer();
}
IgnoreButton();
}
}

/// <summary>
/// Button 4 disables/re-enables the door sensor.
/// </summary>
static void Button4_OnInterrupt(uint data1, uint data2, DateTime time)
{
if (initializing)
return;
if (!IgnoreButtons)
{
IgnoreButtons = true;
ParallaxLCD.Acknowledge();
SetIgnoreDoorSensor(!IsDoorSensorIgnored);
IgnoreButton();
}
}
#endregion
}
}

Lista de componentes :

  • 2 LED, 4 interruptores momentáneos, tornillos diverso, caja de proyecto
  • Opcional: Kit Protoshield para Ardiono – $ 9.90  http://www.seeedstud…uino-p-318.html
  • Este kit incluye una placa de circuito de prototipos, conectores, resistencias, los LED (no caja montable en proyecto), conmutadores (no caja montable proyecto) y otras piezas misceláneas que puede ayudar a un iniciado a empezar.

Fuente  aqui