You are on page 1of 4

LEARN DESIGN SHARE

Windows on
the Raspberry Pi (2)
Remote control of a seven-segment display
By Tam Hanna (Germany)

Following on from our introduction in the first part of this series, we now turn to some more complex
applications. In this example we show how to implement remote control over a network. TCP/IP
implementations are often the bane of embedded programmers, but fortunately Windows 10
includes a ready-made networking stack that makes it easy to implement communication. To
demonstrate that the whole thing really works we make use of a small seven-segment display.

For the following experiments we will employ the circuit shown


in Figure 1. Seven-segment displays have been popular since
before the age of the LCD: they consist of a group of LEDs
which can be lit in various combinations to display digits (see
Figure 2).

Our application uses the Raspberry Pi to drive this


display, with the number to be shown being sent
over the network. One possible real-world
use of this would be to request a cer-
tain number of items on a produc-
tion line.

Telnet client
What makes implementing
networked applications dif-
ficult is that when designing
a protocol it is necessary to
keep in mind both what is hap-
pening at the client end and what is
happening at the server end.
In this case the server is our Raspberry Pi,
which listens for control commands over a TCP/IP
connection. Of course, we will also need to write the
code to run on the client computer, ideally in both Windows project because the same VM is available
and Linux versions. However, there is an even simpler approach: on current versions of Windows (for PCs) as
both Windows and Linux offer Telnet terminal clients which well as on Windows Phone, and as a result the
implement a very simple protocol designed for exchanging compiled server code for the Raspberry Pi can also
textual information between machines. be used on other platforms.
In order to prevent the use of polling loops or other potentially
Under the Microsoft operating system it is usually necessary blocking constructs, Microsoft forces developers to use asyn-
to enable the Telnet client before it can be used: see the FAQ chronous programming. For example, if a web server wants
at [1] for more information. to receive characters over a TCP connection it must create
a listener object for incoming requests at the beginning of
Its all asynchronous! the program. After the listener object has been initialized it is
C# is a programming language whose runtime environment is passed a function which is to be called whenever a client opens
implemented as a virtual machine (VM). This is relevant to our a TCP connection and sends a request to the server. This event

22 January & February 2016 www.elektormagazine.com


BASICS TRAINING Q &A TIPS & TRICKS SOFTWARE

handler function (see also the first part of this series [2]) con-
tains the code to deal with the request. Raspberry Pi 2

The listener runs in its own thread, as if it were a separate


J8
program running in parallel with the main code. The conse-
1
quence is that there is no point in the main program where 2 3V3

it has to spin its wheels waiting for a request to arrive before


7
proceeding: such synchronous programming can lead to the GPIO04

whole application grinding to a halt.


13
For convenience we create the listener directly in the main 15
GPIO27
a CA CA
GPIO22 1k1
page code (again, see the first part of this series [2]): 1k1 b
1k1 c
1k1 d
public MainPage() 1k1 e
1k1 f
{ 1k1 g
29
this.InitializeComponent(); GPIO05
31 GPIO06
33 GPIO13

... 37 GPIO26
39 40

mySocket = new StreamSocketListener();


mySocket.ConnectionReceived +=
MySocket_ConnectionReceived;
mySocket.BindServiceNameAsync(234);
} Figure 1. Our simple circuit displays digits sent over a network connection.

The listener object mySocket is of type


StreamSocketListener.MySocket_ DataReader object, which will accept the characters that arrive
ConnectionReceived is the event handler over the TCP connection.
which will be called when a TCP connection The line
is opened, and it is this function that we
need to write. The BindServiceNameAsync uint sizeFieldCount = await reader.
method causes a new thread to be cre- LoadAsync(sizeof(byte));
ated for the listener, where it will wait
for a request. The handle 234 can be causes the code to wait until a character (a character has a
used to reference this thread later in the size of one byte) is received. The variable sizeFieldCount will
program if we need to. We recommend be 1 if a valid character (as opposed to a control character)
that for now beginners copy our code has been received.
exactly, and only contemplate modifi-
cations later on. The test can be found at the point marked (1) in the listing:
if a control character is seen, the continue statement causes
The event handler M y S o c k e t _ the loop to start again from the beginning. Alternatively, if the
ConnectionReceived() contains the code character is valid its ASCII code is placed in the byte variable
to process the request. In our case the what, so that it can be processed by subsequent commands.
request to the server consists of a sin-
gle character, and so the handler will be
somewhat simpler than a web server that
has to deal with the full HTTP protocol.
Depending on the Telnet client used, it is
possible that the program will receive one
or more control characters in addition to
the desired character. For this
reason we have to check
each incoming char-
acter one by one to
determine whether
it is normal or not.

The code is shown in Figure 2. Most enthusiasts should be able to find a seven-segment display
Listing 1. First we create a like this one in the junk box.

www.elektormagazine.com January & February 2016 23


LEARN DESIGN SHARE

Once the KV store is set up it is easy to access GPIO pin for a


Listing 1. Receiving and checking characters.
given LED segment via its name, from a to g.

private async void MySocket_ Now we need a routine that, given a digit received over the
ConnectionReceived(StreamSocketListener sender, network, will tell us which LED segments should be lit. Again
a little trick: we will represent the set of segments to be lit as
StreamSocketListenerConnectionReceivedEventArgs
a sequence of characters (from a to g) stored as a string.
args)
The following function lights the required LEDs when passed
{
a string (for example abde) specifying the desired pattern.
DataReader reader = new DataReader(args.
Socket.InputStream); void setLeds(String activeSegmentsString)
while (true) {
{ killLeds();
reader.InputStreamOptions =
InputStreamOptions.Partial; for (int i = 0; i < activeSegmentsString.Length;
byte what; i++)
uint sizeFieldCount = await reader. {
LoadAsync(sizeof(byte));
myKVStore[activeSegmentsString[i]].
if (sizeFieldCount != sizeof(byte)) // (1)
Write(GpioPinValue.Low);
{
}
continue; }
}
The expression activeSegmentsString[i] references the char-
what = reader.ReadByte(); acter occurring in position i in the string, which will be a seg-
ment letter from a to g. Then myKVStore[activeSegmentsS
// Code for processing tring[i]] gives us a GPIO pin object corresponding to that

// of character received
letter. We take the pin low and the LED segment will light.
The function killLEDs() at the beginning turns off all the seg-
// ASCII-Code is found in what
ments, and works in a similar way:
}
}
void killLEDs()
{
String darkSegmentsString = abcdefg;
LED control for (int i = 0; i < darkSegmentsString.Length;
To drive our seven-segment display we shall use a little trick
i++)
from the world of systems programming. A key-value (KV) store
{
is a kind of array whose elements can be of any desired type.
myKVStore[darkSegmentsString[i]].
Indeed, we can even write objects to the store. Elements are
Write(GpioPinValue.High);
accessed using a key, which again can be of any desired type.
The configPin method populates a data structure of this kind }
with the required GPIO pin objects (see [2]) to let us access }
the pins on the Raspberry Pi.
The method is called for each pin that is connected to the All we need to do now, when we receive a digit from 0 to 9,
seven-segment display, in each case passing the pin number is to call setLEDs() with the right string to light the segments
(using the conventional Raspberry Pi numbering system) and a we want passed as its parameter. This can be done efficiently
character from a to g specifying the corresponding segment using a switch-case construct (see Listing 2). The code should
of the display. Here is the code for this function, which writes be clear enough: note that we start with a check on the ASCII
a GPIO object to the KV store. The function is also responsible code for the character to verify that it lies in the range of dig-
for configuring the pin as an output. its from 0 to 9.

private void configPin(int rpiPinNumber, char Incidentally, we do not run into any problems with the speed
ledSegmentChar) of this code when turning segments on and off using string
{ parameters. The processing speed of the Raspberry Pi 2 is
GpioPin workPin; much higher than conventional microcontrollers, and as we
saw in the first part of this series, the input/output driver is,
workPin = myGPIO.OpenPin(rpiPinNumber);
shall we say, fast enough.
workPin.SetDriveMode(GpioPinDriveMode.Output);
myKVStore.Add(ledSegmentChar,workPin);
The whole program can be downloaded from the Elektor web-
}
site as a Visual Studio project [3].

24 January & February 2016 www.elektormagazine.com


BASICS TRAINING Q &A TIPS & TRICKS SOFTWARE

Listing 2.
Lighting the correct segments of the display.

what = reader.ReadByte();
if (what >= 48 && what <= 57)
{
switch (what-48)
{
case 0:
setLeds(abcdef);
break;
case 1:
setLeds(bc);
break;
case 2:
setLeds(abged);
break;
Figure 3. The correct permissions need to be given to the app for it to
function correctly. case 3:
setLeds(abgcd);
break;
case 4:
Permissions setLeds(fgcb);
In order to ensure that the app works properly
break;
we need to set up the necessary permissions for
case 5:
it in Visual Studio. Microsoft secures apps using a
setLeds(afgcd);
system whereby developers must state in advance
break;
which system functions are going to be used by
the program. Click on the file Package.appxmani- case 6:
fest and in the Capabilities column tick the three setLeds(afgedc);
attributes shown in Figure 3. break;
That is the final step (for now!): launch the pro- case 7:
gram on the Raspberry Pi and connect to the setLeds(abc);
machine over Telnet. Send a single digit and you break;
should see it appear on the seven-segment display. case 8:
setLeds(abcdefg);
Conclusion
break;
Single-board computers like the Arduino are ideal
case 9:
control and monitoring devices: they are optimized for the
setLeds(abcdfg);
high-speed monitoring of processes. Ease of communicating
break;
with the outside world is a lower priority, as anyone who has
had to deal with an Ethernet shield or who has struggled with }
the dreaded Arduino Yn bridge library will attest. }
Windows 10 has the opposite emphasis: the system is opti-
mized for efficient communication with the user and with other
computers. The socket interface code we have seen here will
run equally well on a normal workstation or on a Windows
Phone; the disadvantage in comparison to Linux systems is
the relatively poor real-time performance.
This disadvantage can be mitigated by using dedicated sen-
sors or a second processor for process control, and in the next
issue we will look at the programming interfaces that make Router headaches
this possible. Until next time!
The Raspberry Pi 2 running under Windows 10 can
(150519)
sometimes cause trouble for routers: the authors
Web Links Asus RT-N12E was giving ping latencies of around
[1] http://windows.microsoft.com/en-us/windows/telnet-faq ten seconds. The problem can be worked around by
adding a second router behind the first to provide the
[2] www.elektormagazine.com/150465
connection between the workstation and the single-
[3] www.elektormagazine.com/150519 board computer.

www.elektormagazine.com January & February 2016 25