Galileo Smart Fan Example

We would use as a starting point the Smart Fan sample https://ms-iot.github.io/content/SmartFan.htm .
The target is to modify the application to:

  • Read the temperature
  • Send it to a server
  • Execute the server commands to start or stop the fan

We would remove the light sensor part.
How do we communicate with the server, and what server it is?
We open a web page (web server then) and through GET parameters we send the data

https://WebSite/SiteName/Temperatures.aspx?id=2&temp=30&fan=false

The parameters are:

  • id – the id of the device
  • temp – the temperature
  • fan – true if the fan is on otherwise false

The page responds with an invalid html page with 3 commands:

  • listen – do nothing
  • start – start the fan
  • stop – stop the fan

The page is invalid because contains no html markings, just the command text to reduce the bandwidth used. Somethimes you pay for data transfer, and the “dead”, no util information, as the markups are in our case, add up.

2. Developing the Galileo application

The hardware pin wise need to be the same. My colleagues (I do software Big Grin | :-D ) did a simplified version on a Arduino Proto Shield.

Electonics

Code wise some constants were changed:

 

// Convert the voltage
double voltage_to_celsius(double voltage) {
 //+1 for our schema
 return 100 * voltage + 1;
}
void toggle_motor(bool motor_is_on) {
 if (motor_is_on) {
  // Turn off motor
  motor_is_on = false;
  analogWrite(MOTOR_PIN, 0);
 }
 else {
  // Turn on motor
  motor_is_on = true;
  //for our schema
  analogWrite(MOTOR_PIN, 250);
  //The old code
  //delay(1);
  //analogWrite(MOTOR_PIN, 50);
 }
}

 

The code to open a web site is a pretty trivial Windows C++ open a page

int SendTempAndGetCommand(int deviceId, double temperature, bool fanRunning)
{
	char hostname[] = "169.254.178.34";

	WSADATA                WsaData;
	size_t                 socketResult;
	WSAStartup(0x0101, &WsaData);
	socketResult = socket(AF_INET, SOCK_STREAM, 0);
	if (socketResult == -1)
	{
		return -100;
	}

	struct addrinfo addrinfoHints;
	struct addrinfo *pAddrinfo = NULL;
	struct addrinfo *pAddrinfoResult = NULL;
	int result = -7;

	// Setup the addrinfoHints address info structure
	// which is passed to the getaddrinfo() function
	ZeroMemory(&addrinfoHints, sizeof(addrinfoHints));
	addrinfoHints.ai_family = AF_INET;

	DWORD  dwReturnValue = getaddrinfo(hostname, PORT, &addrinfoHints, &pAddrinfoResult);
	if (dwReturnValue != 0)
	{
		return -101;
	}

	// loop through all the results and connect to the first we can
	for (pAddrinfo = pAddrinfoResult; pAddrinfo != NULL; pAddrinfo = pAddrinfo->ai_next)
	{
		if ((socketResult = socket(pAddrinfo->ai_family, pAddrinfo->ai_socktype,
			pAddrinfo->ai_protocol)) == -1)
		{
			perror("client: socket");
			continue;
		}

		if (connect(socketResult, pAddrinfo->ai_addr, pAddrinfo->ai_addrlen) == -1) {
			perror("client: connect");
			DWORD lasterr = WSAGetLastError();
			continue;
		}

		break;
	}

	if (pAddrinfo == NULL)
	{
		return -102;
	}

	freeaddrinfo(pAddrinfoResult);

	char msg[500];
	sprintf_s(msg, "GET https://WebSite/SiteName/Temperatures.aspx?id=%d&temp=%f&fan=false rnrn ", deviceId, temperature);
	if (fanRunning)
		sprintf_s(msg, "GET https://WebSite/SiteName/Temperatures.aspx?id=%d&temp=%f&fan=true rnrn ", deviceId, temperature);

	send(socketResult, msg, (int)strlen(msg), 0);
	char buffer[10000];
	recv(socketResult, buffer, 10000, 0);

	char listen[] = "Listen";
	char start[] = "Start";
	char stop[] = "Stop";

	if (strstr(buffer, listen) != NULL)
	{
		result = -1;
	}
	else
		if (strstr(buffer, start) != NULL)
		{
			result = 1;
		}
		else
			if (strstr(buffer, stop) != NULL)
			{
				result = 0;
			}

	closesocket(socketResult);

	WSACleanup();

	return result;
}

And the motor is commanded using the result of this method

	int result = SendTempAndGetCommand(3, temp_in_c, motor_is_on);

	if (result==1) {
		if (!motor_is_on) //super sure
		{
			toggle_motor(motor_is_on);
			motor_is_on = true;
			Log(L"Motor Onrn");
		}
	}
	else if (result==0) {
		if (motor_is_on) //super sure
		{
			toggle_motor(motor_is_on);
			motor_is_on = false;
			Log(L"Motor Offrn");
		}
	}

The code is attached to the article, bear in mind that you need to adapt it to your specific hardware.

3. Developing the server applications

The server app is an ASP.Net web site but could be anything (this is why I do not put the full code for it in the article Smile | :) ).

The aspx is “empty”:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Temperatures.aspx.cs" Inherits="FanTemperature.Temperatures" %>

And on .cs on the Page_Load is all the code.
This is how the paramethers are read.

                int deviceId = 0;
                double curentTemperature = 0;
                bool fanRunning = false;
                int commandFanToRun = -1;

                if (this.Request["ID"] != null)
                {
                    deviceId = Convert.ToInt32(this.Request["ID"], CultureInfo.InvariantCulture);

                    if (this.Request["temp"] != null)
                    {
                        curentTemperature = Convert.ToDouble(this.Request["temp"], CultureInfo.InvariantCulture);
                    }
                    if (this.Request["fan"] != null)
                    {
                        fanRunning = Convert.ToBoolean(this.Request["fan"], CultureInfo.InvariantCulture);
                    }
                }

 

And to suppress the headers the adding of the markups, this is the code used, the result string is the command.

            Response.Clear();
            Response.ClearContent();
            Response.ClearHeaders();
            Response.Write(result);
            Response.ClearHeaders();

About the rest of the code could be a simple if,  switch or a decision workflow etc. Also the data could be written into the database, and get a report from it using Reporting Services for example.

3. Run all Smile | :)

Depending on the sensor temperature (by touching the sensor is enough to change it) the fan will start

Start

or stop

Stop

The temperature report is set to auto refresh each second.

Report

4. Security consideration

I would admit that we use this communication through GET parameters in our solutions for years, our company was in IoT before the term was coined. But because our clients are in manufacturing the networks we use with our apps and sensors are private.
My point, the GET method is not secure, bear this in mind, is a solution in the cases when data is not sensitive or the network is private. As a plus the traffic is very easy to debug.
So if security is a concern POST, https, Azure and so is something that you should think of.
And there are some interesting Azure offerings that could be used in this IoT scenarios (for sure you can connect from Windows Galileo since the C++ method surface is almost all of a “big” Windows)

And I’m naming just a few Azure offerings that could fit in an IoT solution.

5. Conclusions

I hope that I was able to fill the gaps of the Windows Developer Program for IoT (https://dev.windows.com/en-us/featured/windows-developer-program-for-iot) documentation and tutorials and show you that running Windows on Galileo is an option.

Also there (https://ms-iot.github.io/content/AdvancedUsage.htm) are answers to some advanced questions, how to do not use the USB network card, how to ensure that my application runs on the device without using Visual Studio debug, how to kill a Galileo task and so on.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.