This example demonstrates how to send notification from a .NET Gadgeteer sensor to a REST Web service that records data about the sensor activation. In this case the sensor is the Light Sensor implemented in the previous post. The notification data is simply a sensor account identifier and the date and time the sensor is activated. The code is extensible for various types of sensors and notifications.
<This example has been updated to use the new Gadgeteer.Networking.HttpHelper methods. The POSTContent class sends the request asynchronously and does not block the dispatcher while the HTTP request is outstanding. The process is simpler to use and more efficient.>
Sending the Sensor Notification to Web Service as POST Request
This code extends the basic test scenario described under the heading Using the Events raised by a new .NET Gadgeteer Module in Application Code in the Light Sensor implementation.
The Light Sensor test code is adapted to send notification to a Web service when the sensor is activated. In the LightSensorHigh event handler, use the static CreateHttpPostRequest method of HttpHelper to create the request to the Web service. The account identifier is specified by the URL (the URL template is described in the Web service implementation following this code). This example doesn’t need any information in the body of the POST request, so the POSTContent parameter is created without content. Content type is null. This is an asynchronous method that gets the response in the delegate method, HttpRequest.ResponseReceived. The delegate is assigned before calling HttpRequest.SendRequest.
using System; using Microsoft.SPOT; using Microsoft.SPOT.Presentation; using Microsoft.SPOT.Presentation.Controls; using Microsoft.SPOT.Presentation.Media; using GT = Gadgeteer; using GTM = Gadgeteer.Modules; using Gadgeteer.Modules.GHIElectronics; using Gadgeteer.Modules.IntegralDesign; using System.Net; namespace TestLightSensor { public partial class Program { void ProgramStarted() { lightSensor.LightSensorHigh += new LightSensor.LightSensorEventHandler(lightSensor_LightSensorHigh); lightSensor.LightSensorLow += new LightSensor.LightSensorEventHandler(lightSensor_LightSensorLow); led.TurnRed(); ethernet.UseDHCP(); ethernet.NetworkUp += new GTM.Module.NetworkModule.NetworkEventHandler(ethernet_NetworkUp); ethernet.NetworkDown += new GTM.Module.NetworkModule.NetworkEventHandler(ethernet_NetworkDown); Debug.Print("Program Started"); } void lightSensor_LightSensorHigh(LightSensor sender, LightSensor.LightSensorState state) { led.BlinkRepeatedly(GT.Color.White); POSTContent emptyPost = new POSTContent(); var req = HttpHelper.CreateHttpPostRequest("http://integral-data.com/SensorData/Sensor/TestREST", emptyPost, null); req.ResponseReceived += new HttpRequest.ResponseHandler(req_ResponseReceived); req.SendRequest(); } void req_ResponseReceived(HttpRequest sender, HttpResponse response) { if (response.StatusCode != "200") Debug.Print(response.StatusCode); led.TurnOff(); } void ethernet_NetworkDown(GTM.Module.NetworkModule sender, GTM.Module.NetworkModule.NetworkState state) { led.TurnRed(); } void ethernet_NetworkUp(GTM.Module.NetworkModule sender, GTM.Module.NetworkModule.NetworkState state) { led.TurnGreen(); } void lightSensor_LightSensorLow(LightSensor sender, LightSensor.LightSensorState state) { led.TurnOff(); } } }
Web service Implementation to Record Sensor Data
The REST Web service for sensor data is implemented using the Visual Studio WCF REST Service Template 4. Setting up a REST Web Service with this template is elementary, but there are configuration details that can be tricky. First download and install the Online template from Visual Studio->New Project->Online Templates.
Most of the code for the project is in the Service1.cs file. I changed the name Service1 and the name of the file to SensorData . I don’t use the SampleItem.cs code in this service because I want to use an ADO.NET Entity Data Model to manage the sensor data collected by this Web service. A lot of programmers use the ADO.NET Entity Framework object/relational mapping to avoid putting SQL strings in their code. If you haven’t used it, this example demonstrates a basic scenario for using the Entity Framework. You need SQL Server Compact Edition, but you don’t have to use SQL syntax in the implementation of this service.
Add a SQL Server Compact 4.0 Local Database the project from the Solution Explorer->Add->New Item. I name the database SensorDatabase and put it in the App_Data folder as recommended by the wizard. Double click the SensorDatabase item in Solution Explorer to open the Server Explorer.
The data from sensor accounts consists of an identity key for each row of data, an account name, and the time the sensor is activated. The table that stores these data elements will have columns for each element. In the Server Explorer, click the Tables icon, and add a new table; name the table SensorDataTable. Create an Id column of type int that does not allow nulls, is unique, and is the primary key. Make it an Identity column with Identity Increment = 1 and Identity Seed = 1. Create a second column named SensorAccount of type nvarchar(20) that is not unique, is not an identity column, and does not allow nulls. Create a third column named ActivationTime of type DateTime that is not unique, is not an identity column, and does not allow nulls. Accept the defaults for size of these columns.
Now that we have the place to store data, we can implement the data model that will access the data from code. Right click the project in Solution Explorer and add an ADO.NET Entity Data Model, named SensorDataModel. In the next page of the wizard select Generate from database. The SensorDatabase should be already selected in the next page drop-down list and the connection string written for you. Be sure Save entity connection string settings in Web.Config is checked. The default name, SensorDatabaseEntities is ok. click Next. Check on the Tables box in the final page of the wizard and click Finish.
When the SensorDataModel.edmx designer page opens, in the properties pane, rename SensorDataTables to SensorDataItems and SensorDataTable to SensorDataItem. The SensorDataItem in the designer area represents an entity that contains the results of a single request that is sent to the Web service when a sensor is activated. Save the SensorDataModel.edmx file or build the application.
URL Template Implementations
In the SensorData.cs file comment out or delete the list of SampleItems as shown in the following lines. This example uses data base storage and the Entity Data Model.
// TODO: Implement the collection resource that will contain the SampleItem instances //System.Collections.Generic.List SensorDataList = new List();
The WCF REST Service Template 4 is pretty slick for creation of the URL templates that define the path to the Web service. The attributes WebGet and WebInvoke specify HTML methods GET, PUT, POST, and DELETE in several permutations. It takes a little experimentation to get the idea, but using the templates saves a lot of work. The implementations of a GET request take two forms, one to get the collection of all sensor data and another to get those of a single account.
The method to get all the sensor data follows. The default code is left commented so you can see how it fits with the templage generated code. Note the attribute [WebGet(UriTemplate = “Data”)] that specifies the path after the domain name in the Web service address syntax. This method will return all the SensorDataItems in storage orderd by ActivationTime.
[WebGet(UriTemplate = "Data")] public List<SensorDataItem> GetCollection() { // TODO: Replace the current implementation to return a collection of SampleItem instances // return new List<SampleItem>() { new SampleItem() { Id = 1, StringValue = "Hello" } }; using (SensorDatabaseEntities objectContext = new SensorDatabaseEntities()) { return objectContext.SensorDataItems.OrderBy(SensorData => SensorData.ActivationTime).ToList(); } }
The POST method that creates a new instance of SensorDataItem uses [WebInvoke(UriTemplate = “Sensor/{account”)}. This syntax indicates the path item “Sensor” and a variable parameter that specifies the sensor account name. The complete method implementation follows.
[WebInvoke(UriTemplate = "Sensor/{account}", Method = "POST")] public SensorDataItem Create(SensorDataItem instance, string account) { using (SensorDatabaseEntities objectContext = new SensorDatabaseEntities()) { SensorDataItem newDatum = new SensorDataItem(); newDatum.ActivationTime = DateTime.Now; newDatum.SensorAccount = account; objectContext.SensorDataItems.AddObject(newDatum); objectContext.SaveChanges(); return newDatum; } }
Starting the Web Service and Recording Sensor Activation Data
These two methods are enough to get started with the Web service. I had to add the following line in the Web.Config file after finally getting to the bottom of an error message.
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
With this in place you should be able to test the service using the Visual Studio Development Server. Testing on the Visual Studio Development Server may not give the same results as a commercial host. See this article by Rick Anderson if your application works on the development server but not on the host server. SQL Server Compact Edition evidently requires some libraries that are not on my host’s server. Right click the project in Solution Explorer and select SQL Server Compact in Add Deployable Dependencies. You may also have to disable Basic Authentication in IIS for the project.
The easiest way to test the POST method to create a new SensorDataItem is to use the code .NET Gadgeteer device code under the heading above: Sending the Sensor Notification to Web Service as POST Request.
#1 by tamberg on November 12, 2011 - 5:43 AM
Hi Michael, would it make sense to trigger the WebRequest with a timer (e.g. once a minute) and post the state (high/low) of the light sensor to your server? This way you’ll prevent sending too many requests in the case of an unstable sensor “flickering” between low and high and you’ll have some sort of feedback even if the sensor stays low for an extended amount of time. Cheers, tamberg
#2 by Michael Dodaro on November 12, 2011 - 7:21 AM
Yes, the NetMF timer is solid, and you can run serveral instances of it if needed by an application. I have seen some bounce in my sensor occasionally; that’s why the pull-up resister is in the circuit. The current version is conceived as a way to monitor many sensors, so, in this scenario, I don’t want so much data that it might overload the service, but I see your point that an unstable sensor could overload it anyway. By the way, the service is locked down, but I can open it up to you if you want to try out a sensor of one sort or another.
#3 by tamberg on November 12, 2011 - 7:28 AM
Ok, I see. Nice that you also show how to program a service. Another option would probably be to use http://www.pachube.com/ (now they’re free, even if you’ve got many feeds). Cheers, tamberg
#4 by Marco Minerva on November 13, 2011 - 4:58 AM
Hi!
Great post, as usual 🙂
I have an offer for you. I have created a new project on CodePlex, .NET Gadgeteer Toolkit, in which I want to add all the classes and helper methods that I develop for this platform. It would be great if you would like to contribute. What do you think about it?
#5 by Michael Dodaro on November 13, 2011 - 8:20 AM
Yes, thanks, Marco. I’d be happy to contribute and would be interested in having posts by you on this site.
#6 by Michael Dodaro on January 20, 2012 - 2:31 PM
I updated this example to use the new Gadgeteer.Networking.HttpHelper methods. The POSTContent class sends the request asynchronously in order to not block the dispatcher while the HTTP request is outstanding. The process is simpler to use and more efficient.
#7 by Lin on September 13, 2012 - 7:14 PM
Dear Michael:
I can’t install WCF REST Service Template 4 in VS 2010 express.
Does this Template only support VS 2010 Professional ?
#8 by Michael Dodaro on September 13, 2012 - 9:37 PM
From this page: http://visualstudiogallery.msdn.microsoft.com/fbc7e5c1-a0d2-41bd-9d7b-e54c845394cd I think you can install the template on Visual Studio 2010 Express.
#9 by Marco Minerva on September 14, 2012 - 12:44 AM
In this page: http://www.csharpcity.com/adding-templates-to-visual-studio-express-2010/ you can find some instructions about how to install project templates in Visual Studio Express. As you’re talking about WCF, you need to use Visual Web Developer Express, of course.
#10 by Lin on September 18, 2012 - 10:55 PM
Dear Michael and Marco:
I read this article many times, but still can’t understand how to create the REST web service.
Now,I can open the VWD WCF REST service. but
1. I can’t find SQL Server Compact 4.0 Local Database in my VS.
2. I can’t find this (//System.Collections.Generic.List SensorDataList = new List(); )in my Service1.cs
I also read your other article “Sending .NET Gadgeteer Sensor Data to a REST Web Service”
Could you share the complete project code for this article(“Sending .NET Gadgeteer Sensor Data to a REST Web Service” )? Then I can trace the code to learn.
Thank you very much.
#11 by Marco Minerva on September 19, 2012 - 4:33 PM
1. SQL Server Compact 4.0 is an external component, so you need to download it separately. You can find it here: http://www.microsoft.com/en-us/download/details.aspx?id=17876
2. Never mind if you don’t need this step.
#12 by Lin on September 19, 2012 - 2:03 AM
I create a simple Web Service and test it.
The get method can work. but the post not.
The post code is below,
[WebInvoke(UriTemplate = “Sensor/{test}”, Method = “POST”)]
public string HelloWorldPost(string test)
{
return “hello ” + test;
}
Then I type http://localhost:3769/Service1/Sensor/1 in IE
Then I get
“Method not allowed. Please see the service help page for constructing valid requests to the service.”
Thanks.
#13 by Marco Minerva on September 19, 2012 - 4:34 PM
You can’t use a browser to send a POST request, because you can’t specify the request content. You can only test GET method.
#14 by Michael Dodaro on September 19, 2012 - 6:21 PM
Thanks, Marco! It’s been a busy day here.