© Charles Bell 2016

Charles Bell, Windows 10 for the Internet of Things, 10.1007/978-1-4842-2108-2_6

6. Windows 10 IoT Development with C#

Charles Bell

(1)Warsaw, Virginia, USA

Now that you have a basic understanding of how to use Visual Studio 2015, you can learn more about some of the languages you may encounter when developing your IoT solutions. One of those languages is C# (pronounced “see sharp”1)—a very robust and powerful object-oriented language that you can use to write managed Windows .NET and UWP applications. Mastering C# is not a trivial task, but it is not quite as challenging as other programming languages.

If you are used to using C++ or Java, you may find C# to be familiar, but a bit more verbose. That is, it may seem like you’re typing a lot more or adding more lines of code. Both are true to some extent. The libraries we will use have more verbose naming conventions (names or identifiers are longer) and we will use a few more lines of code in the process. However, as you will see, C# source code reads easier than some other languages making it easier to understand and modify in the future. It also helps when debugging your code. You don’t have to guess what a library class and method may do because the name is more descriptive (in general).

Some find learning C# easier than other programming languages because, if you are familiar with C++ or especially Java, some of what you will learn is similar. Essentially, C# is an improvement on languages like C++ and Java. In fact, you may only need a little knowledge of the fundamentals of the language and how to use it in Visual Studio to become proficient in creating Windows 10 IoT Core applications.

This chapter presents a crash course on the basics of C# programming in Visual Studio including an explanation about some of the most commonly used language features. As such, this chapter provides you with the skills you need to understand the growing number of IoT project examples available on the Internet. The chapter concludes with a walk-through of a C# example project that shows you how to interact with hardware. Specifically, you will implement the LED project you saw in Chapter 3. Only this time, you’ll be writing it as a C# Windows 10 IoT Core application. So let’s get started!

Tip

If you are not interested in using C# in your IoT solutions, or you already know the basics of C# programming, feel free to skim through this chapter. I recommend working through the example project at the end of the chapter, especially if you’ve not written IoT applications.

Getting Started

The C# language has been around since the first introduction of the .NET Framework (pronounced “dot net”). In fact, C# was specifically designed to be the object-oriented programming language of choice for writing .NET applications. C# was released in 2000 with the release of the .NET Framework. The latest version of C# is version 6.0 and is sometimes called Visual C# (but Microsoft seems to prefer C#).

You may be thinking that C# and .NET may restrict the types of applications you can write, but that also is not true. You can use C# to write a host of applications from Windows 10 IoT Core to desktop to web applications and beyond. As you will see, you can also write C# console applications like you did in Chapter 3 with C++.

C# was heavily influenced by Java and C++. Indeed, if you have programmed in Java or C++, C# will seem familiar to you. What sets C# apart is it is a purely object-oriented language. That is, you must write all of your programs as an object using a class. In fact, with an easy to understand class syntax,2 which makes developing your object-oriented programming easier. This gives C# a powerful advantage over C++ and other languages that have more complex syntax where objects are largely optional.

While some may be tempted to think C# is another flavor of C++, there are some serious differences. The most important is the use of an execution manager to keep the C# applications in a protected area (called a managed application). This protected area ensures that the C# code has access only to portions that the execution manager permits—those defined in the code. One of the great side effects of this arrangement is automatic garbage collection—freeing of allocated memory that is no longer used (or in scope). While you can choose to make a C# application an unmanaged application, the practice is discouraged (and largely unnecessary for the vast majority of use cases).

Tip

For more information about the differences between C++ and C#, see www.differencebetween.info/difference-between-cplusplus-and-csharp .

Another very important difference between C# and C++ is how they are made into an executable (compiled). C++ is compiled and linked to form an executable program with object code native to the platform, but C# is compiled in two steps: the first is a platform-independent intermediate object code called the common intermediate language (CIL) and the second when that is converted to native object code by the just-in-time compiler (JIT) . The JIT compiler operates in the background and as the name implies prepares the code for execution when needed. In fact, you cannot tell the JIT compiler is running.

When a CIL is built, the compiler includes all of the references needed to execute the application. This may include framework files, dynamic libraries, and metadata needed by the system. This is called an assembly. You need not think of this other than an executable but technically it is a small repository (hence, assembly).

This mechanism allows C# applications to execute within a cordoned off area of memory (called a managed application) that the .NET common language runtime (CLR) can monitor and protect other applications from harm. A side effect allows C# applications to have garbage collection (by the CLR) freeing memory automatically based on scope and use. Figure 6-1 shows a pictorial example of the way C# applications are compiled and executed in phases.

A416714_1_En_6_Fig1_HTML.jpg
Figure 6-1. How C# applications are compiled and executed

Here you see the two phases of compilation. Phase one occurs when you compile the application in Visual Studio. Phase two occurs when you execute the application placing the executable in the CLR for execution. Notice that I depict other applications running in the same CLR, each protected from the other by the CLR’s managed features.

Note

While C# is technically compiled in two phases, the rest of the chapter focuses on compilation as executed from Visual Studio.

Should you require more in-depth knowledge of C#, there are a number of excellent books on the topic. Here is a list of a few of my favorites.

  • Beginning C# 6.0 Programming with Visual Studio 2015 by Benjamin Perkins and Jacob Vibe Hammer (Wrox, 2015)

  • The C# Player's Guide by R. B. Whitaker (Starbound Software, 2015)

  • Microsoft Visual C# Step by Step (Developer Reference) by John Sharp (Microsoft Press, 2015)

Another excellent resource is Microsoft’s documentation on MSDN. The following are some excellent resources for learning C#.

Now that you know some of the origins and unique features of C# and the .NET CLR, let’s learn about the syntax and basic language features for creating applications.

C# Crash Course

Now let’s learn some of the basic concepts of C# programming. Let’s begin with the building blocks of the language, such as classes, methods, variables, and basic control structures, and then move into the more complex concepts of data structures and libraries.

