Created
January 2, 2012 07:03
-
-
Save lancefisher/1549667 to your computer and use it in GitHub Desktop.
Netduino Webserver that sets the nixie tubes for Twilio Rube Goldberg Contest
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Threading; | |
using Microsoft.SPOT.Hardware; | |
using SecretLabs.NETMF.Hardware.Netduino; | |
namespace NetduinoArduinix | |
{ | |
public class ArduinixShield | |
{ | |
/// <summary> | |
/// There are two SN74141 chips on the Arduinix. These Netduino Output ports are connected | |
/// to the inputs of the first SN74141. | |
/// </summary> | |
/// <remarks> | |
/// SN74141 Truth Table | |
/// D C B A # | |
/// L,L,L,L 0 | |
/// L,L,L,H 1 | |
/// L,L,H,L 2 | |
/// L,L,H,H 3 | |
/// L,H,L,L 4 | |
/// L,H,L,H 5 | |
/// L,H,H,L 6 | |
/// L,H,H,H 7 | |
/// H,L,L,L 8 | |
/// H,L,L,H 9 | |
/// </remarks> | |
public static OutputPort[] SN74141AInputs = | |
{ | |
new OutputPort(Pins.GPIO_PIN_D2, false), //A | |
new OutputPort(Pins.GPIO_PIN_D3, false), //B | |
new OutputPort(Pins.GPIO_PIN_D4, false), //C | |
new OutputPort(Pins.GPIO_PIN_D5, false) //D | |
}; | |
/// <summary> | |
/// There are two SN74141 chips on the Arduinix. These Netduino Output ports are connected | |
/// to the inputs of the second SN74141. | |
/// </summary> | |
public static OutputPort[] SN74141BInputs = | |
{ | |
new OutputPort(Pins.GPIO_PIN_D6, false), //A | |
new OutputPort(Pins.GPIO_PIN_D7, false), //B | |
new OutputPort(Pins.GPIO_PIN_D8, false), //C | |
new OutputPort(Pins.GPIO_PIN_D9, false) //D | |
}; | |
/// <summary> | |
/// Writing to these Netduino OutputPorts fires the high voltage anode on the Arduinix Shield | |
/// </summary> | |
public static OutputPort[] Anodes = | |
{ | |
new OutputPort(Pins.GPIO_PIN_D10, false), | |
new OutputPort(Pins.GPIO_PIN_D11, false), | |
new OutputPort(Pins.GPIO_PIN_D12, false), | |
//todo: support the 4th anode for larger displays | |
}; | |
/// <summary> | |
/// Displays the numbers on the nixie tubes. Designed for 6 tube display. | |
/// Each integer should be < 16. 0-9 will show on the display. 11-15 will blank that digit. | |
/// Integers greater than 15 start over at 0. e.g. 16 displays 0, 17 displays 1, etc. | |
/// </summary> | |
public void DisplayNumbers(int number1, int number2, int number3, int number4, int number5, int number6) | |
{ | |
//I'm not sure if my anodes are wired to my display in the standard way. -Lance | |
DisplaySet(Anodes[1], number1, number4); | |
DisplaySet(Anodes[0], number5, number2); | |
DisplaySet(Anodes[2], number3, number6); | |
} | |
/// <summary> | |
/// Displays the number on the arduinix display. | |
/// </summary> | |
/// <param name="number"> | |
/// The number to display. For a six-digit display, | |
/// the number should be less than 999,999. | |
/// </param> | |
/// <param name="displayMode"> | |
/// The number can be padded with zeros to the left, | |
/// or aligned to the right or left with leading zeros blanked. | |
/// </param> | |
public void DisplayNumber(int number, DisplayMode displayMode = DisplayMode.ZeroPadded) | |
{ | |
//Get each digit of the number, up to 6 digits | |
int[] digits = | |
{ | |
number / 100000 % 10, | |
number / 10000 % 10, | |
number / 1000 % 10, | |
number / 100 % 10, | |
number / 10 % 10, | |
number % 10 | |
}; | |
if (displayMode == DisplayMode.AlignRight) | |
{ | |
//blank leading zeros | |
for (var i = 0; i < digits.Length; i++) | |
{ | |
if (digits[i] != 0) break; | |
digits[i] = 10; //setting to 10 will blank that digit | |
} | |
} | |
if (displayMode == DisplayMode.AlignLeft) | |
{ | |
var leadingZeroCount = 0; | |
for (var i = 0; i < digits.Length; i++) | |
{ | |
if (digits[i] != 0) break; | |
leadingZeroCount++; | |
} | |
//move the digits left | |
for (var i = 0; i < digits.Length - leadingZeroCount; i++) | |
{ | |
digits[i] = digits[i + leadingZeroCount]; | |
} | |
//blank the old positions | |
for (var i = digits.Length - leadingZeroCount; i < digits.Length; i++) | |
{ | |
digits[i] = 10; | |
} | |
} | |
DisplayNumbers( | |
digits[0], | |
digits[1], | |
digits[2], | |
digits[3], | |
digits[4], | |
digits[5] | |
); | |
} | |
public enum DisplayMode | |
{ | |
ZeroPadded, | |
AlignRight, | |
AlignLeft | |
} | |
//digits, anode, chip | |
//1, 2, 1 | |
//2, 1, 1 | |
//3, 3, 1 | |
//4, 2, 2 | |
//5, 1, 2 | |
//6, 3, 2 | |
private static void DisplaySet(OutputPort anode, int number1, int number2) | |
{ | |
var input1 = GetInput(number1); | |
WriteInput(input1, SN74141AInputs); | |
var input2 = GetInput(number2); | |
WriteInput(input2, SN74141BInputs); | |
anode.Write(true); | |
Thread.Sleep(2); | |
anode.Write(false); | |
} | |
private static bool[] GetInput(int number) | |
{ | |
//Get an array of bool that represents the binary version of number... | |
var b = new[] | |
{ | |
(number & 1) != 0, | |
(number & 2) != 0, | |
(number & 4) != 0, | |
(number & 8) != 0, | |
}; | |
return b; | |
} | |
private static void WriteInput(bool[] input, OutputPort[] inputs) | |
{ | |
for (var i = 0; i < input.Length; i++) | |
inputs[i].Write(input[i]); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Microsoft.SPOT; | |
//thanks: http://forums.netduino.com/index.php?/topic/575-updated-web-server/ | |
// This is needed for extension methods to work | |
namespace System.Runtime.CompilerServices | |
{ | |
public class ExtensionAttribute : Attribute { } | |
} | |
namespace NetduinoPlusWebServer | |
{ | |
/// <summary> | |
/// Extension methods | |
/// </summary> | |
public static class Extension | |
{ | |
/// <summary> | |
/// Replace characters in a string | |
/// </summary> | |
/// <param name="stringToSearch"></param> | |
/// <param name="charToFind"></param> | |
/// <param name="charToSubstitute"></param> | |
/// <returns></returns> | |
public static string Replace(this string stringToSearch, char charToFind, char charToSubstitute) | |
{ | |
// Surely there must be nicer way than this? | |
char[] chars = stringToSearch.ToCharArray(); | |
for (int i = 0; i < chars.Length; i++) | |
if (chars[i] == charToFind) chars[i] = charToSubstitute; | |
return new string(chars); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Microsoft.SPOT; | |
using System.Net.Sockets; | |
using System.Net; | |
using System.Text; | |
using System.Threading; | |
//thanks: http://forums.netduino.com/index.php?/topic/575-updated-web-server/ | |
namespace NetduinoPlusWebServer | |
{ | |
public delegate void RequestReceivedDelegate(Request request); | |
public class Listener : IDisposable | |
{ | |
const int maxRequestSize = 1024; | |
readonly int portNumber = 80; | |
private Socket listeningSocket = null; | |
private RequestReceivedDelegate requestReceived; | |
public Listener(RequestReceivedDelegate RequestReceived) | |
: this(RequestReceived, 80) { } | |
public Listener(RequestReceivedDelegate RequestReceived, int PortNumber) | |
{ | |
portNumber = PortNumber; | |
requestReceived = RequestReceived; | |
listeningSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); | |
listeningSocket.Bind(new IPEndPoint(IPAddress.Any, portNumber)); | |
listeningSocket.Listen(10); | |
new Thread(StartListening).Start(); | |
} | |
~Listener() | |
{ | |
Dispose(); | |
} | |
public void StartListening() | |
{ | |
while (true) | |
{ | |
using (Socket clientSocket = listeningSocket.Accept()) | |
{ | |
IPEndPoint clientIP = clientSocket.RemoteEndPoint as IPEndPoint; | |
Debug.Print("Received request from " + clientIP.ToString()); | |
var x = clientSocket.RemoteEndPoint; | |
int availableBytes = clientSocket.Available; | |
Debug.Print(DateTime.Now.ToString() + " " + availableBytes.ToString() + " request bytes available"); | |
int bytesReceived = (availableBytes > maxRequestSize ? maxRequestSize : availableBytes); | |
if (bytesReceived > 0) | |
{ | |
byte[] buffer = new byte[bytesReceived]; // Buffer probably should be larger than this. | |
int readByteCount = clientSocket.Receive(buffer, bytesReceived, SocketFlags.None); | |
using (Request r = new Request(clientSocket, Encoding.UTF8.GetChars(buffer))) | |
{ | |
Debug.Print(DateTime.Now.ToString() + " " + r.URL); | |
if (requestReceived != null) requestReceived(r); | |
} | |
} | |
} | |
// I always like to have this in a continuous loop. Helps prevent lock-ups | |
Thread.Sleep(10); | |
} | |
} | |
#region IDisposable Members | |
public void Dispose() | |
{ | |
if (listeningSocket != null) listeningSocket.Close(); | |
} | |
#endregion | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Threading; | |
using Microsoft.SPOT; | |
using Microsoft.SPOT.Hardware; | |
using NetduinoArduinix; | |
using SecretLabs.NETMF.Hardware; | |
using SecretLabs.NETMF.Hardware.Netduino; | |
using System.IO; | |
namespace NetduinoPlusWebServer | |
{ | |
public class Program | |
{ | |
const string WebFolder = "\\SD\\Web"; | |
static readonly object Locker = new object(); | |
static int _numbers = -1; | |
static readonly ArduinixShield Arduinix = new ArduinixShield(); | |
public static void Main() | |
{ | |
var webServer = new Listener(RequestReceived); | |
while (true) | |
{ | |
if (_numbers == -1) | |
//blank the display | |
Arduinix.DisplayNumbers(10, 10, 10, 10, 10, 10); | |
else | |
Arduinix.DisplayNumber(_numbers); | |
} | |
} | |
private static void RequestReceived(Request request) | |
{ | |
// Use this for a really basic check that it's working | |
//request.SendResponse("<html><body><p>Request from " + request.Client.ToString() + " received at " + DateTime.Now.ToString() + "</p><p>Method: " + request.Method + "<br />URL: " + request.URL +"</p></body></html>"); | |
//watch for /favicon.ico | |
Debug.Print("URL: " + request.URL); | |
//request. | |
if (request.URL == "/") | |
{ | |
const string html = @"<!DOCTYPE html> | |
<html> | |
<head><title>Nixie Tubes</title></head> | |
<body> | |
<h1>Set the nixie tubes!</h1> | |
<form> | |
<div style='margin-bottom:15px'><label>Numbers: <input id=numbers type=text></label></div> | |
<div><input type=submit value='Set the nixie tubes!'></div> | |
</form> | |
<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js'></script> | |
<script> | |
$(function() { | |
$('input[type=submit]').click(function(){ | |
$.get('/numbers/' + $('#numbers').val()); | |
return false; | |
}); | |
}); | |
</script> | |
</body> | |
</html> | |
"; | |
request.SendResponse(html); | |
return; | |
} | |
if (request.URL.Length < 9 || request.URL.Substring(0, 9) != "/numbers/") | |
{ | |
Debug.Print("URL not matched"); | |
Debug.Print(request.URL.Substring(0, 8)); | |
return; | |
} | |
var newNumbers = int.Parse(request.URL.Trim('/').Split('/')[1]); | |
lock(Locker) | |
{ | |
_numbers = newNumbers; | |
} | |
Debug.Print("numbers: " + _numbers); | |
request.SendResponse("{ 'ok' : true, 'numbers': " + _numbers + "}"); | |
// Send a file | |
//TrySendFile(request); | |
} | |
/// <summary> | |
/// Look for a file on the SD card and send it back if it exists | |
/// </summary> | |
/// <param name="request"></param> | |
private static void TrySendFile(Request request) | |
{ | |
// Replace / with \ | |
string filePath = WebFolder + request.URL.Replace('/', '\\'); | |
if (File.Exists(filePath)) | |
request.SendFile(filePath); | |
else | |
request.Send404(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Microsoft.SPOT; | |
using System.Net.Sockets; | |
using System.Text; | |
using System.Net; | |
using System.IO; | |
//thanks: http://forums.netduino.com/index.php?/topic/575-updated-web-server/ | |
namespace NetduinoPlusWebServer | |
{ | |
/// <summary> | |
/// Holds information about a web request | |
/// </summary> | |
/// <remarks> | |
/// Will expand as required, but stay simple until needed. | |
/// </remarks> | |
public class Request : IDisposable | |
{ | |
private string method; | |
private string url; | |
private Socket client; | |
const int fileBufferSize = 256; | |
internal Request(Socket Client, char[] Data) | |
{ | |
client = Client; | |
ProcessRequest(Data); | |
} | |
/// <summary> | |
/// Request method | |
/// </summary> | |
public string Method | |
{ | |
get { return method; } | |
} | |
/// <summary> | |
/// URL of request | |
/// </summary> | |
public string URL | |
{ | |
get { return url; } | |
} | |
/// <summary> | |
/// Client IP address | |
/// </summary> | |
public IPAddress Client | |
{ | |
get | |
{ | |
IPEndPoint ip = client.RemoteEndPoint as IPEndPoint; | |
if (ip != null) return ip.Address; | |
return null; | |
} | |
} | |
/// <summary> | |
/// Send a response back to the client | |
/// </summary> | |
/// <param name="response"></param> | |
public void SendResponse(string response, string type = "text/html") | |
{ | |
if (client != null) | |
{ | |
string header = "HTTP/1.0 200 OK\r\nContent-Type: " + type + "; charset=utf-8\r\nContent-Length: " + response.Length.ToString() + "\r\nConnection: close\r\n\r\n"; | |
client.Send(Encoding.UTF8.GetBytes(header), header.Length, SocketFlags.None); | |
client.Send(Encoding.UTF8.GetBytes(response), response.Length, SocketFlags.None); | |
Debug.Print("Response of " + response.Length.ToString() + " sent."); | |
} | |
} | |
/// <summary> | |
/// Sends a file back to the client | |
/// </summary> | |
/// <remarks> | |
/// Assumes the application using this has checked whether it exists | |
/// </remarks> | |
/// <param name="filePath"></param> | |
public void SendFile(string filePath) | |
{ | |
// Map the file extension to a mime type | |
string type = ""; | |
int dot = filePath.LastIndexOf('.'); | |
if (dot != 0) | |
switch (filePath.Substring(dot + 1)) | |
{ | |
case "css": | |
type = "text/css"; | |
break; | |
case "xml": | |
case "xsl": | |
type = "text/xml"; | |
break; | |
case "jpg": | |
case "jpeg": | |
type = "image/jpeg"; | |
break; | |
case "gif": | |
type = "image/gif"; | |
break; | |
// Not exhaustive. Extend this list as required. | |
} | |
using (FileStream inputStream = new FileStream(filePath, FileMode.Open)) | |
{ | |
// Send the header | |
string header = "HTTP/1.0 200 OK\r\nContent-Type: " + type + "; charset=utf-8\r\nContent-Length: " + inputStream.Length.ToString() + "\r\nConnection: close\r\n\r\n"; | |
client.Send(Encoding.UTF8.GetBytes(header), header.Length, SocketFlags.None); | |
byte[] readBuffer = new byte[fileBufferSize]; | |
while (true) | |
{ | |
// Send the file a few bytes at a time | |
int bytesRead = inputStream.Read(readBuffer, 0, readBuffer.Length); | |
if (bytesRead == 0) | |
break; | |
client.Send(readBuffer, bytesRead, SocketFlags.None); | |
Debug.Print("Sending " + readBuffer.Length.ToString() + "bytes..."); | |
} | |
} | |
Debug.Print("Sent file " + filePath); | |
} | |
/// <summary> | |
/// Send a Not Found response | |
/// </summary> | |
public void Send404() | |
{ | |
string header = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"; | |
if (client != null) | |
client.Send(Encoding.UTF8.GetBytes(header), header.Length, SocketFlags.None); | |
Debug.Print("Sent 404 Not Found"); | |
} | |
/// <summary> | |
/// Process the request header | |
/// </summary> | |
/// <param name="data"></param> | |
private void ProcessRequest(char[] data) | |
{ | |
string content = new string(data); | |
string firstLine = content.Substring(0, content.IndexOf('\n')); | |
// Parse the first line of the request: "GET /path/ HTTP/1.1" | |
string[] words = firstLine.Split(' '); | |
method = words[0]; | |
url = words[1]; | |
// Could look for any further headers in other lines of the request if required (e.g. User-Agent, Cookie) | |
} | |
#region IDisposable Members | |
public void Dispose() | |
{ | |
if (client != null) | |
{ | |
client.Close(); | |
client = null; | |
} | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment