Contents
1. Background
2. Arduino Serial Communication With C#
2.1 Getting Started With Arduino Serial
2.2 C# Serial Client
2.2.1 Reading Data Over Serial Port
2.2.2 Writing Data To Serial Port
2.3 ArdOS based Arduino Sketch to Handle Serial Communication
3. Developing Our Own WebService as Middleware between C# Client and World
3.1 Communication Protocol
3.2 Database Design
3.3 WebService
4. Binding C# Client to WebService
4.1 Web Service Discovery
4.2 Generating Notification
4.3 Generating Commands
5. Developing Web Client To Communicate With IoT Device through Our Service
6. Android Client For Our IoT Service
7. Conclusion
Figure 1: Architecture of Turning Arduino to IoT device
Arduino is a popular platform for DIY projects and developing hardware prototype. Internet of Things (IoT) is an abstraction of connection such devices and hardware platform to Internet using unique IP address. So an Arduino connected to IoT can be controlled and it's data can be obtained remotely. But for that Arduino has to be connected to internet.
Arduino Yun is latest in the family of Arduino hardware which has both Wifi as well as an Ethernet module to connect to internet. Once connected we need certain mechanism to discover the device on the internet. Common techniques are Port Forwarding, WebSockets and Polling. But for any of these to work, primary requirement is that the device must have TCP/IP stack which ofcourse Yun has.
However if we have a real low cost Arduino device ( say Arduino Dueminolova or Decimilia) which are about under $10 and which does not have any connectivity module and if we still want the device to be in the network, what do we do?
This tutorial will focus on :
1) Show the serial communication principle with Arduino
2) Develop a web service to connect Arduino with Internet
3) Show an Android based technique to access the connected device remotely.
Figure 1 should clearly tell you as what we are about to do in this tutorial.
Any developer who has little to no exposure to embedded programming and hardware devices should be relatively be comfortable with this tutorial and will find it easy to get started with IoT. What's best is the technique can also be used for a wide range of hardware that supports serial communication.
Further once the basic functioning of IoT at different design level is understood, working with Services ( Connection as Service, Platform as Service) will become easier as you have knowledge of all the layers of IoT.
If you are relatively new to Arduino, I recommend you to go through my basic tutorial on Arduino and ArdOS. This should give you some clear understanding of setting up Arduino environment and working with basic Arduino programming. I have used Arduino Duemilanove for this tutorial, you are free to use Arduino Uno R3 or Decimilia or even Freeduino.
First plug in the Arduino USB cable with your laptop. We would first test the Serial communication with Arduino Serial followed by integrating it with a C# Application.
Let us write a simple Arduino sketch that initiates a Serial communication at 19200 baud rate. The application reads the analog pin 5 and prints the value in Serial port once at every second.
void setup()
{
Serial.begin(19200);
}
void loop()
{
int a=analogRead(5);
Serial.println(a);
delay(1000);
}
Once you compile and upload the sketch to your Arduino board, you can test the result through Serial Monitor of Arduino by opening the Serial Monitor Window from Tools->Serial Monitor
Figure 2.1: Result of Simple Arduino Serial Printing of Analog Pin 5 Voltage
Serial Monitor is a Serial Communication Client which can be used to bind to any devices that supports Serial Communication. Now our Aim is to build a C# Client that can be used to communicate with the device. Let us have a look at the basic client design.
Figure 2.2: Design of C#.Net Serial Client
The design is very simple. The combobox at the top must display all the available ports , out of which the user will select the appropriate USB port. Once selected, he will click on the connect button which should request the device connected in the selected serial port to initiate a serial communication session. Once the communication is established, the data being print at the serial port by our first Arduino sketch would be displayed in the list box.
We also create a text box and a button to send command to the device through serial port. But first we would test readin the serial values from port.
In the Form_Load event we would read all the available Serial ports and would assign the values to the combobox.
private void Form1_Load(object sender, EventArgs e)
{
string[] ports = System.IO.Ports.SerialPort.GetPortNames();
for (int i = 0; i < ports.Length; i++)
{
comboBox1.Items.Add(ports[i]);
}
}
You can also create another combo box for storing baud rate values like 9600,19200, 57600 ,115200 and so on. But as I am using time tested 19200 baud rate, I have nt used another option for baud rate selection.
Now we attach event handler for the connect button.
private void button2_Click(object sender, EventArgs e)
{
try
{
serialPort1.PortName = comboBox1.SelectedItem.ToString();
serialPort1.BaudRate = 19200;
serialPort1.Open();
MessageBox.Show("Success");
}
catch (Exception ex)
{
MessageBox.Show("Failed: "+ex.Message);
}
}
serialPort1
is an uninitialized object of SerialPort
class. As you see that once user has selected a port name from combo box and clicked on connect button, the SerialPort object is initialized with the selected port name and specified baud rate of 19200. Finally port is opened.
Our aim is to read data coming from Serial Port. As serialPort1 will send and receive data from a different thread than the main thread, we can not access the data coming over serial port through main thread as it would cause Cross Threaded application. Hence we will use a simple delegate to pass data from Serial Port to Main UI components.
Let's declare a simple delegate to a void method that accepts a string argument. Serial data would be passed as argument.
delegate void del(string data);
Let us now create the method which this delegate would be initialized to:
void Display(string s)
{
listBox1.Items.Add(s);
}
Finally let us initialize our del
Delegate to Display
method in the constructor of the form.
del MyDlg;
public Form1()
{
InitializeComponent();
MyDlg = new del(Display);
}
Arrival of Serial data is asynchronous and thankfully raises an event. Therefore we must initialize Serial Data Receive event handler in Initialize component after serialPort1
is initialized.
serialPort1.DataReceived +=new System.IO.Ports.SerialDataReceivedEventHandler(this.serialPort1_DataReceived);
Finally when Serial data arrives, read entire line as a String using ReadLine() method of SerialPort class and assign the string to ListBox by calling MyDlg through BeginInvoke
method.
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
String s = serialPort1.ReadLine();
this.BeginInvoke(MyDlg, s);
}
That's all and we are ready to read data from Arduino device.
For testing, first ensure that you have closed Arduino IDE's Serial Monitor interface. As that is also a client, till Serial Monitor is On and communicating with your device, no other application can open the port.
Close Arduino Serial Monitor, Keep USB cable plugged in, build and run your C# application. Select the port on which the device is connected and click on the connect button.
You will see that the application is receiving data and is logging in the list box. You can verify that the data is indeed analog port voltage by touching the port values with your finger. You will see a jump in the values.
Figure: 2.3: C# Application receiving Serial Data from Arduino
Having successfully implemented Serial Read, now let us work towards sending command to serial port.
Before completing Serial read, I want to discuss few crucial points that are essential to know for Serial communication. Add following code snippet in your loop()
method.
if(Serial.available())
{
n2=Serial.read() ;
if(n2>=0)
{
Serial.print(" DATA RECEIVED AS SERIAL INPUT COMMAND IS :");
Serial.println(n2);
}
}
When you compile and upload the sketch in your Arduino device and test the result with Serial monitor you will see some surprising result:
When you enter 1 in Serial Monitor input window, you will see 49, for zero you will see 48 and for 10, you will see two lines: 49 followed by 48. See figure 2.4.
Figure 2.4: Serial Input from Serial Monitor
That is because Serial monitor interface accepts input as character. So while you give input as '1', it's not number that you are providing, you are giving character '1' as input whose's ASCII equivalent is 49 and thus you see 49 when you enter 1.
However C#'s serial communication can send string as well as numbers to the serial port. Therefore the logic you chalk out while working with Serial Input would differ based on whether you are giving the input from Serial Monitor client or C# client. You can also write some simple logic for handling both types of inputs. Also it is not advisable to print anything when you receive a serial data from a client in Arduino as that printed text would be available to the client also.
Now when C# client generates a command, Arduino sketch must read asynchronously. Therefore Arduino must run two tasks "parallely"( Cocurrently to be more precise). In one task, it should keep printing the analog value after every second and in another task, it must keep checking the value available in Serial port and deal with the value.
Therefore we will use ArdOS for Arduino sketch. ArdOS provides very good support for multi tasking which is trivial here. In case you are not familiar with ArdOS, I urge you to please go through my ArdOS tutorial and set up your Arduino environment to support ArdOS.
Let us then create an ArdOS sketch with two tasks. One which keeps printing Analog Data into serial port every second and the other one should take data from serial port as command. If user provides 1 through Serial port, LED in pin 13 should be turned on, when giving 2, it should be turned off.
#include <kernel.h>
#include <queue.h>
#include <sema.h>
#define NUM_TASKS 2
void taskSerialRead(void *p)
{
int n2=0;
while(1)
{
if(Serial.available())
{
n2=Serial.read() ;
if(n2>0)
{
if(n2==1)
{
digitalWrite(13,HIGH);
}
if(n2==2)
{
digitalWrite(13,LOW);
}
}
OSSleep(100);
}
}
}
void taskSerialWrite(void * p)
{
int n1=0;
while(1)
{
n1=analogRead(5);
Serial.println(n1);
OSSleep(1000);
}
}
void setup()
{
OSInit(NUM_TASKS);
Serial.begin(19200);
pinMode(13, OUTPUT);
digitalWrite(13,LOW);
OSCreateTask(0, taskSerialRead, NULL);
OSCreateTask(1, taskSerialWrite, NULL);
OSRun();
}
void loop()
{
}
Compile and upload the sketch and prepare your C# Clicnt to send data to the device through Serial port. It is quite simple to guess that Writing data by C# client will be synchronous and with button click event.
So let us complete the Event handler for button1 and allow the client to write data to Serial port.
private void button1_Click_1(object sender, EventArgs e)
{
try
{
serialPort1.Write(new byte[] { byte.Parse(textBox1.Text) }, 0, 1);
}
catch
{
}
}
So we send our command as a byte and notify that the array should be read from offset 0 and only 1 element must be read.
Now when you run your .Net application and click send button after giving 1 in the command in the input text box, you will see the LED in Pin 13 is turned on and will be turned off when you provide command 2.
So with that we are now ready with an Arduino sketch that can communicate with C# Client application through Serial communication.
Now we need to develop a WebService which can read command from other applications and the C# Client can poll the command from the web service.
There are three entities now which must communicate in a seamless fashion to enable controlling Arduino device and fetching data from it. The entities involve in this communications are: C# Client which is connected to the Arduino using Serial Port, The Web Service, The remote client which requests for this specific device's data.
Hence a suitable protocol must be developed that enables this communication. Have a look at the foloowing diagram to understand the communication basics.
Figure: 3.1: Communication Protocol of Proposed Custom Service
We can summerize the protocol as below:
1) C# Client must continuesly receive data from Arduino and analyze the data. If there is an event (say temperature above some threshold or LDR falling behind certain range and so on) then it must raise an event and send some notification data to webservice along with IP address of the system.
2) When web service received data from C# Client, it saves the data with current time stamp.
3) When any new notification comes from the same C# Client, the web service deletes previous entry and stores the current entry ( this is to keep database usage limited)
4) A remote client( say an Android Client) must know the IP address of the system that IoT Android device is connected to and can request for the data. Web Service will search in the database for data from specific IP and should return the data.
5) Remote client may request to execute certain commands. This request is also stored in the database.
6) C# Client periodically polls the WebService to check if there is any command available for execution or not. WebService forwards the data stored in the database to C# Client. This notifies the Arduino device through Serial port. Arduino executes the command and notifies the result. If the result is successful, C# Sends WebService a Success notification. Upon receiving the success notification, the WebService must delete the stored command in order to avoid repeating the execution of same command over and over again.
Though Ideally Authentication, Authorization and Encryption must also be part of the above protocol in order to make it more secured, my focus in this article would be on developing the communication protocol. You can always work on the security layer.
So let's just get started by creating two tables in our database called Commands and Notifications
create table Commands( projId varchar(100),toDevice varchar(100),commandText varchar(100),generatedBy varchar(100), whatTime datetime, commandStatus varchar(100));
create table Notifications(projId varchar(100),fromDevice varchar(100), notificationMessage text, whatTime datetime);
I have created the table in my Server database. You can test the logic in your local SqlServer database. Among other fields, I have created a field called projId to facilitate reusability of the table across different applications. This is a good way to keep data of different apps separate when you are working with web database.
Let us now create light weight ASP.Net web service that Inserts, Updates, Deletes and Searches data in both the tables.
As our intention is to use the web service across different platforms, we would not return any object data or DataSet. Rather we will return a Marshalled string for the queries.
Here is our WebService which I am going to deploy in real time.
<%@ WebService language="C#" class="IoTService" %>
using System;
using System.Web.Services;
using System.Xml.Serialization;
public class IoTService
{
[WebMethod]
public int InsertCommand(string projId, string toDevice, string commandText, string generatedBy, System.DateTime whatTime, string commandStatus) {
string connectionString = "server=\'YOUR_SERVER_ADDRESS\'; user id=\'USER_ID\'; password=\'PASSWORD" +
"\'; database=\'DATABASE_NAME\'";
System.Data.IDbConnection dbConnection = new System.Data.SqlClient.SqlConnection(connectionString);
string queryString = "INSERT INTO [Commands] ([projId], [toDevice], [commandText], [generatedBy], [what" +
"Time], [commandStatus]) VALUES (@projId, @toDevice, @commandText, @generatedBy, " +
"@whatTime, @commandStatus)";
System.Data.IDbCommand dbCommand = new System.Data.SqlClient.SqlCommand();
dbCommand.CommandText = queryString;
dbCommand.Connection = dbConnection;
System.Data.IDataParameter dbParam_projId = new System.Data.SqlClient.SqlParameter();
dbParam_projId.ParameterName = "@projId";
dbParam_projId.Value = projId;
dbParam_projId.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_projId);
System.Data.IDataParameter dbParam_toDevice = new System.Data.SqlClient.SqlParameter();
dbParam_toDevice.ParameterName = "@toDevice";
dbParam_toDevice.Value = toDevice;
dbParam_toDevice.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_toDevice);
System.Data.IDataParameter dbParam_commandText = new System.Data.SqlClient.SqlParameter();
dbParam_commandText.ParameterName = "@commandText";
dbParam_commandText.Value = commandText;
dbParam_commandText.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_commandText);
System.Data.IDataParameter dbParam_generatedBy = new System.Data.SqlClient.SqlParameter();
dbParam_generatedBy.ParameterName = "@generatedBy";
dbParam_generatedBy.Value = generatedBy;
dbParam_generatedBy.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_generatedBy);
System.Data.IDataParameter dbParam_whatTime = new System.Data.SqlClient.SqlParameter();
dbParam_whatTime.ParameterName = "@whatTime";
dbParam_whatTime.Value = whatTime;
dbParam_whatTime.DbType = System.Data.DbType.DateTime;
dbCommand.Parameters.Add(dbParam_whatTime);
System.Data.IDataParameter dbParam_commandStatus = new System.Data.SqlClient.SqlParameter();
dbParam_commandStatus.ParameterName = "@commandStatus";
dbParam_commandStatus.Value = commandStatus;
dbParam_commandStatus.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_commandStatus);
int rowsAffected = 0;
dbConnection.Open();
try {
rowsAffected = dbCommand.ExecuteNonQuery();
}
finally {
dbConnection.Close();
}
return rowsAffected;
}
[WebMethod]
public string CommandToExecute(string projId, string toDevice) {
string connectionString = "server=\'YOUR_SERVER_ADDRESS\'; user id=\'USER_ID\'; password=\'PASSWORD" +
"\'; database=\'DATABASE_NAME\'";
System.Data.IDbConnection dbConnection = new System.Data.SqlClient.SqlConnection(connectionString);
string queryString = "SELECT [Commands].[commandText], [Commands].[generatedBy], [Commands].[whatTime]," +
" [Commands].[commandStatus] FROM [Commands] WHERE (([Commands].[projId] = @projI" +
"d) AND ([Commands].[toDevice] = @toDevice))";
System.Data.IDbCommand dbCommand = new System.Data.SqlClient.SqlCommand();
dbCommand.CommandText = queryString;
dbCommand.Connection = dbConnection;
System.Data.IDataParameter dbParam_projId = new System.Data.SqlClient.SqlParameter();
dbParam_projId.ParameterName = "@projId";
dbParam_projId.Value = projId;
dbParam_projId.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_projId);
System.Data.IDataParameter dbParam_toDevice = new System.Data.SqlClient.SqlParameter();
dbParam_toDevice.ParameterName = "@toDevice";
dbParam_toDevice.Value = toDevice;
dbParam_toDevice.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_toDevice);
dbConnection.Open();
System.Data.IDataReader dataReader = dbCommand.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
string s="";
while(dataReader.Read())
{
s=dataReader[0]+"#"+dataReader[1]+"#"+dataReader[2];
}
return s;
}
[WebMethod]
public int InsertNotification(string projId, string fromDevice, string notificationMessage, System.DateTime whatTime) {
string connectionString = "server=\'YOUR_SERVER_ADDRESS\'; user id=\'USER_ID\'; password=\'PASSWORD" +
"\'; database=\'DATABASE_NAME\'";
System.Data.IDbConnection dbConnection = new System.Data.SqlClient.SqlConnection(connectionString);
string queryString = "INSERT INTO [Notifications] ([projId], [fromDevice], [notificationMessage], [what" +
"Time]) VALUES (@projId, @fromDevice, @notificationMessage, @whatTime)";
System.Data.IDbCommand dbCommand = new System.Data.SqlClient.SqlCommand();
dbCommand.CommandText = queryString;
dbCommand.Connection = dbConnection;
System.Data.IDataParameter dbParam_projId = new System.Data.SqlClient.SqlParameter();
dbParam_projId.ParameterName = "@projId";
dbParam_projId.Value = projId;
dbParam_projId.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_projId);
System.Data.IDataParameter dbParam_fromDevice = new System.Data.SqlClient.SqlParameter();
dbParam_fromDevice.ParameterName = "@fromDevice";
dbParam_fromDevice.Value = fromDevice;
dbParam_fromDevice.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_fromDevice);
System.Data.IDataParameter dbParam_notificationMessage = new System.Data.SqlClient.SqlParameter();
dbParam_notificationMessage.ParameterName = "@notificationMessage";
dbParam_notificationMessage.Value = notificationMessage;
dbParam_notificationMessage.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_notificationMessage);
System.Data.IDataParameter dbParam_whatTime = new System.Data.SqlClient.SqlParameter();
dbParam_whatTime.ParameterName = "@whatTime";
dbParam_whatTime.Value = whatTime;
dbParam_whatTime.DbType = System.Data.DbType.DateTime;
dbCommand.Parameters.Add(dbParam_whatTime);
int rowsAffected = 0;
dbConnection.Open();
try {
rowsAffected = dbCommand.ExecuteNonQuery();
}
finally {
dbConnection.Close();
}
return rowsAffected;
}
[WebMethod]
public string FetchNotification(string projId, string fromDevice) {
string connectionString = "server=\'YOUR_SERVER_ADDRESS\'; user id=\'USER_ID\'; password=\'PASSWORD" +
"\'; database=\'DATABASE_NAME\'";
System.Data.IDbConnection dbConnection = new System.Data.SqlClient.SqlConnection(connectionString);
string queryString = "SELECT [Notifications].[notificationMessage], [Notifications].[whatTime] FROM [No" +
"tifications] WHERE (([Notifications].[projId] = @projId) AND ([Notifications].[f" +
"romDevice] = @fromDevice))";
System.Data.IDbCommand dbCommand = new System.Data.SqlClient.SqlCommand();
dbCommand.CommandText = queryString;
dbCommand.Connection = dbConnection;
System.Data.IDataParameter dbParam_projId = new System.Data.SqlClient.SqlParameter();
dbParam_projId.ParameterName = "@projId";
dbParam_projId.Value = projId;
dbParam_projId.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_projId);
System.Data.IDataParameter dbParam_fromDevice = new System.Data.SqlClient.SqlParameter();
dbParam_fromDevice.ParameterName = "@fromDevice";
dbParam_fromDevice.Value = fromDevice;
dbParam_fromDevice.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_fromDevice);
dbConnection.Open();
System.Data.IDataReader dataReader = dbCommand.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
string s="";
while(dataReader.Read())
{
s=dataReader[0]+"#"+dataReader[1];
}
return s;
}
[WebMethod]
public int UpdateCommandStatus(string projId, string toDevice, string commandStatus) {
string connectionString = "server=\'YOUR_SERVER_ADDRESS\'; user id=\'USER_ID\'; password=\'PASSWORD" +
"\'; database=\'DATABASE_NAME\'";
System.Data.IDbConnection dbConnection = new System.Data.SqlClient.SqlConnection(connectionString);
string queryString = "UPDATE [Commands] SET [commandStatus]=@commandStatus WHERE (([Commands].[projId] " +
"= @projId) AND ([Commands].[toDevice] = @toDevice))";
System.Data.IDbCommand dbCommand = new System.Data.SqlClient.SqlCommand();
dbCommand.CommandText = queryString;
dbCommand.Connection = dbConnection;
System.Data.IDataParameter dbParam_projId = new System.Data.SqlClient.SqlParameter();
dbParam_projId.ParameterName = "@projId";
dbParam_projId.Value = projId;
dbParam_projId.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_projId);
System.Data.IDataParameter dbParam_toDevice = new System.Data.SqlClient.SqlParameter();
dbParam_toDevice.ParameterName = "@toDevice";
dbParam_toDevice.Value = toDevice;
dbParam_toDevice.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_toDevice);
System.Data.IDataParameter dbParam_commandStatus = new System.Data.SqlClient.SqlParameter();
dbParam_commandStatus.ParameterName = "@commandStatus";
dbParam_commandStatus.Value = commandStatus;
dbParam_commandStatus.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_commandStatus);
int rowsAffected = 0;
dbConnection.Open();
try {
rowsAffected = dbCommand.ExecuteNonQuery();
}
finally {
dbConnection.Close();
}
return rowsAffected;
}
[WebMethod]
public int DeleteCommand(string projId, string toDevice) {
string connectionString = "server=\'YOUR_SERVER_ADDRESS\'; user id=\'USER_ID\'; password=\'PASSWORD" +
"\'; database=\'DATABASE_NAME\'";
System.Data.IDbConnection dbConnection = new System.Data.SqlClient.SqlConnection(connectionString);
string queryString = "DELETE FROM [Commands] WHERE (([Commands].[projId] = @projId) AND ([Commands].[to" +
"Device] = @toDevice))";
System.Data.IDbCommand dbCommand = new System.Data.SqlClient.SqlCommand();
dbCommand.CommandText = queryString;
dbCommand.Connection = dbConnection;
System.Data.IDataParameter dbParam_projId = new System.Data.SqlClient.SqlParameter();
dbParam_projId.ParameterName = "@projId";
dbParam_projId.Value = projId;
dbParam_projId.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_projId);
System.Data.IDataParameter dbParam_toDevice = new System.Data.SqlClient.SqlParameter();
dbParam_toDevice.ParameterName = "@toDevice";
dbParam_toDevice.Value = toDevice;
dbParam_toDevice.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_toDevice);
int rowsAffected = 0;
dbConnection.Open();
try {
rowsAffected = dbCommand.ExecuteNonQuery();
}
finally {
dbConnection.Close();
}
return rowsAffected;
}
[WebMethod]
public int DeleteNotification(string projId, string fromDevice) {
string connectionString = "server=\'YOUR_SERVER_ADDRESS\'; user id=\'USER_ID\'; password=\'PASSWORD" +
"\'; database=\'DATABASE_NAME\'";
System.Data.IDbConnection dbConnection = new System.Data.SqlClient.SqlConnection(connectionString);
string queryString = "DELETE FROM [Notifications] WHERE (([Notifications].[projId] = @projId) AND ([Not" +
"ifications].[fromDevice] = @fromDevice))";
System.Data.IDbCommand dbCommand = new System.Data.SqlClient.SqlCommand();
dbCommand.CommandText = queryString;
dbCommand.Connection = dbConnection;
System.Data.IDataParameter dbParam_projId = new System.Data.SqlClient.SqlParameter();
dbParam_projId.ParameterName = "@projId";
dbParam_projId.Value = projId;
dbParam_projId.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_projId);
System.Data.IDataParameter dbParam_fromDevice = new System.Data.SqlClient.SqlParameter();
dbParam_fromDevice.ParameterName = "@fromDevice";
dbParam_fromDevice.Value = fromDevice;
dbParam_fromDevice.DbType = System.Data.DbType.String;
dbCommand.Parameters.Add(dbParam_fromDevice);
int rowsAffected = 0;
dbConnection.Open();
try {
rowsAffected = dbCommand.ExecuteNonQuery();
}
finally {
dbConnection.Close();
}
return rowsAffected;
}
}
When you test your WebService in browser by running it in a web server it should look like following figure. The asmx file is also distributed with code zip file and is by name IoTService_CP_Distro.asmx. All you have to do is create database table and replace YOUR_SERVER_ADDRESS,USERNAME, PASSWORD, DATABASE_NAME field with your server id, username ,password and database name credentials respectively.
Figure 3.2: Running WebService Locally
So remote client will first insert a command to be executed using InsertCommand()
with status as 'NEW' web method, C# Client while polling the commands through CommandToExecute()
method when sees a command for itself with NEW tag, would fetch it and execute. After successfull execution it will change the status to 'EXECUTED' using UpdateCommandStatus()
. Once remote client finds that it's command is executed, removes the command from the stack by calling DeleteCommand()
.
The C# Client keeps analyzing the data coming from Arduino device over Serial port and when there is a trigger ( say for instance value in analog port increased over 800) then generates a Notification using InsertNotification()
. Remote client while polling particular device's data when sees a fresh notification ( depending upon time stamp) generates an alarm. Notifications are not deleted as they might be seeked by many clients at any given instance of time.
Also observe the following code block in CommandToExecute()
method
string s="";
while(dataReader.Read())
{
s=dataReader[0]+"#"+dataReader[1]+"#"+dataReader[2];
}
return s;
You can see that while returning the data from a WebMethod, we are marshalling all the fields into a single string separated by #. This format of data is easy to decode at any web service consumer client.
I have released the web service in the public domain which you can utilize for your testing:
I am using visual studio 2010. In VS 2010, you can discover and add the service reference of the web service from
Project->Add Service Reference as shown in figure 3.3
Figure 3.3 Adding Web Service Reference in C# Client
Now we shall see the service in action. As you might be aware that WebService once deployed in the server can not be tested from web page, but the same web service can be tested locally. As the service wherever it is run, will write data in a single database, we can always test the result with locally running web service.
So our C# Client will write and access data through remote service deployed in the server and we will test the protocol by accessing the database data from locally deployed service.
Remember that notification needs to be sent from a device with a valid ID. Therefore we will use the ip address of the system with which the Arduino Board is connected. In order to get the IP address of the computer we will use following simple method called LocalIpAddress()
which returns the IP address of your computer through DNS request.
public string LocalIPAddress()
{
IPHostEntry host;
string localIP = "";
host = Dns.GetHostEntry(Dns.GetHostName());
foreach (IPAddress ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
localIP = ip.ToString();
break;
}
}
return localIP;
}
We will keep the value of the IP address at form load in a variable called ipAddress such that we can use this value from rest of the project.
Now when you are handling Sensor events you need to be very careful. For example if you want to generate a notification when Your temperature Sensor value exceeds 30', then as your C# client is reading data from Arduino at every second would find a trigger in every second if the temperature is over 30'c. But you just can not keep generating remote notification at every second. In that way, you will flood the server. So we will use a timer called timNotDelay. Once we generate a notification we will start the timer and would not generate any notification till the timer is elapsed.
As Display()
method is handling the data coming from SerialPort we shall write our notification generation logic here. We insert the Notification by invoking InsertNotification()
from an object of IoTServiceSoapClient
class which is a proxy ( stub) class of our remote IoTService
class. ipAddress
is the local variable that stores system's IP address and projID
is a global string initialized with "ArduinoIoT", If you want to test only your logic, you are free to use a different project name.
void Display(string s)
{
listBox1.Items.Add(s);
this.Text = s;
try
{
int n = int.Parse(s);
if (n > 800)
{
ArduinoSerial.ServiceReference1.IoTServiceSoapClient iotClient = new ArduinoSerial.ServiceReference1.IoTServiceSoapClient();
if (!timNotDelay.Enabled)
{
iotClient.InsertNotification(projId, ipAddress, "VALUE EXCEEDS 800", DateTime.Now);
timNotDelay.Enabled = true;
}
}
}
catch
{
}
}
Here is the Screenshot of the output. I touched all the analog pins in Arduino board. So the analog value was obviously over 800. Hence it generated the notifiication. I could read the notification by locally running service as both remote and local service are using same database.
Figure 3.4 Screenshot of Generating Notification
Here is a snapshot of database values for table Notifications
Figure 3.5: Notification Table Entries
You can see that the entries are atleast 20 seconds apart which we wanted. However, there would be still several entries. Designer might forget to delete the entries. In order to keep poor man's database in order I would therefore make a call to DeleteNotifications() method from InsertNotification. I could use Update method too, but in applications where I might need to actually log different data for making a chart, such technique would fail.
[WebMethod]
public int InsertNotification(string projId, string fromDevice, string notificationMessage, System.DateTime whatTime)
{
DeleteNotification( projId, fromDevice);
.... Rest of Code
}
Let's consider that user can remotely generate 'LED ON' and 'LED OFF' command respectively to direct the device to turn on or off the LED of Pin 13 of Arduino. Now the C# Client should poll after every specific period ( say 10 s) by calling CommandToExecute()
web method. The method returns a string that contains the command, the status and date time as a single string separated by '#'. The polling part should separate the command and status and check if the status is "OK" or not, if not ok, executes the command and upon successful execution, updates the command status to OK such that the same command is not repeated again.
Once it obtains a command, it check if it is LED ON and LED OFF or not and then sends appropriate data to serial port. Arduino expects 1 for turning on the LED and 2 for Turning it off. Therefore C# Client must send 1 and 2 respectively depending upon the command.
The best thing about this technique is that you can design your own command set, you can incorporate atleast 10 commands (0-9) without any change in C# Client code. True, when you design your own command set, you have to update the Arduino Sketch accordingly.
The remote client must generate the command with a status other than OK, (say EXECUTE) by calling InsertCommand()
method. After generating the command the client must poll to check the status. When Status becomes OK, it must notify the user that the command he issued is successfully executed.
Here is the timer tick event handler for Polling command.
private void timPollCommands_Tick(object sender, EventArgs e)
{
timPollCommands.Enabled = false;
string []result=iotClient.CommandToExecute(projId, ipAddress).Split(new char[]{'#'});
string command = result[0];
string status = result[1];
if (command.Equals("LED ON")&& !status.Equals("OK"))
{
try
{
serialPort1.Write(new byte[] { 1 }, 0, 1);
iotClient.UpdateCommandStatus(projId, ipAddress, "OK");
}
catch
{
iotClient.DeleteCommand(projId, ipAddress);
}
}
if (command.Equals("LED OFF") && !status.Equals("OK"))
{
try
{
serialPort1.Write(new byte[] { 2 }, 0, 1);
iotClient.UpdateCommandStatus(projId, ipAddress, "OK");
}
catch
{
iotClient.UpdateCommandStatus(projId, ipAddress, "FAILED");
}
}
timPollCommands.Enabled = true;
}
Testing with local service is all fine. But what about testing your device over real internet? As WebService can not be tested without a client while running in server, we need a Web Client.
In this section, we shall develop a ASP.Net client to test our Service.
The ASP.Net Client has two text boxes for entering Ip address of your IoT device ( nothing but the ip address of the system it is connected to) and the command. We use two buttons One for generating command and the other for Querying notification. TestClient.aspx communicates with the webService using Proxy.
Rather than filling this article page with entire HTML code, I have provided the Aspx file with the code folder along with the proxy for grasshoppernetwork.com/IoTService.asmx. You can test them locally or can modify it. The proxy is located inside the bin folder.
The two button click events are as shown below.
void Button1_Click(object sender, EventArgs e)
{
iics.IoTService c=new iics.IoTService();
c.InsertCommand("ArduinoIot",txtIp.Text,txtCommand.Text,"WebClient",DateTime.Now,"EXECUTE");
Label4.Text="Command Sent";
}
void Button2_Click(object sender, EventArgs e)
{
iics.IoTService c=new iics.IoTService();
string s=c.FetchNotification("ArduinoIot",txtIp.Text);
Label4.Text=s;
}
you can test the client at:
Figure 4: Web Remote Client For Testing IoT Service
You are free to use JavaScript to implement a polling at the client side to check the status of the command. You may also develop a JavaScipt based technique to check the Notification status in Background generate an event to display notification in web page.
We have seen how Aspx client can communicate with WebService using Proxy. As the Web Page is deployed in server, it can be used directly used from any device. However I would also add an Android Native app to show that our protocol is suited for standard Mobile Apps too.
As a matter of fact I request you to have a look at .Net WebService Integration With Android Section of my Android Article on Handling Data.
This would give you a good idea about how to go about with an Android App that can communicate with WebServices.
Now for the layout design, we will modify the same layout that we used for the aforementioned article. But we change the EditText and Button Ids and labels to suit our current work.
I have kept the Android App little simple with two event driven synchronous call to InsertCommand()
and FetchCommand()
methods. I have not used any polling or background processes just to keep the stuff simple. You can take the app as starting point and develop more complicated workflow.
Here is our layout design called activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.integratedideas.androidiotclient.MainActivity$PlaceholderFragment"
android:fillViewport="true">
<TextView
android:id="@+id/tvResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="56dp"
android:text="@string/hello_world" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/edIp"
android:layout_centerHorizontal="true"
android:layout_marginBottom="56dp"
android:text="IP Of IoT Node"
android:textAppearance="?android:attr/textAppearanceMedium" />
<Button
android:id="@+id/btnCheckNotification"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/btnSendCommand"
android:layout_alignBottom="@+id/btnSendCommand"
android:layout_toRightOf="@+id/btnSendCommand"
android:text="Check Message" />
<EditText
android:id="@+id/edCommand"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/btnCheckNotification"
android:layout_centerVertical="true"
android:ems="1"
android:inputType="text|textUri" >
<requestFocus />
</EditText>
<Button
android:id="@+id/btnSendCommand"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/tvResult"
android:layout_alignLeft="@+id/edIp"
android:layout_marginBottom="26dp"
android:text="Send Command" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/edCommand"
android:layout_alignRight="@+id/tvResult"
android:layout_marginBottom="14dp"
android:text="Command"
android:textAppearance="?android:attr/textAppearanceMedium" />
<EditText
android:id="@+id/edIp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/textView2"
android:layout_alignParentLeft="true"
android:layout_marginBottom="35dp"
android:ems="1"
android:gravity="top|left"
android:inputType="textMultiLine"
android:lines="1"
android:maxLines="2"
android:minLines="1"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical" />
</RelativeLayout>
While testing this layout I observed that the SoftKeyboard was making the layout fluid, meaning whenever the keyboard was visible, it was shrinking the control, changing the entire layout.
So I had to make little known hack in Manifest file and add
android:windowSoftInputMode="stateVisible|adjustPan"
Section in Activity section. Here is our Manifest file:
="1.0"="utf-8"
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.integratedideas.androidiotclient"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="14" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.integratedideas.androidiotclient.MainActivity"
android:windowSoftInputMode="stateVisible|adjustPan"
android:configChanges="keyboardHidden|orientation"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Let us now write a class called CallSoap to incorporate GetNotification()
and InsertCommand()
methods through which we will invoke FetchNotification()
and InsertCommand()
web methods respectively.
I have kept the methods without Arguments so that they are easier to call if you are calling them from an instance of class implementing Runnable or from BackgroundThread. We would initialize class properties to handle the arguments for the web methods.
public class CallSoap
{
public String projId="ArduinoIoT";
public String iotDevIp="";
public String command="";
public static String SOAP_ACTION = "http://tempuri.org/IoTService";
public static String OPERATION_NAME = "AdminLogin";
public static final String WSDL_TARGET_NAMESPACE = "http://tempuri.org/";
public static final String SOAP_ADDRESS = "http://grasshoppernetwork.com/IoTService.asmx";
public CallSoap()
{
}
public String GetNotification()
{
SOAP_ACTION = "http://tempuri.org/FetchNotification";
OPERATION_NAME = "FetchNotification";
Object response=null;
SoapObject request = new SoapObject(WSDL_TARGET_NAMESPACE,OPERATION_NAME);
PropertyInfo p=new PropertyInfo();
p.setName("projId");
p.setType(String.class);
p.setValue(projId);
request.addProperty(p);
p=new PropertyInfo();
p.setName("fromDevice");
p.setType(String.class);
p.setValue(iotDevIp);
request.addProperty(p);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
HttpTransportSE httpTransport = new HttpTransportSE(SOAP_ADDRESS);
try
{
httpTransport.call(SOAP_ACTION, envelope);
response = envelope.getResponse();
}
catch (Exception exception)
{
response=response+"Here it is"+exception.toString();
}
return response.toString();
}
private static Object getSOAPDateString(java.util.Date itemValue) {
String lFormatTemplate = "yyyy-MM-dd'T'hh:mm:ss";
SimpleDateFormat lDateFormat = new SimpleDateFormat(lFormatTemplate);
String lDate = lDateFormat.format(itemValue);
return lDate;
}
public String InsertCommand()
{
SOAP_ACTION = "http://tempuri.org/InsertCommand";
OPERATION_NAME = "InsertCommand";
Object response=null;
SoapObject request = new SoapObject(WSDL_TARGET_NAMESPACE,OPERATION_NAME);
PropertyInfo p=new PropertyInfo();
p.setName("projId");
p.setType(String.class);
p.setValue(projId);
request.addProperty(p);
p=new PropertyInfo();
p.setName("toDevice");
p.setType(String.class);
p.setValue(iotDevIp);
request.addProperty(p);
p=new PropertyInfo();
p.setName("commandText");
p.setType(String.class);
p.setValue(command);
request.addProperty(p);
p=new PropertyInfo();
p.setName("generatedBy");
p.setType(String.class);
p.setValue("Android Client");
request.addProperty(p);
p=new PropertyInfo();
p.setName("whatTime");
p.setType(java.util.Date.class);
java.util.Date utilDate = new java.util.Date();
Object ob=getSOAPDateString(utilDate);
p.setValue(ob.toString());
request.addProperty(p);
p=new PropertyInfo();
p.setName("commandStatus");
p.setType(String.class);
p.setValue("Execute");
request.addProperty(p);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
HttpTransportSE httpTransport = new HttpTransportSE(SOAP_ADDRESS);
try
{
httpTransport.call(SOAP_ACTION, envelope);
response = envelope.getResponse();
}
catch (Exception exception)
{
response=response+"Here it is"+exception.toString();
}
return response.toString();
}
}
There is a very interesting part about this class. When you are calling remote Asp.Net web service from Android code, it can handle any property nicely. But there is quite a big problem casting java specific date time to C# Specific date time. The problem is that the data is passed over XML.
XML Specification says that it expects date time in yyyy-MM-dd'T'HH:mm:ss format. So we need to first format java.util.Date object using SimpleDateFormat
object. So we have written a small utility method called getSOAPDateString
which does exactly that.
Finally we can call the methods from Button click event handler.
public void onClick(View arg0)
{
Button b=(Button)arg0;
if(b.getText().toString().trim().equals("Send Command"))
{
CallSoap cs=new CallSoap();
cs.projId="ArduinoIoT";
cs.iotDevIp=edIp.getText().toString().trim();
cs.command=edCommand.getText().toString().trim();
webResult=cs.InsertCommand();
tvResult.setText(webResult);
}
if(b.getText().toString().trim().equals("Check Message"))
{
CallSoap cs=new CallSoap();
cs.projId="ArduinoIoT";
cs.iotDevIp=edIp.getText().toString().trim();
webResult=cs.GetNotification();
tvResult.setText(webResult);
}
}
But Android does not allow you to call a network specific operation from Main thread. So I added the following lines at the end of createView method to permit accessing network related operation from main thread.
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
Note that it is never a good programming practice to call Network thread from main thread using above hack. You should avoid it by all means in your real app.
I have used this in order to keep the logical part simple.
That's it. We are now ready to test our client.
Figure 6: ScreenShot of Android Screen
Figure 7: Final Output of the Work
Above figure shows the overall result of the where where you can see that we are able to turn on and off the LED associated with Arduino Pin 13 with the help of both our Andoird Mobile as well as from browser. Though there are several Cloud Service providers available now which eliminates the need of Writing your own Service for IoT. But there are specific cases where you want to design your own solution because your logic might differ. You might want to be able to connect your IoT device with a larger and existing business logic of your enterprise. In such cases the described architecture of Custom Services can come handy. For example in near future railways decides to do away with paper ticket and introduces IoT enabled RFID based smart tickets. Heving your own service enables the reservation system to be directly integrated with RFID data reader and writer through your services. Custom services also helps implementing complicated processing to be implemented at the service level itself. For instance you might write a service that returns the standard deviation of stored data.
In this tutorial I have given an overview of entire protocol to be followed for connecting an embedded device to internet using C# Serial Communication and with the help of Asp.Net web service. I have also shown how to design an Android client for the service. This is to make one understand how services can abstract the client technology and embedded devices can be communicated with any platform. We also deployed the service in real time. I hope you enjoyed the tutorial as much as I enjoyed to code and write.