While the material may seem to come at you in a rush (hence the crash part), this crash course on C# covers only the most fundamental knowledge of the language and how to use it in Visual Studio. It is intended to get you started writing C# Windows 10 IoT Core applications. If you find you want to write more complex applications than the examples in this book, I encourage you to acquire one or more of the resources listed earlier to learn more about the intriguing power of C# programming.

C# Fundamentals

There are a number of basic concepts about the C# programming language that you need to know in order to get started. In this section, I describe some of the fundamental concepts used in C#, including how the code is organized, how libraries are used, namespaces, and how to document your code.

C# is a case sensitive language so you must take care when typing the names of methods or classes in libraries. Fortunately, Visual Studio’s IntelliSense feature recognizes mistyped case letters, which allows you to choose the correct spelling from a drop-down list as you write your code. Once you get used to this feature, it is very hard to live without it.

Namespaces

The first thing you may notice is that C# is an object-oriented language and that every program you write is written as a class. Applications are implemented with a namespace that has the same name. A namespace is a special organizational feature that allows you to group identifiers (names of variables, constants, etc.) under a group that is localized to the namespace. Using the namespace tells the compiler to look in the namespace for any identifier you’ve used in your code that is not found.

You can also create namespaces yourself, as you see in the upcoming example source code. Namespaces may contain any number of classes and may extend to other source files. That is, you can define a namespace so that it spans several source files.

Tip

Source files in C# have a file extension of .cs.

Classes

The next thing you may notice is a class definition. A class is more than a simple data structure. You use classes to model concepts that include data and operations on the data. A class can contain private and public definitions (called members) and any number of operations (called methods) that operate on the data and give the class meaning.

You can use classes to break your programs down into more manageable chunks. That is, you can place a class you’ve implemented in its own .cs file and refer to it in any of the code provided there aren’t namespace issues and even then you simply use the namespace you want.

Let’s look at a simple class named Vector implemented in C#. This class manages a list of double variables hiding the data from the caller while providing rudimentary operations for using the class. Listing 6-1 shows how such a class could be constructed.

Listing 6-1. Vector Class in C#
class Vector
{
    private double[] elem;
    private int sz;


    public Vector(int s)
    {
        elem = new double[s];
        sz = s;
    }


    public ∼Vector() { /* destructor body */ }

    public int size() { return sz; }

    public double this[int i]
    {
        get { return elem[i]; }
        set { elem[i] = value; }
    }
}

This is a basic class that is declared with the keyword class followed by a name. The name can be any identifier you want to use but convention is to use an initial capital letter for the name. All classes are defined with a set of curly braces that define the body or structure of the class.

By convention, you list the member variables (also called attributes) indented from the outer curly braces. In this example, you see two member variables that are declared as private. You make them private to hide information from the caller. That is, only member methods inside the class itself can access and modify private member variables. Note that derivatives (classes built from other classes—sometimes called a child class—can also access protected member variables.

Next, you see a special method that has the same name as the class. This is called the constructor. The constructor is a method that is called when the class is instantiated (used). You call the code that defines the attributes and methods a class and when executed, you call the resulting object an instance of the class.

In this case, the constructor takes a single integer parameter that is used to define the size of the private member variable that stores the array of double values. Notice how this code is used to dynamically define that array. More specifically, you use the new command to allocate memory for the array.

Following the constructor is another special method called the destructor. This method is called when the object is destroyed. Thus, you can place any cleanup code that you want to occur when the object is destroyed. For example, you can add code to close files or remove temporary storage. However, since C# runs on .NET with a garbage collector (a special feature that automatically frees allocated memory when no longer in scope), you do not have to worry about freeing (deleting) any memory you’ve allocated.

Next are two public methods, which users (or callers of the instance) can call. The first method, size(), looks as you would expect and in this case simply returns the value of the private member variable sz. The next method is a special form of method called an operator (or get/set) method. Notice that there are two sections or cases: get and set.

The method allows you to use the class instance (object) as if it were an array. The method is best understood by way of an example. The following shows how this operator method is used. Notice the lines in bold.

Vector v = new Vector(10);
for (int i=0; i < v.size(); ++i)
{
    v[i] = (i * 3);
}
Console.Write("Values of v: ");
for (int j=0; j < v.size(); ++j)
{
    Console.Write(v[j]);
    Console.Write(" ");
}
Console.WriteLine();

The code instantiates an instance of the Vector class requesting storage for 10 double values. Next, you iterate over the values in the instance using the operator to set the value of each of the 10 elements. This results in the set portion of the operator method executing. Next, the code iterates over the values again this time requesting (getting) the value for each. This results in the get portion of the operator method executing. Neat, eh?

Note

Some programming language books on C# use the term function and method interchangeably while other books make a distinction between the two.3 However, Microsoft uses the term method.

I built this example as a single code file but had I wanted to use modularization, I would have placed the code for the Vector class in its own source (.cs) file. The name of the source file is not required to be the same as the class it contains. Indeed, a source file may contain multiple classes. Still, you would likely choose a meaningful name. To add a new source file to a Visual Studio C# solution, simply right-click the project name in Solution Explorer, and then choose AddAdd new item and choose C# in the tree view and finally C# Code file. At the bottom of the dialog you can name the file. When ready, click the Add button. You can then create any classes you want (or move classes) in the file. You can also use the same namespace to keep all of your classes in the same namespace. However, if you create a new namespace, you must use the using command to use the new namespace.

As you may have surmised, classes are the building block for object-oriented programming and as you learn more about using classes, you can build complex libraries of your own.

Tip

Visual Studio provides a tool called the Class View window that you can use to explore the libraries and classes used in your application.

Curly Braces

Notice that both methods are implemented with a pair of curly braces {} that define the body of the method. Curly braces in C# are used to define a block of code or simply to express grouping of code. Curly braces are used to define the body of methods, structures, classes, and more. Notice that they are used everywhere, even in the conditional statements (see the if statements).

Tip

Some C# programmers prefer to place the starting curly brace on the same line as the line of code to which it belongs like I did in the example. However, others prefer the open curly brace placed on the next line. You should choose the style you like best.

Comments

One of the most fundamental concepts in any programming language is the ability to annotate your source code with text that not only allows you to make notes among the lines of code but also forms a way to document your source code.4

To add comments to your source code, use two slashes, // (no spaces between the slashes). Place them at the start of the line to create a comment for that line repeating the slashes for each subsequent line. This creates what is known as a block comment, as shown. Notice that I used a comment without any text to create whitespace. This helps with readability and is a common practice for block comments.

//
// Windows 10 for the IoT
//
// Example C# console application rewrite.
//
// Created by Dr. Charles Bell
//

You can also use the double slash to add a comment at the end of a line of code. That is, the compiler ignores whatever is written after the double slash to the end of the line. You see an example of this next. Notice that I used the comment symbol (double slash) to comment out a section of code. This can be really handy when testing and debugging, but generally discouraged for final code. That is, don’t leave any commented out code in your deliverable (completed) source code. If it’s commented out, it’s not needed!

if (size < max_size) {
  size++;  /* increment the size */
} //else {
//  return -1;
//}

Notice that you also see the use of /* */, which is an alternative C-like mechanism for writing comments. Anything that is included between the symbols becomes a comment and can include multiple lines of code. However, convention seems to favor the // symbol but as you can see you can mix and match however you like. I recommend choosing one or the other with consistency over variety.

Writing good comments and indeed documenting your code well is a bit of an art form; one that I encourage you to practice regularly. Since it is an art rather than a science, keep in mind that your comments should be written to teach others what your code does or is intended to do. As such, you should use comments to describe any preconditions (or constraints) of using the code, limitations of use, errors handled, and a description of how the parameters are used and what data is altered or returned from the code (should it be a method or class member).

How C# Programs Are Structured

Now let’s look at how C# programs are structured by examining a slightly different version of the temperature application you saw in Chapter 4. Listing 6-2 shows the code rewritten for C#.

Listing 6-2. Temperature Code Example Rewrite
//
// Windows 10 for the IoT
//
// Example C# console application rewrite
//
// Created by Dr. Charles Bell
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace temperature_csharp
{
    class Program
    {
        static double convert_temp(char scale, double base_temp)
        {
            if ((scale == 'c') || (scale == 'C'))
            {
                return ((9.0/5.0) * base_temp) + 32.0;
            }
            else
            {
                return (5.0 / 9.0) * (base_temp - 32.0);
            }
        }


        static void Main(string[] args)
        {
            double temp_read = 0.0;
            char scale = 'c';


            Console.WriteLine("Welcome to the temperature conversion application.");
            Console.Write("Please choose a starting scale (F) or (C): ");
            scale = Console.ReadKey().KeyChar;
            Console.WriteLine();
            Console.Write("Please enter a temperature: ");
            temp_read = Convert.ToDouble(Console.ReadLine());


            if ((scale == 'c') || (scale == 'C'))
            {
                Console.WriteLine("Converting value from Celsius to Fahrenheit.");
                Console.Write(temp_read);
                Console.Write(" degrees Celsius = ");
                Console.Write(convert_temp(scale, temp_read));
                Console.WriteLine(" degrees Fahrenheit.");
            }
            else if ((scale == 'f') || (scale == 'F'))
            {
                Console.WriteLine("Converting value from Fahrenheit to Celsius.");
                Console.Write(temp_read);
                Console.Write(" degrees Fahrenheit = ");
                Console.Write(convert_temp(scale, temp_read));
                Console.WriteLine(" degrees Celsius.");
            }
            else
            {
                Console.Write("ERROR: I'm sorry, I don't understand '");
                Console.Write(scale);
                Console.WriteLine("'.");
            }
        }
    }
}

In the example, the only methods created are convert_temp() and MainPage() but this is because you are implementing a very simple solution. Had you wanted to model (create a separate class for) temperature, you would still have the one class named Program with the MainPage() method (which is the starting method for the application), but would have added a new class named Temperature, which would contain its own methods for working with temperature. Indeed, this is how one should think when writing C# code—model each distinct concept as a class. Here you see the sample application named temperature_csharp was implemented with a class with the name Program.

Wow, that’s quite a change from the code in the last chapter! While the functionality is exactly the same, the code looks very different from the C++ version. The following describe the C# concepts I have implemented in this example.

The using Keyword

First, you notice a number of lines that begin with using. These are pre-processor directives that tell the compiler you want to “use” a classes or a class hierarchy that exists in a particular namespace. The using directive tells the compiler that you are using the namespace System.

using System;

In the other lines I have included additional namespaces with multiple names separated by a period. This is how you tell the compiler to use a specific namespace located in libraries of classes often form hierarchies that you can chain together. For example, if you wanted to use the namespace inside the Windows Foundations library named Collections, you would refer to it as follows.

using System.Threading.Tasks;

This is a very common occurrence in Windows C# applications. In fact, you will use several namespaces in our example project. The following is an example of using the Tasks namespace located in the Threading sub-class namespace of the System namespace.

The MainPage( ) Method

The method named MainPage(). The MainPage() method is the starting or initial execution for the C# console project. Here you see the name is preceded by the keyword static (which means its value with not change and indeed cannot change during runtime) followed by a type (in this case void). This tells the C# compiler that this method will not return any value (but you can make methods that return a value).

static void Main(string[] args)

Next, you see the name, main, followed by a list of parameters enclosed in parenthesis. For the MainPage() method, the parameters are fixed and are used to store any command line arguments provided by the user. In this case, you have the arguments stored in args, which is an array of strings.

A method in C# is used as an organizational mechanism to group functionality and make your programs easier to maintain (methods with hundreds of lines of code are very difficult to maintain), improves comprehensibility, and localize specialized operations in a single location thereby reducing duplication.

Methods therefore are used in your code to express the concepts of the functionality they provide. Notice how I used the convert_temp() method. Here I declared it as a method that returned a double and takes a character and a double as input. As you can see, the body of the method (defined inside the curly braces) uses the character as the scale in the same was as you do in main and uses the double parameter as the target (or base) temperature to convert. Since I made the parameters generic, I can use only the one variable.

Tip

Method parameters and values passed must match on type and order when called.

Notice also that I placed it in the line of code that prints the value to the screen. This is a very common practice in C# (and other programming languages). That is, you use the method to perform some operation and rather than store the result in a variable, you use it directly in the statements (code) .

Variables and Types

No program would be very interesting if you did not use variables to store values for calculations. As you saw earlier, variables are declared with a type and once defined with a specific type cannot be changed. Since C# is strongly typed, the compiler ensures that anywhere you use the variable that it obeys its type. For example, that the operation on the variable is valid for the type. Thus, every variable must have a type assigned.

There are a number of simple types that the C# language supports. They are the basic building blocks for more complex types. Each type consumes a small segment of memory which defines not only how much space you have to store a value, but also the range of values possible.5

For example, an integer consumes 4 bytes and you can store values in the range –2,147,483,648 to 2,147,483,647. In this case, the integer variable is signed (the highest bit is used to indicate positive or negative values). An unsigned integer can store values in the range 0 to 4,294,967,295.

You can declare a variable by specifying its type first and then an identifier. The following shows a number of variables using a variety of types.

int num_fish = 0;                    // number of fish caught
double max_length = 0.0;             // length of the longest fish in feet
char[] fisherman = new char[25];  // name of the fisherman

Notice also that I have demonstrated how to assign a value to the variable in the declaration. The assignment operator is the equal sign. All assignments must obey the type rules. That is, I cannot assign a floating-point number (e.g., 17.55) to an integer value. Table 6-1 shows a list of the commonly used built-in types you will use in your applications.

Table 6-1. Commonly Used Types in C#

Symbol

Size in bytes

Range

bool

1

false or true

char

1

–128 to 127

string

User-defined

–128 to 127 per character

sbyte

1

–128 to 127

byte

1

0-255

short

2

–32,768 to 32,767

ushort

2

0 to 65,535

int

4

–2,147,483,648 to 2,147,483,647

uint

4

0 to 4,294,967,295

long

4

–2,147,483,648 to 2,147,483,647

ulong

4

0 to 4,294,967,295

float

4

3.4E +/- 38 (7 digits)

decimal

8

(-7.9 x 1028 to 7.9 x 1028) / (100 to 28)

double

8

1.7E +/- 308 (15 digits)

It is always a good practice to initialize your variables when you declare them. It can save you from some nasty surprises if you use the variable before it is given a value (although the compiler will complain about this).

Arithmetic

You can perform a number of mathematical operations in C#, including the usual primitives but also logical operations and operations used to compare values. Rather than discuss these in detail, I provide a quick reference in Table 6-2 that shows the operation and example of how to use the operation.

Table 6-2. Arithmetic, Logical, and Comparison Operators in C#

Type

Operator

Description

Example

Arithmetic

+

Addition

int_var + 1

-

Subtraction

int_var - 1

*

Multiplication

int_var * 2

/

Division

int_var / 3

%

Modulus

int_var % 4

-

Unary subtraction

-int_var

+

Unary addition

+int_var

Logical

&

Bitwise and

var1&var2

|

Bitwise or

var1|var2

^

Bitwise exclusive

var1^var2

Bitwise compliment

∼var1

&&

Logical and

var1&&var2

||

Logical or

var1||var2

Comparison

==

Equal

expr1==expr2

!=

Not equal

expr1!=expr2

<

Less than

expr1<expr2

>

Greater than

expr1>expr2

<=

Less than or equal

expr1<=expr2

>=

Greater than or equal

expr1>=expr2

Bitwise operations produce a result on the values performed on each bit. Logical operators (and, or) produce a value that is either true or false and are often used with expressions or conditions.

Now that you understand variables and types, the operations permitted on them, and expressions, let’s look at how you can use them in flow control statements.

Flow Control Statements

Flow control statements change the execution of the program. They can be conditionals that use expressions that restrict execution to only those cases where the expression evaluates true (or negated), special constructs that allow you to repeat a block of code (loops), and the use of methods to switch context to perform some special operations. You’ve already seen how methods work so let’s look at conditional and loop statements.

Conditionals

Conditional statements allow you to direct execution of your programs to sections (blocks) of code based on the evaluation of one or more expressions. There are two types of conditional statements in C#—the if statement and the switch statement.

You have seen the if statement in action in our example code. In the example, you can have one or more (optional) else phrases that you execute once the expression for the if conditions evaluate to false. You can chain if/else statements to encompass multiple conditions where the code executed depends on the evaluation of several conditions. The following shows the general structure of the if statement.

if (expr1) {
  // execute only if expr1 is true
} else if ((expr2) || (expr3)) {
  // execute only if expr1 is false *and* either expr2 or expr3 is true
} else {
  // execute if both sets of if conditions evaluate to false
}

While you can chain the statement as much as you want, use some care here because the more else/if sections you have, the harder it becomes to understand, maintain, and avoid logic errors in your expressions.

If you have a situation where you want to execute code based on one of several values for a variable or expression that returns a value (such as a method or calculation), you can use the switch statement. The following shows the structure of the switch statement.

switch (eval) {
  case <value1> :  
     // do this if eval == value1
     break;
  case <value2> :  
     // do this if eval == value2
     break;
  default :  
     // do this if eval != any case value
     break;  // Not needed, but good form
 }

The case values must match the type of the thing you are evaluating. That is, case values must be same type as eval. Notice the break statement. This is used to halt evaluation of the code once the case value is found. Otherwise, each successive case value will be compared. Finally, there is a default section for code you want to execute should eval fail to match any of the values.

Tip

Code style varies greatly in how to space/separate these statements. For example, some indent the case statements, some do not.

Loops

Loops are used to control the repetitive execution of a block of code. There are three forms of loops that have slightly different behavior. All loops use conditional statements to determine whether to repeat execution or not. That is, they repeat as long as the condition is true. The three types of loops are while, do, and for. I explain each with an example.

The while loop has its condition at the “top” or start of the block of code. Thus, while loops only execute the body if and only if the condition evaluates to true on the first pass. The following illustrates the syntax for a while loop. This form of loop is best used when you need to execute code only if some expression(s) evaluate to true. For example, iterating through a collection of things those number of elements is unknown (loop until you run out of things in the collection).

while (expression) {
   // do something here
 }

The do loop places the condition at the “bottom” of the statement which permits the body of the loop to execute at least once. The following illustrates the do loop. This form of loop is handy for cases where you want to execute code that, depending on the results of that execution, may require repetition. For example, repeatedly asking the user for input that matches one or more known values repeating the question if the answer doesn’t match.

do {
  // do something here – always done once
} while (expression);

for loops are sometimes called counting loopsbecause of their unique form. for loops allow you to define a counting variable, a condition to evaluate, and an operation on the counting variable. More specifically, for loops allow you to define stepping code for a precise number of operations. The following illustrates the structure of the for loop. This form of loop is best used for a number of iterations for a known number (either at run time or as a constant) and commonly used to step through memory, count, and so forth.

for (<init> ; <expression> ; <increment>) {
// do someting
}

The <init> section or counting variable declaration is executed once and only once. The <expression> is evaluated on every pass. The <increment> code is executed every pass except the last. The following is an example for loop.

for (int i; i < 10; i++) {
   // do something here
}

Now let’s look at some commonly used data structures.

Basic Data Structures

What you have learned so far about C# will allow you to create applications that do simple to moderately complex operations. However, when you start needing to operate on data—either from the user or from sensors and similar sources—you need a way to organize and store data and operations on the data in memory. The following introduces three data structures in order of complexity: arrays, structures, and classes.

Arrays allocate a contiguous area of memory for multiple storage of a type. That is, you can store several integers, characters, and so forth, set aside in memory. Arrays also provide an integer index that you can use to quickly access a specific element. The following illustrates how to create an array of integers and iterate through them with a for loop. Array indexes start at 0.

Int[] num_array = {0,1,2,3,4,5,6,7,8,9};  // an array of 10 integers
for (int i=0; i < 10; ++i) {
  Console.Write(num_array[i]);
  Console.Write(" ");
}
Console.Writeline();

You can also define multiple dimensional arrays (arrays of arrays). Arrays can be used with any type or data structure.

If you have a number of data items that you want to group together, you can use a special data structure called, amazingly, struct. A struct is formed as follows.

struct <name> {
  // one or more declarations go here
};

You can add whatever declarations you want inside the struct body (defined by the curly braces). The following shows a crude example. Notice that you can use the structure in an array.

struct address {
  int street_num;
  string street_name;
  string city;
  string state;
  string zip;
};


address[] address_book = new address[100];

Arrays and structures can increase the power of your programs by allowing you to work with more complex data types.

Wow! That was a wild ride, wasn’t it? I hope that this short crash course in C# has explained enough about the sample programs shown so far that you now know how they work. This crash course also forms the basis for understanding the other C# examples in this book.

OK, now it’s time to see some of these fundamental elements of C# in action. Let’s look at the blink an LED application you saw in Chapter 3 only this time you’re going to write it for Windows 10 IoT Core!

Blink an LED, C# Style

OK, let’s write some C# code! This project is the same concept as the project from Chapter 3 where you used Python to blink an LED on your Raspberry Pi. Rather than simply duplicate that project, you’ll mix it up a bit and make this example a headed application (recall a headed application has a user interface). The user interface presents the user with a greeting, a symbol that changes color in time with the LED, and a button to start and stop the blink timer.

Rather than build the entire application at once by presenting you a bunch of code, we walk through this example in two phases. The first phase builds the basic user interface built. The code for the GPIO is added in the second phase. By using this approach, you can test the user interface on your PC, which is really convenient.

Recall that the PC does not support the GPIO libraries (there is no GPIO!) so if you built the entire application, you would have to test it on the device, which can be problematic if there are serious logic errors in your code. This way, you can ensure that the user interface is working correctly and therefore eliminate any possible issues in that code before you deploy it.

Before you get into the code for the user interface, let’s look at the components that you will use, and then set up the hardware.

Required Components

The following lists the components that you need. All of these are available in the Microsoft Internet of Things Pack for the Raspberry Pi from Adafruit. If you do not have that kit, you can find these components separately on the Adafruit web site ( www.adafruit.com ), from SparkFun ( www.sparkfun.com ), or any electronics store that carries electronic components.

  • 560 ohm 5% 1/4W resistor (green, blue, brown stripes6)

  • Diffused 10mm red LED (or similar)

  • Breadboard (mini, half, or full sized)

  • (2) male-to-female jumper wires

You may notice that this is the same set of components you used in Chapter 3.

Set up the Hardware

Begin by placing the breadboard next to your Raspberry Pi and power the Raspberry Pi off orienting the Raspberry Pi with the label facing you (GPIO pins in the upper-left). Next, take one of the jumper wires and connect the female connector to pin 6 on the GPIO. The pins are numbered left-to-right starting with the lower left pin. Thus, the left two pins are 1 and 2 with pin 1 below pin 2. Connect the other wire to pin 7 on the GPIO.

Tip

The only component that is polarized is the LED. This longer side is the positive side.

Next, plug the resistor into the breadboard with each pin on one side of the center groove. You can choose whichever area you want on the breadboard. Next, connect the LED so that the long leg is plugged into the same row as the resistor and the other pin on another row. Finally, connect the wire from pin 6 to the same row as the negative side of the LED and the wire from pin 7 to the row with the resistor. Figure 6-2 shows how all of the components are wired together. Be sure to study this drawing and double-check your connections prior to powering on your Raspberry Pi. Once you’re satisfied everything is connected correctly, you’re ready to power on the Raspberry Pi and write the code.

A416714_1_En_6_Fig2_HTML.jpg
Figure 6-2. Wiring the LED to a Raspberry Pi

Since you are building a headed application, you’ll also need a keyboard, mouse, and monitor connected to the Raspberry Pi.

OK, now that the hardware is set up, it’s time to start writing the code.

Write the Code: User Interface

Begin by opening a new project template. Choose C#WindowsUniversal in the tree and the Blank App (Universal Windows) template in the list. This template creates a new solution with all of the source files and resources you need for a UWP headed application. Figure 6-3 shows the project template you need. Use the project name BlinkCSharpStyle.

A416714_1_En_6_Fig3_HTML.jpg
Figure 6-3. New Project dialog: blank application

Notice that there are a number of files created. You first add the XAML code in the MainPage.xaml file. Listing 6-3 shows the bare XAML code placed in the file by default. I’ve added a note that shows where to add new code.

Listing 6-3. Bare XAML code (MainPage.xaml)
<Page
    x:Class="BlinkCSharpStyle.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:BlinkCSharpStyle"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        --> Our code goes here.
    </Grid>
</Page>

Recall that the XAML file is used to define a user interface in a platform independent way using an XML-like language. In this project, I demonstrate the more basic controls: a text box, a button, and an ellipse (circle) placed inside a special controlled called a stacked panel. The stacked panel allows you to arrange the controls in a vertical “stack,” making it easier to position them. As you can see in the listing, you want to place your XAML user interface items in the <Grid></Grid> section.

In this example, you want a text box at the top, a circle (ellipse) to represent the LED that you will use to turn on (change to green) and off (change to gray) to correspond with the hardware on/off code that you will add later. You also need a button to toggle the blink operation on and off. Finally, you’ll add another text box to allow you to communicate with the user about the state of the GPIO code (that you’ll add later).

Now let’s add the code. Since the stacked panel is a container, all of the controls are placed inside it. Listing 6-4 shows the code you want to add (shown in bold).

Listing 6-4. Adding XAML Code for the User Interface: MainPage.xaml
<Page
    x:Class="BlinkCSharpStyle.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:BlinkCSharpStyle"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel Width="400" Height="400">
            <TextBlock x:Name="title" Height="60" TextWrapping="NoWrap"
                     Text="Hello, Blinky C# Style!" FontSize="28" Foreground="Blue"
                     Margin="10" HorizontalAlignment="Center"/>
            <Ellipse x:Name="led_indicator" Fill="LightGray" Stroke="Gray"  Width="75"
                     Height="75" Margin="10" HorizontalAlignment="Center"/>
            <Button x:Name="start_stop_button" Content="Start" Width="75" ClickMode="Press"
                     Click="start_stop_button_Click" Height="50" FontSize="24"
                     Margin="10" HorizontalAlignment="Center"/>
            <TextBlock x:Name="status" Height="60" TextWrapping="NoWrap"
                     Text="Status" FontSize="28" Foreground="Blue"
                     Margin="10" HorizontalAlignment="Center"/>
        </StackPanel>
    </Grid>
</Page>

Notice the button control. Here you have an event that you want to associate with the button named start_stop_button_Click, which you assigned via the Click attribute. That is, when the user clicks it, a method named start_stop_button_Click() will be called.

XAML provides a great way to define a simple, easy user interface with the XML-like syntax. However, it also provides a mechanism to associate code with the controls. The code is placed in another file called a source-behind file named MainPage.xaml.cs. You will place all of the source code for the application in this file.

If you were typing this code in by hand, you will notice a nifty feature of Visual Studio—a context-sensitive help called IntelliSense that automatically completes the code you’re typing and provides drop-down lists of choices. For example, when you type in the button control and type Click=. A drop-down box will appear, allowing you to create the event handler (a part of the code that connects to the XML). In fact, it creates the code in the MainPage.xaml.cs file for you. If you copy and pasted the code, you would not get this option and would have to type in the code manually.

Let’s look at the code for the button control implemented in the class that you created in the source code file (MainPage.xaml.cs). Listing 6-5 shows the code you need to add in bold. You place everything in the class named BlinkCPPStyle (the application).

Listing 6-5. Adding the Code: MainPage.xaml .cs
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// Add using clause for GPIO
using Windows.Devices.Gpio;


namespace BlinkCSharpStyle
{
public sealed partial class MainPage : Page
    {
        // Create brushes for painting contols
        private SolidColorBrush greenBrush = new SolidColorBrush(Windows.UI.Colors.Green);
        private SolidColorBrush grayBrush = new SolidColorBrush(Windows.UI.Colors.Gray);


        // Add a variable to control button
        private Boolean blinking = false;


        public MainPage()
        {
            this.InitializeComponent();
            // Add code to initialize the controls
            this.led_indicator.Fill = grayBrush;
        }


        private void start_stop_button_Click(object sender, RoutedEventArgs e)
        {
            this.blinking = !this.blinking;
            if (this.blinking)
            {
                this.start_stop_button.Content = "Stop";
                this.led_indicator.Fill = greenBrush;
            }
            else
            {
                this.start_stop_button.Content = "Start";
                this.led_indicator.Fill = grayBrush;
            }
        }
    }
}

OK, there are a few extra bits here that may not be very obvious why they’re here. You want to paint the LED control green and gray for on and off. To do that, you need a reference to the green and gray brush resources. Thus, I create a new object from the Windows user interface colors namespace.

You also add the code for the button click event, start_stop_button_Click(), as well as a boolean member variable that you use to trigger the LED timer. You add code that inverts the blinking variable (toggles between false and true) and depending on the value, you turn the led indicator control green (meaning the LED is on) or gray (meaning the LED is off). You also change the label of the button to correspond with the operation. That is, if the button is labeled Start, the LED indicator is off, and when clicked, the label changes to Stop and the LED indicator is turned on.

That’s it! You’ve finished the user interface. Go ahead and build the solution correcting any errors that may appear. Once compiled, you’re ready to test it. The following shows an example of what you should see in the output window .

Restoring NuGet packages...
To prevent NuGet from restoring packages during build, open the Visual Studio Options dialog, click on the Package Manager node and uncheck 'Allow NuGet to download missing packages during build.'
1>------ Rebuild All started: Project: BlinkCSharpStyle, Configuration: Debug ARM ------
1>  BlinkCSharpStyle -> C:UsersChuckDocumentsVisual Studio 2015ProjectsBlinkCSharpStyleinARMDebugBlinkCSharpStyle.exe
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

Test and Execute: User Interface Only

That was easy, wasn’t it? Better still, since this is a Universal app, you can run this code on your PC. To do so, choose debug and x86 (or x64) from the platform box and press Ctrl+F5 . Figure 6-4 shows an excerpt of the output (just the controls itself).

A416714_1_En_6_Fig4_HTML.jpg
Figure 6-4. The user interface: timer off

Figure 6-5 shows what happens when you click the button. Cool, eh?

A416714_1_En_6_Fig5_HTML.jpg
Figure 6-5. The user interface: timer on

You may be wondering where the blink part is. Well, you haven’t implemented it yet. You will do that in the next phase.

Add the GPIO Code

Now, let’s add the code to work with the GPIO header. For this phase, you cannot run the code on your PC because the GPIO header doesn’t exist, but you can add code to check the GPIO header status—hence the extra text box in the interface.

Note

The following is a bit more complicated and requires additional objects. Thus, I walk through the code changes one part at a time.

Let’s start with adding the resource you need to access the GPIO header. Right-click References in the project and choose Add Reference.... When dialog opens, choose Universal WindowsExtensions from the tree view and Windows IoT Extensions for the UWP from the list. This allows you to include additional namespaces for the GPIO. Figure 6-6 shows the resources dialog with the item selected.

A416714_1_En_6_Fig6_HTML.jpg
Figure 6-6. Adding a new resource

Notice on my system there are two entries—one for each version of the libraries I have loaded. You may only see the one entry. Click OK once you have the item selected.

Tip

Henceforth, for brevity, I present excerpts of the files that we will be editing.

Now, let’s add the variables you need for the timer in the source file (MainPage.xaml.cs). The following shows the code you need to add. Here you add an instance of the DispatchTimer as well as several private variables for working with the pin in the GPIO library . I show the changes in context with the new lines in bold.

...
        private SolidColorBrush greenBrush = new SolidColorBrush(Windows.UI.Colors.Green);
        private SolidColorBrush grayBrush = new SolidColorBrush(Windows.UI.Colors.Gray);


        // Add a Dispatch Timer
        private DispatcherTimer blinkTimer;


        // Add a variable to control button
        private Boolean blinking = false;


        // Add variables for the GPIO
        private const int LED_PIN = 4;
        private GpioPin pin;
        private GpioPinValue pinValue;
...

The private variables store the pin value, a constant set to GPIO 4 (hardware pin #7), and a variable o store the pin variable result.

Next, you need a new method to initialize the GPIO—InitGPIO(). You add a new private method to the class and complete it with code to control the GPIO. As you can see, there is a lot going on here.

private void InitGPIO()
{
    var gpio_ctrl = GpioController.GetDefault();


    // Check GPIO state
    if (gpio_ctrl == null)
    {
        this.pin = null;
        this.status.Text = "ERROR: No GPIO controller found!";
        return;
    }
    // Setup the GPIO pin
    this.pin = gpio_ctrl.OpenPin(LED_PIN);
    // Check to see that pin is Ok
    if (pin == null)
    {
        this.status.Text = "ERROR: Can't get pin!";
        return;
    }
    this.pin.SetDriveMode(GpioPinDriveMode.Output);
    this.pinValue = GpioPinValue.Low; // turn off
    this.pin.Write(this.pinValue);
    this.status.Text = "Good to go!";
}

The code first creates an instance of the default GPIO controller class. Next, you check to see if that instance is null, which indicates the GPIO header cannot be initiated and if so you change the label of the status text and return. Otherwise, you open the GPIO pin defined earlier. If that value is null, you print the message that you cannot get the pin. Otherwise, you set up the pin for output mode, and then turn off the bin and state all is well in the status label text.

Next, you add a new method to handle the event fired from the DispatchTimer. The timer fires (or call) this method on the interval you specify (see the following changes to the MainPage() method ).

private void BlinkTimer_Tick(object sender, object e)
{
    // If pin is on, turn it off
    if (this.pinValue == GpioPinValue.High)
    {
        this.led_indicator.Fill = grayBrush;
        this.pinValue = GpioPinValue.Low;
    }
    // else turn it on
    else
    {
        this.led_indicator.Fill = greenBrush;
        this.pinValue = GpioPinValue.High;
    }
    this.pin.Write(this.pinValue);
}

In this method, you check the value of the pin. Here is where the real operation of the code happens. In this code, if the pin is set to high (on), you set it to low (off), and paint the LED control gray. Otherwise, you set the pin to high (on) and paint the LED control green. Cool, eh?

Note

You could change this color to match the color of your LED if you wanted. Just remember to change the brush accordingly in the source file.

Next, you need to add code to the start_stop_button_Click() method to start and stop the timer. The changes are in bold.

private void start_stop_button_Click(object sender, RoutedEventArgs e)
{
    this.blinking = !this.blinking;
    if (this.blinking)
    {
        this.start_stop_button.Content = "Stop";
        this.led_indicator.Fill = greenBrush;
        this.blinkTimer.Start();
    }
    else
    {
        this.start_stop_button.Content = "Start";
        this.led_indicator.Fill = grayBrush;
        this.blinkTimer.Stop();
        this.pinValue = GpioPinValue.Low;
        this.pin.Write(this.pinValue);
    }

You see here a few things going on. First, notice that you invert the blinking variable to toggle blinking on and off. You then add a call to the blinkTimer instance of the DispatchTimer to start the timer if the user presses the button when it is labeled Start. Notice you also set the label of the button to Stop so that, when clicked again, the code turns off the timer and set the pin value to low (off). This is an extra measure to ensure that if the button is clicked when the timer is between tick events, the LED is turned off. Try removing it and you’ll see.

Finally, you must add a few lines of code to the MainPage() method to get everything started when you launch the application. The following shows the code with changes in bold.

public MainPage()
{
    this.InitializeComponent();
    // Add code to initialize the controls
    this.led_indicator.Fill = grayBrush;
    // Add code to setup timer
    this.blinkTimer = new DispatcherTimer();
    this.blinkTimer.Interval = TimeSpan.FromMilliseconds(1000);
    this.blinkTimer.Tick += BlinkTimer_Tick;
    this.blinkTimer.Stop();
    // Initalize GPIO
    InitGPIO();
}

Notice you add code to create a new instance of the DispatchTimer class, set the interval for the tick event to 1 second (1000 milliseconds), add the BlinkTimer_Tick() method to the new instance (this is how you assign a method reference to an existing event handle). Next, you stop the timer and finally call the method that you wrote to initialize the GPIO.

That’s it! Now, let’s build the solution and check for errors. You should see something like the following in the output window .

Restoring NuGet packages...

To prevent NuGet from restoring packages during build, open the Visual Studio Options dialog, click on the Package Manager node and uncheck 'Allow NuGet to download missing packages during build.'
1>------ Build started: Project: BlinkCSharpStyle, Configuration: Debug x86 ------
1>  BlinkCSharpStyle -> c:userschuckdocumentsvisual studio 2015ProjectsBlinkCSharpStyleinx86DebugBlinkCSharpStyle.exe
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

OK, now you’re ready to deploy the application to your device. Go ahead and set up everything and power on your device.

Deploy and Execute: Completed Application

Once your code compiles, you’re ready to deploy the application to your Raspberry Pi (or other device). Recall from Chapter 4, you have to set up the debug settings to specify the IP address of your Raspberry Pi. Fortunately, you only have to change three things: IP address, authentication, and uninstall/install check box, as indicated in Figure 6-7. Remember to choose ARM for the platform. Click Apply and then OK to close the dialog.

A416714_1_En_6_Fig7_HTML.jpg
Figure 6-7. Debug Settings for Deployment

Next, you should also name your application so that you can recognize it on the device. Double-click the Package.appxmanifest file. You see a new tabbed window open in the IDE. Click the Packaging tab and change the name of the application, as shown in Figure 6-8. You do this so that the application shows up in the application list on the device. Otherwise, you would get a strange name that is not readily recognizable.

A416714_1_En_6_Fig8_HTML.jpg
Figure 6-8. Setting Package Name

Once you have these set, you can power on your Raspberry Pi, and once it is booted, go to the Device Portal and turn on the remote debugger, as shown in Figure 6-9.

A416714_1_En_6_Fig9_HTML.jpg
Figure 6-9. Turning on the remote debugger

Now you can deploy the application from the Build menu. When complete, you’ll get messages like the following from the Output window.

1>------ Build started: Project: BlinkCSharpStyle, Configuration: Debug ARM ------
1>  BlinkCSharpStyle -> c:userschuckdocumentsvisual studio 2015ProjectsBlinkCSharpStyleinARMDebugBlinkCSharpStyle.exe
2>------ Deploy started: Project: BlinkCSharpStyle, Configuration: Debug ARM ------
2>Creating a new clean layout...
2>Copying files: Total 16 mb to layout...
2>Checking whether required frameworks are installed...
2>Framework: Microsoft.NET.CoreRuntime.1.0/ARM, app package version 1.0.23430.0 is not currently installed.
2>Installing missing frameworks...
2>Registering the application to run from layout...
2>Deployment complete (54476ms). Full package name: "BlinkCSharpStyle_1.0.0.0_arm__2v0q544fdcg4c"
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
========== Deploy: 1 succeeded, 0 failed, 0 skipped ==========

Notice the name that you were shown. This is the name that appears on your device. Go back to the Device Portal and click Apps, and then choose the application in the drop-down list and click Run. Figure 6-10 shows the settings.

A416714_1_En_6_Fig10_HTML.jpg
Figure 6-10. Starting an application on the device
Note

If the app deployed successfully but doesn’t show in the drop-down list, try disconnecting and reconnecting. If that doesn’t work, try rebooting your device.

You should soon see the application start on your device. Go ahead and click the start/stop button and observe the application running. The LED on the breadboard should blink in unison with the LED represented in the user interface. Play with it until the thrill dissipates.

If the LED is not blinking, double-check your wiring and ensure that you chose pin 4 in the code and pin 7 on the pin header (recall that the pin is named GPIO 4 but it is pin #7 on the header).

You can stop the application in the Device Portal by clicking the Remove button, as shown in Figure 6-10. And that’s it! Congratulations, you’ve just written your first C# application that uses the GPIO header to power some electronics!

Tip

As a challenge, you can modify this project to add a Close or an Exit button to stop the application.

Summary

If you are learning how to work with Windows 10 IoT Core and don’t know how to program with C#, learning C# can be a challenge. While there are many examples on the Internet you can use, very few are documented in such a way as to provide enough information for someone new to C# to understand or much less get started or even compile and deploy the sample!

This chapter has provided a crash course in C# that covers the basics of the things that you encounter when examining most of the smaller example projects. You discovered the basic syntax and constructs of a Visual C# application, including a walk-through of building a real C# application that blinks an LED. Through the course of that example, you learned a little about XAML, including how to wire events to controls, and even a little about how to use the dispatcher timer.

In the next chapter, you’ll discover another programming language named Python. You implement the same example project you saw in this chapter but without the user interface. If you want to see how to work with the GPIO in Python, read on!

Footnotes

1 Not “see-hash” or worse, “see-hashtag”—both of which may show ignorance, so don’t do that.

2 As opposed to other languages like C++ with differing notation based on how an object is instantiated.

3 A function returns a value, whereas a method does not.

4 If you ever hear someone claim, “My code is self-documenting,” be cautious when using his or her code. There is no such thing. Sure, plenty of good programmers can write code that is easy to understand (read), but all fall short of that lofty claim.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.42.196