| PC Hardware interface knowledge update |
|
| Typically we think of robots as machines from science fiction or as industrial robots such as those that build and paint cars. In the world of Microsoft Robotics Developer Studio, anything that has sensors and/or actuators can be considered a robot. In this article, we look at an automated house as a robot and apply the Decentralized Software Services model of Microsoft Robotics Studio to implementing some home automation tasks. Charles Stacy Harris III Difficulty: Intermediate Time Required: 6-10 hours Cost: $100-$200 for hardware (Optional, $0 if using only simulation) Software: Visual Studio or Visual Studio Express, Microsoft Robotics Developer Studio 2008 CTP April, ControlThink Z-Wave PC SDK Hardware: (All Optional) Elk M1G/M1EZ Security and Automation Panel, Z-Wave Dimmer Switches, Z-Wave Controller such as the ControlThink ThinkStick or equivalent. Download: Download House, Robot Those of you who know me or who have read my previous Coding4Fun article Home Automation with Windows Workflow, know that I’m nuts about home automation. I’m even more passionate about robotics and I’ve wanted to try out some things with Microsoft Robotics Studio since the very first version. Recently I had a long weekend, so I spent a few hours putting together some simple home automation examples using Microsoft Robotics Developer Studio 2008 CTP April. Check out the Microsoft Robotics Developer Center for details on how to get the CTP. The examples in this article duplicate what I did using Windows Workflow in my previous article, but instead use the Decentralized Software Services (DSS) approach of Microsoft Robotics, and Microsoft Visual Programming Language (VPL) for a simple way to write automation tasks. The Microsoft Robotics Developer Center contains a lot of detailed information about DSS, VPL, and the other technologies found in Microsoft Robotics Developer Studio. For now, let’s just cover some basics. To get further along with the code in this article, you’ll want to check out some of the excellent tutorials presented by the Microsoft Robotics team. Services, Messages, and Ports A DSS Service is the basic component upon which Microsoft Robotics applications are built. In fact, DSS services are a generic construct that can and have been used outside of the context of robotics. DSS services contain state and the service state is manipulated via messages sent to the service on a service port. Messages sent to a service are structured .NET classes that may contain a message payload that determines how or even if state will be modified, or what part of a service state should be retrieved. There are also message that do not directly manipulate state, but may have some other side-effects. Services respond to messages such as CREATE, LOOKUP, UPDATE, etc. In addition, you can define messages that extend the semantics of the core messages. Ports are the mechanism through which services communicate. Ports accept a set of message types that are defined by the service itself. In addition, ports are also used for outbound communication in situations such as subscribing to event notification from a service. Again, there are excellent online resources for learning the details of Microsoft Robotics, DSS, and the underlying technologies for Microsoft Robotics developer Studio. You’ll probably want basic familiarity with the Microsoft Robotics architecture including the Concurrency and Coordination Runtime (CCR) along with the notion of Arbiters and iterators. Robo-Moose The security and automation system in my house is centered on an Elk M1G alarm panel from Elk Products Incorporated . This panel allows for up to 208 input zones in the form of contact switches, motion sensors, and so on. It also allows for up to 208 outputs, on-board task scripting and many other features. One of the key features for my use is the capability of being able to monitor and control the panel via an Ethernet adaptor. So, my first order of business was to write a DSS service – the ElkService – to communicate with the Elk M1G via sockets. This ElkService exchanges messages with the Elk M1G via an ASCII protocol that is documented here. The code that the ElkService uses to read from the Elk M1G hardware is shown below. Note that the code looks sequential, but is actually asynchronous. The StreamAdapter.Read method sets up a task to do an asynchronous IO operation and the line “yield return (Choice)ioResultPort;” returns this task to the runtime which is iterating over all tasks returned by ElkReader. /// <summary> /// ElkReader opens a socket connection to the hardware panel. It then /// enters a loop that /// </summary> /// <returns></returns> public IEnumerator<ITask> ElkReader() { Connect(); // Send a request to the Elk panel get a report of the entire zone status. SendElkMessage(RequestStrings.ZoneStatus); byte [] buffer = new byte [256]; int bytesRead = -1; Exception ex = null ; do { var ioResultPort = StreamAdapter.Read(networkStream, buffer, 0, buffer.Length); yield return (Choice)ioResultPort; ex = ioResultPort; if (ex != null ) throw ex; bytesRead = ioResultPort; if (bytesRead != 0) ProcessRawElkMessage(Encoding.ASCII.GetString(buffer, 0, bytesRead)); } while (bytesRead != 0); } When the ElkService receives a packet from the hardware panel, it converts the packet from a string to a custom message containing the raw sensor data and posts the message to the main port of the ElkService itself. For example, when the service receives a “ZC” message from the hardware, it converts this to an UpdateRawZone message that contains the raw data from the security panel. This happens in the method ProcessRawElkMessage. Here is a fragment of that code: public void ProcessRawElkMessage( string message) { string messageType = message.Substring(2, 2); switch (messageType) { case "ZC" : // Zone status change var zoneState = new UpdateRawZoneRequest { Id = byte .Parse(message.Substring(4, 3)), State = byte .Parse(message.Substring(7, 1), NumberStyles.HexNumber) }; var updateZoneMessage = new UpdateRawZone(); updateZoneMessage.Body = zoneState; _mainPort.Post(updateZoneMessage); break ; … UpdateRawZoneRequest is the message payload and is used to send data about a zone change event to a subscriber. UpdateRawZone is the actual message type that is transmitted on the port for the ElkService. [DataContract] [Description( "UpdateRawZone Request Message Payload" )] public class UpdateRawZoneRequest { [DataMember] public byte Id { get; set; } [DataMember] public byte State { get; set; } } /// <summary> /// Update Elk zone status /// </summary> [Description( "UpdateZone request message" )] public class UpdateRawZone : Update<UpdateRawZoneRequest, PortSet<DefaultUpdateResponseType, Fault>> { } The message processing code can also be written a bit more compactly as: public void ProcessRawElkMessage( string message) { string messageType = message.Substring(2, 2); switch (messageType) { case "ZC" : // Zone status change var updateZoneMessage = new UpdateRawZone { Body = new UpdateRawZoneRequest { Id = byte .Parse(message.Substring(4, 3)), State = byte .Parse(message.Substring(7, 1), NumberStyles.HexNumber) } }; _mainPort.Post(updateZoneMessage); break ; … UpdateRawZoneHandler is a message receiver that is active on the main port and which responds to the update message by updating internal state, and notifying subscribers of the change in state. [ServiceHandler(ServiceHandlerBehavior.Exclusive)] public IEnumerator<ITask> UpdateRawZoneHandler(UpdateRawZone message) { _state.ZoneStates[message.Body.Id - 1] = message.Body.State; SendNotification(_submgrPort, message); message.ResponsePort.Post(DefaultUpdateResponseType.Instance); yield break ; } In the Zone The ElkService represents the core service that communicates with the Elk M1G hardware. The next service – ElkZoneSensor – represents a higher level “sensor array” that is attached to the Elk hardware. ElkZoneSensor “partners” with the ElkService. This means that the ElkZoneSensor relies on the ElkService to function. The DSS infrastructure will ensure that the ElkService is started if needed when starting the ElkZoneSensor service. The ElkZoneSensor subscribes to updates from the ElkService for changes in the hardware zones. When these changes occur, the ElkService notifies the ElkZoneSensor which updates its state and notifies any of its subscribers. The ElkZoneSensor uses a higher level message – UpdateZone – that contains a sensor ID and an enumeration that describes the state of the sensor. [DataContract] public class Zone { [DataMember] [Description( "The Elk hardware zone id of the sensor" )] [DataMemberConstructor] public byte Id { get; set; } [DataMember] [Description( "The Elk defined state of the sensor" )] public ZoneStatus Status { get; set; } } Candlepower The next services that we need for the robotic house are services to control the lights. In this case, I started with a generic contract for the DSS service. A generic contract in DSS terms just defines the set of messages to which a service will respond and defines the port type on which those messages will be sent. The generic contract does not contain code to actually implement the service behavior. One of the benefits of a generic service contract is that you can specify the contract, write algorithms that use the contract, and later connect the generic service contracts to code that actually implements the behavior. This is very much like programming to an abstract interface in the object oriented world, and having multiple classes that actually implement the interface. We start off with a generic service called the GenericDimmer that represents a dimmer switch and we implement two concrete services: the SimulatedDimmer and the ZWaveDimmer. SimulatedDimmer just logs a message to represent its state change. The ZWaveDimmer actually controls the lights via a hardware Z-Wave controller. Here’s what the GenericDimmer contract looks like: namespace Robotics.GenericHouseControls.Dimmer { /// <summary> /// GenericDimmer Contract class /// </summary> public sealed class Contract { /// <summary> /// The Dss Service contract /// </summary> [DataMember] public const String Identifier = "http://schemas.tempuri.org/2008/04/generichousecontrolsdimmer.html" ; } /// <summary> /// The GenericDimmer State /// </summary> [DataContract] public class GenericDimmerState { [DataMember] [DataMemberConstructor(Order = 1)] public int Id; [DataMember] public int Level; } /// <summary> /// GenericDimmer Main Operations Port /// </summary> [ServicePort(AllowMultipleInstances = true )] public class GenericDimmerOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, HttpGet, On, Off, SetLevel> { } /// <summary> /// GenericDimmer Get Operation /// </summary> public class Get : Get<GetRequestType, PortSet<GenericDimmerState, Fault>> { /// <summary> /// GenericDimmer Get Operation /// </summary> public Get() { } /// <summary> /// GenericDimmer Get Operation /// </summary> public Get(Microsoft.Dss.ServiceModel.Dssp.GetRequestType body) : base (body) { } /// <summary> /// GenericDimmer Get Operation /// </summary> public Get(Microsoft.Dss.ServiceModel.Dssp.GetRequestType body, Microsoft.Ccr.Core.PortSet<GenericDimmerState, W3C.Soap.Fault> responsePort) : base (body, responsePort) { } } [DataContract] public class OnRequest { } [Description( "Turn the switch on" )] public class On : Update<OnRequest, PortSet<DefaultUpdateResponseType, Fault>> { } [DataContract] public class OffRequest { } [Description( "Turn the switch off" )] public class Off : Update<OffRequest, PortSet<DefaultUpdateResponseType, Fault>> { } [DataContract] public class SetLevelRequest { [DataMember] [DataMemberConstructor] public int Level { get; set; } } [Description( "Set the dim level" )] public class SetLevel : Update<SetLevelRequest, PortSet<DefaultUpdateResponseType, Fault>> { } } This code defines the contract only. Now to implement that contract we can run the DssNewService utility with the /implement switch and generate a service based on this contract. For example: dssnewservice /s:SimulatedDimmer /n:Robotics.HouseControls.SimulatedDimmer /i:GenericHouseControls.Y2008.M04.Proxy.dll will generate a new service called SimulatedDimmer in the .NET namespace Robotics.HouseControls.SimulatedDimmer. The service will have a stubbed out implementation of the GenericDimmer contract including service startup code, state, and stubbed out message handlers. Here’s what the main service file looks like for the simulated dimmer after I added logging to the generated methods. I’ve also deleted some of the using statements for easier reading. using pxdimmer = Robotics.GenericHouseControls.Dimmer.Proxy; namespace Robotics.HouseControls.SimulatedDimmer { /// <summary> /// House Controls Service /// </summary> [DisplayName( "Simulated Dimmer" )] [Description( "The Simulated Dimmer Switch Service" )] [Contract(Contract.Identifier)] [AlternateContract(pxdimmer.Contract.Identifier)] public class SimulatedDimmerService : DsspServiceBase { /// <summary> /// _state /// </summary> [ServiceState] [InitialStatePartner(Optional = true )] private pxdimmer.GenericDimmerState _state = new pxdimmer.GenericDimmerState(); /// <summary> /// _main Port /// </summary> [ServicePort( "/simulateddimmer" , AllowMultipleInstances = true )] private pxdimmer.GenericDimmerOperations _mainPort = new pxdimmer.GenericDimmerOperations(); /// <summary> /// Default Service Constructor /// </summary> public SimulatedDimmerService(DsspServiceCreationPort creationPort) : base (creationPort) { } /// <summary> /// Service Start /// </summary> protected override void Start() { base .Start(); // Add service specific initialization here. LogInfo( string .Format( "Dimmer State => Id:{0} Level:{1}" , _state.Id, _state.Level)); } [ServiceHandler(ServiceHandlerBehavior.Concurrent)] public IEnumerator<ITask> OnHandler(pxdimmer.On update) { LogInfo( string .Format( "Simulated Dimmer => Id:{0} On" , _state.Id)); update.ResponsePort.Post(DefaultUpdateResponseType.Instance); yield break ; } [ServiceHandler(ServiceHandlerBehavior.Concurrent)] public IEnumerator<ITask> OffHandler(pxdimmer.Off update) { LogInfo( string .Format( "Simulated Dimmer => Id:{0} Off" , _state.Id)); update.ResponsePort.Post(DefaultUpdateResponseType.Instance); yield break ; } [ServiceHandler(ServiceHandlerBehavior.Exclusive)] public IEnumerator<ITask> SetLevelHandler(pxdimmer.SetLevel update) { _state.Level = update.Body.Level; LogInfo( string .Format( "Simulated Dimmer => Id:{0} Level:{1}" , _state.Id, update.Body.Level)); update.ResponsePort.Post(DefaultUpdateResponseType.Instance); yield break ; } } } Check the code download for the implementation of the ZWaveDimmer. You’ll find that it’s pretty straightforward. It should be simple to write similar code if you use a different technology for lighting control. You just write a service that matches the GenericDimmer contract, and you should be able to configure the GenericDimmer using a manifest to point to the actual dimmer service. Check out the documentation in Microsoft Robotics Developer Studio for more information on manifests and how to use the manifest editor. Bulb On, Bulb Off! The “Hello, World” application for any home automation software that I’ve tested in my house has always been the automating of my pantry light. The pantry has a Z-Wave light switch inside and the pantry door has a contact switch that is connected to the Elk M1G. When the door is open, we want the light to turn on, and when the door is closed we want the light to turn off. Now that we have all the pieces for this, here’s what the VPL diagram for this looks like: As you can see, it was also a relatively easy matter to add text-to-speech to announce the opening and closing of the pantry door. You may find out quickly that this is an annoyance to those in the testing areas – for me that was my kitchen – so it’s nice to know that two quick hits of the delete key will also remove the text-to-speech. This is part of the beauty of the Microsoft Robotics programming model! The second example shows how you can use a motion detector – in this case connected to zone 19 on the Elk – to trigger a light. The light comes on at a level that you control from the VPL code, and stays on for a duration that you can also control from the code. For the example, I set the timer to a pretty low level, but you can customize it. You could also add additional rules to only turn on the light at certain times, or better still connect an ambient light sensor to decide whether you should turn the light on. In this diagram, if motion is detected – that is zone 19 is triggered with a status of ViolatedOpen – the light is turned on at a low dim level and a timer is set. When the timer completes, the light is turned off. If, however, there is more motion before the timer completes, the SetTimer message will cancel the old timeout and restart the timer. There are more VPL examples included in the download including an extra service that you can use to send email from your VPL code. I’m experimenting with using the email service to send me a text message whenever my alarm is armed or disarmed. Miscellanea Since I haven’t had a lot of time to study the testing framework and simulation environments used by Microsoft Robotics, I included a service called the ElkTestDataPlayback service that allows me to replay the contents of a text file to simulate the ElkService getting actual data. This works pretty well for testing. The contents of the file are merely raw data captures that I made from the data sent by the Elk M1G panel. To use this service in VPL, you just drop it onto your diagram, configure it’s TestDataFileName via “Set initial configuration” to point to a data file, and send it a message to start playback. Important: If you have the Elk hardware, you will want to uncomment the SpawnIterator(ElkReader) line in the ElkService.Start method. I know this is a hack for now. It would probably be better to write a SimulatedElk service based on a generic service contract. If I have time, I will do that in the future. That would involve a lot more work than I had for this weekend project! TODO List Since this was my first foray into Microsoft Robotics Developer Studio and I had limited time to really explore the features in depth, there are a few things that I’ve left unimplemented. Here are some of the things that I would like to expand on in the future: 1. Support for more of the Elk features: Currently, I only support Zone Change notifications. Fuller support of the Elk hardware would not be difficult and would be beneficial to anyone wanting to use this on an ongoing basis. There’s some stub code for a few other notifications if you want to add to the service. 2. Generic contracts for the Elk M1G: I didn’t base the Elk services on generic contract because I’m not familiar with enough other types of security and automation panels to make the needed generalizations. It would be interesting to try to come up with something more generic. I thought about using the Generic Contact Sensor array, but I’m not sure that it models what we need. For example, a door contact is considered violated when it’s open, but a water sensor is considered violated when it’s closed. 3. Additional Z-Wave features: I’ve left out notification for lighting level changes, support for scenes and a host of other features. There’s so much more that’s possible with full Z-Wave support. 4. Refined Generic Contracts: Generic contracts allow developers to write code that is independent of the final hardware. That is one of the major benefits of the model used by Microsoft Robotics. You could for example have a generic light switch for simulation and testing, and based on configuration you could plug in a Z-Wave switch or an Insteon switch when it’s time to deploy the actual solution. 5. Simulation Support: One of the major benefits of Microsoft Robotics Developer Studio is the simulation environment. For example, if we had a motion detector in the simulation environment, we could test some of the algorithms that I presented above by having a simulated mobile robot “violate” the motion detector’s zone and trigger the corresponding VPL code. 6. Exception Handling: As I build more on the code in this article, I’ll add exception handling and retry logic. Microsoft Robotics has some extremely robust features such as Causalities that make handling exceptions in a concurrent environment much easier. 7. HttpHandlers: I have a start at code for handling HTTP requests in the ZWaveController service. Handling HTTP requests is handy for building client applications that access your services without themselves having to be DSS services. Other experiments that might be fun to try include integrating your Microsoft Robotics controlled home with remote control applications that are based on WPF or Silverlight, or perhaps integrating control from a Windows Media Center PC or Windows Home Server. I hope you enjoy Microsoft Robotics Developer Studio as much as I have. I look forward to seeing what other people build with this fantastic technology. If people are interested, I can continue to share my home automation experiments. I think Microsoft Robotics Developer Studio will be the major tool in my home automation toolbox for quite some time. Project Settings Note: You will have to modify the project settings for the projects included in the House.Robot solution since your installation of Microsoft Robotics Developer Studio will be in a different directory than mine. The DssProjectMigration.exe command line tool will make short work of this. Just run it with a parameter that specifies the directory you want it to search, and it will find all of the project files in that directory and subdirectories and convert them to be buildable with your installation settings. If you use the ElkTestDataPlayback service, in the examples, you’ll need to set the path to the location of the test data file either in the initial configuration for the component, or in a manifest that configures the component. Make sure that you do not put quotes around the path name. I included a test data file from my system. 
Author: Rob Miles: www.robmiles.com Download: http://coding4fun.net/source/festivelights1.0.zip Software: Visual Studio 2008 Express Edition or better, .NET Micro Framework 3.0 Hardware: Digi Connect-ME GHI Electronics Embedded Master Devices Solutions Tahoe II Time Required: 3 hours Cost: 30 dollars for lights plus the .NET Micro Framework device The Micro Framework is one of the newest kids on the .NET block, but it does something really rather wonderful. It brings embedded development within the reach of any C# programmer. If you know C# and love Visual Studio, you can now get started building hardware and controlling it with your software. Moreover, it lets developers achieve one of their most cherished dreams, to control their festive lights using programs that they have written. This project shows you how to do just that and adds an extra magical feature, in that you can make all your festive lights flash red whenever I, Rob Miles, make a new post on that most famous of blogs, www.robmiles.com. Actually, you can modify the code so that you can make your lights do most anything in response to an event that happens on the web. You could signal home that you are running late, send the weather forecast to your Christmas tree or explore any number of communication options. You might decide that this is so useful that you leave your decorations up all year round. If you just want to play with the .NET Micro Framework and get a feel for how easy it is to create software for tiny devices you don't actually need to use any extra hardware at all. The project comes with a complete emulation of the lights display so that you can run the whole thing on your computer and learn how hardware and software can be made to work together without burning your fingers with a soldering iron. However building the hardware will give you an understanding of how some simple electronic components can be controlled from C# and even how serial and parallel data transfer works. It is also great fun. To get started you will need some hardware and some software. Let's take each in turn. Hardware Processor Hardware The .NET Micro Framework lets you run C# programs on tiny embedded devices. There are a number of these available today, and they are getting progressively cheaper. You can base this project on any.NET Micro Framework device that has a network port and three or more output ports. The ones I'd recommend are: I built the project for Digi Connect board but the code can be customised for any of the above platforms. The beauty of the .NET Micro Framework is that you hardly need to change your program even if the underlying hardware is completely replaced. The only thing you will have to do is adjust the code to target different output pins. I'll flag this part of the program up when we get to it. The project as supplied runs on a special emulator that runs on the PC and behaves like a .NET Micro Framework device with lights connected, so you can get started exploring the code right away. Lights I'm keeping things deliberately low voltage for this project. This means that kids of all ages can have a go at building the hardware without messing around with mains. The lights that I used were supplied as lines of twenty leds wired up as four strands of five leds each. All the leds in each strand were the same colour. The lights were fitted with a little battery box which held three AA batteries and a tiny controller. To get my display I simply removed the battery box and connected the strands to my hardware. Figure 1: My Battery Powered Lights You can do the same with any low power lighting kit that you wish to use. The drivers that I'm using can handle around 500 milliamps of continuous current and so you can connect a fair number of lights to each strand. Buying the Lights The lights I used can be bought in the UK from Lights4Fun: www.lights4fun.co.uk and are called "C-LED-4.5-M 20 Multi Coloured Battery Operated LED Fairy Lights". They are supplied with a battery box and controller that you can remove to connect to the Darlington drivers. I used an old 5 volt mobile phone charger to power the lights. If you search eBay for "led christmas lights battery" you should find plenty of suppliers. Driver Hardware We can't connect a .NET Micro Framework device directly to our lights. There are two reasons for this: - The Micro Framework device will not be able to switch the amount of current that we need to drive the lights themselves.
- The Micro Framework device will not have enough outputs to control all the lights that we want to use.
This means that we need to make some hardware that goes between the processor and our lights. I actually don't see this as a problem. Making hardware is great fun, seeing something that you've built spring into life is very nice. The hardware we are going to use will provide a means by which we can control many hundreds of lights from a single Micro Framework board if we wish. It also has the ability to switch reasonable amounts of current, so you can use quite large numbers of lights if you wish. We are going to use two semiconductor components, a CD4094 Shift Register/Latch and a ULN2803 Octal Darlington Driver. A pair of these chips will allow us to control 8 outputs. If you want to control more outputs you simply get more chips and chain them together. For my version of the project I just used one of each chip, you can use as many as you like.The shift registers and Darlington amplifiers can be obtained in the UK from Maplin: www.maplin.co.uk. The chip numbers and part numbers as are as follows: QW54J 4094 Shift Register, QY79L ULN2803A Darlington Driver. In the US you can obtain the components and breadboard from Digi-Key: www.digikey.com. Serial and Parallel Data You might be wondering how we can use just three output lines to control lots of lights. We are going to do this by using the three output lines to provide a serial data stream which is converted by our hardware into parallel data that can be used to control our lights. This is a fundamental principle of digital electronics and is how, amongst many other things, computer networks transfer data. We are going to use three signals which are called clock , data and latch . Each of these can be set high (a voltage is present) or low (no voltage is present) by the .NET Micro Framework device under the control of our software. The signals are connected to the clock, data and latch inputs of our CD4094 shift register so that the program can talk to it. The clock line triggers the shift register to do two things: - Shift all the bits along to make room.
- Sample the value of the data input and store this value in the space that was created.
To get a feel for how this works, take a look at Figure 2 below. It shows a shift register with a pattern of bits in it. The pattern is 01100001 . Note that although this represents a number; it can also be regarded as a pattern of 0s and 1s in the shift register itself. The value 0 means 0 volts and the value 1 means some volts. These are the signals that will be used to control our lights. I'm going to call them 0 and 1 from now on. The Shift Register has Clock, Data and Latch signals connected and they are all set to 1. We can ignore the latch part of the chip for now. Figure 2: A shift register and latch with some data in it When the clock signal changes from 1 to 0 this causes the shift register to perform the two steps described above. First the data is shifted along to the right. Note that this means that there is an "empty" location at the start of the register, and that the right most bit in the register "falls off" the register and disappears. Figure 3 shows how this works. Figure 3: Shifting along the values in the shift register Once the shift has finished the shift register can copy the data signal into the empty bit at the left as shown in Figure 4 below. Figure 4: Storing the new data bit By repeating this process 8 times a program can load a new pattern of 8 bits into the shift register. Then it is time to latch the new value to control the lights. This is the point at which the lights will appear to change. When the latch value is changed from 0 to 1 this causes the chip to copy the value in the Shift Register into the latch, as shown in Figure 5 below. Figure 5: Copying the shift register pattern into the latch. The latch is required so that the lights will not flicker as the new patterns are shifted into position. Each of the bits in the latch is connected to an output pin on the CD4094 which is used to switch a particular light colour on or off. We need to create some C# that will provide the appropriate sequence of signals. It turns out that using the .NET Micro Framework to achieve this is actually very easy. The displayByte method below sends an 8 bit value into a shift register and then latches it into the output. If you read through the code you can see how the clock, data and latch values are all set to true (high) or false (low) to first clock the data out and then trigger the latch to display the pattern on the lights. The input is an 8 bit byte value and the program uses a mask to pick out the value of each bit in turn and set the data output accordingly. private static void displayByte( byte value ) { latchPort.Write( false ); clockPort.Write( false ); byte mask = 1; for ( int i = 0; i < 8; i++) { if (( value & mask) > 0) { dataPort.Write( true ); } else { dataPort.Write( false ); } clockPort.Write( true ); clockPort.Write( false ); mask <<= 1; } latchPort.Write( true ); latchPort.Write( false ); } The dataPort , clockPort and latchPort variables are instances of the .NET Micro Framework class OutputPort , which provides a method called Write which can be used to control the state of the output signal. We will consider how these are created a little later in the article. The sample code for this project comes with a software emulator of the CD4094 which shows how it works. Figure 6 below shows that a new pattern is in the process of being shifted into the shift register, while the lights retain the previous one in the latch. The Clock and Data signals are high and the next statement will drop the Clock signal to add the next bit into the new pattern. Figure 6: The light emulator This figure also shows that I have two bits in the pattern controlling a strand of each colour. You can single step through the displayByte method above and watch it perform this output. You can use this technique every time you want to control a large number of outputs using a small number of output pins. The CD4094 has "daisy chain" inputs and outputs so that the devices can be connected in sequence. If I used two devices I could control 16 bits, with three I could control 24 and so on. This would require only minimal changes to the software. Output Driver The CD4094 device will produce a signal output, but it is not really powerful enough to drive things like lights. To do this we need an amplifier and the ULN2803 Octal Darlington Driver is perfect for this. It is packaged as a single chip which contains 8 pairs of transistors. Each transistor pair is wired in a "Darlington" configuration and can be used as a switch which is controlled by on output from the CD4094. When the transistors are turned on they allow current to pass through them and this will cause the lamps to light. The lights that I bought used a "pull down" arrangement to make them light up. All of the light emitting diode (LED) lamps had one end wired to a common line that was connected to the positive supply. To make the a chain of LEDs light the other end that controls that chain needed to be pulled down to the ground level. This is a common arrangement with lights like these. The ULN2803 driver has the transistors wired in an arrangement that allows it to pull signals low in this way. Figure 7 shows how this arrangement works. The resistor shown is actually wired into each LED in the set of lights that I used. Figure 7: Lighting the LEDs Complete Circuit The complete circuit shown in Figure 8 simply links each output of the CD4094 shift register to an input on the ULN2803 Darlington Driver. Note that there is no reason to link any particular bit with any other, the diagram shown is one which will translate most easily to a prototype "breadboard". Figure 8: The Complete Circuit The diagram just shows four lights connected to OUT1 from pin 18 of the ULN2803, the other 7 channels are connected in exactly the same way. Some of the pins can be left unconnected. Pins 9 and 10 of the CD4094 are only required if you are connecting multiple shift registers together and the common connection on pin 10 of the ULN2803 is not required. The Latch, Data and Clock signals on the CD4094 are connected to the output signals from the Micro Framework device. Figure 9 shows the completed circuit built up on a prototype breadboard. The chip on the left is the CD4094 and the one on the right is the ULN2803. Figure 9: The Completed Circuit The red wires carry the positive voltage from the power supply, which is connected to the top left of the circuit. The green wires are ground. Other coloured wires are used for signals. The Clock, Latch and Data signals are brought out to a connector which will fit a Digi-ME prototyping board. Selecting Lights Each bit in the value sent to the displayByte method and then into the shift register will be mapped to a particular chain of lights. The mapping of these is not particularly important, since you can use program constants to represent particular values. I wired the red signals to output pins 4 and 8. To light up just the red lights I used the value 0x88 which is the appropriate bit pattern. I then set up constants for all the other colors: const byte GREEN = 0x11; const byte BLUE = 0x22; const byte YELLOW = 0x44; const byte RED = 0x88; Setting Up the Hardware The .NET Micro Framework provides a set of classes that can be used to represent the hardware in a system. The program uses instances of the OutputPort class to represent the output pins. These are created in the method that sets up the hardware: const Cpu.Pin clockPin = Cpu.Pin.GPIO_Pin0; const Cpu.Pin dataPin = Cpu.Pin.GPIO_Pin1; const Cpu.Pin latchPin = Cpu.Pin.GPIO_Pin2; static OutputPort clockPort; static OutputPort dataPort; static OutputPort latchPort; private static void setupOutputs() { clockPort = new OutputPort(clockPin, false ); dataPort = new OutputPort(dataPin, false ); latchPort = new OutputPort(latchPin, false ); } In this version of the hardware I have connected pin 0 of the processor to the clock, pin 1 to the data and pin 2 to the latch. If you use different pins you can change the settings above. Software Now that we have working hardware we can consider how the software is to work. Note that this version of the program works correctly but lacks exception handlers that would make it truly robust. I've left these out to simplify the explanation. To make sense of this description you will need to have a copy of the program itself available for reference. You can develop the code using Visual Studio 2008 Express edition, which you can download from http://www.microsoft.com/express/ The software is written for the .NET Micro Framework 3.0 which you can download from http://www.microsoft.com/downloads/details.aspx?FamilyID=9356ed6f-f1f0-43ef-b21a-4644dd089b4a&displaylang=en Reading the Blog The program reads the RSS feed from a blog and looks for the <pubDate> value. This contains the date when the feed was last updated. Whenever this date changes the lights must flash red for a few seconds before resuming a random display. Users of the full .NET Framework can use the HTTPRequest class to build a GET command to be sent to a server. Unfortunately the .NET Micro Framework does not support this, so we have to access the web feed using socket based communication. This part of the program is heavily based on the SocketClient example supplied with the .NET Micro Framework. // This method requests a page from the specified server. private static String GetWebPage(String server, string webPage) { const Int32 c_httpPort = 80; const Int32 c_microsecondsPerSecond = 1000000; // Create a socket connection to the specified server and port. using (Socket serverSocket = ConnectSocket(server, c_httpPort)) { // Send request to the server. String request = "GET " + webPage + " HTTP/1.1
Host: " + server + "
Connection: Close
" ; Byte[] bytesToSend = Encoding.UTF8.GetBytes(request); serverSocket.Send(bytesToSend, bytesToSend.Length, 0); // Allocate a buffer to receive HTML chunks Byte[] buffer = new Byte[1024]; // 'page' refers to the HTML data as it is built up. String page = String.Empty; // Wait up to 30 seconds for initial data // Will throw exception if connection closed DateTime timeoutAt = DateTime.Now.AddSeconds(30); while (serverSocket.Available == 0 && DateTime.Now < timeoutAt) { System.Threading.Thread.Sleep(100); } // Poll for data until 30 second time out // Returns true for data and connection closed while (serverSocket.Poll(30 * c_microsecondsPerSecond, SelectMode.SelectRead)) { // Zero all bytes in the re-usable buffer Array.Clear(buffer, 0, buffer.Length); // Read a buffer-sized HTML chunk Int32 bytesRead = serverSocket.Receive(buffer); // If 0 bytes in buffer, then connection is closed, // or we have timed out if (bytesRead == 0) break ; // Append the chunk to the string page += new String(Encoding.UTF8.GetChars(buffer)); } return page; // Return the complete string } } This method is called to fetch the journal RSS feed from my blog: string address = "www.robmiles.com" ; string name = "/journal/rss.xml" ; string html = GetWebPage(address, name); You can use it to download from any RSS feed or page on the web. It will throw an exception if the page cannot be read. This version of my program does not perform exception handling however. Getting the Published Date The date is held in the form: < pubDate > Wed, 05 Nov 2008 22:38:52 +0000 </ pubDate > It would be enough just to record this content and check for changes in the text, but I decided that I might want to use the date information in a later version of the program and so I created some small helper methods to read numbers from the input string and a larger method to read the publish date itself: private static DateTime getRecentPubDate( string html, string startTag) { int index = html.IndexOf(startTag) ; if (index < 0) throw new Exception( "Missing tag " + startTag); index += startTag.Length; // spin past the name of the day while (index < html.Length && html[index] != ',' ) index++; if (index == html.Length) throw new Exception( "Short publish date" ); int dayValue = getInt(html, ref index, ' ' ); string monthName = getString(html, ref index, ' ' ); int monthValue = getMonth(monthName); int yearValue = getInt(html, ref index, ' ' ); int hourValue = getInt(html, ref index, ':' ); int minuteValue = getInt(html, ref index, ':' ); int secondValue = getInt(html, ref index, ' ' ); return new DateTime(yearValue, monthValue, dayValue, hourValue, minuteValue, secondValue); } The main body of the program uses this method to extract the date out of the RSS feed. When a new date is found it is time to update the lights. Lights and Threads My first version of the program flashed the lights for a while and then checked to see if a new blog post had been made. This worked OK, but the process of loading the RSS feed from the server and checking the dates can take a few seconds, which meant that the flashing lights would freeze every now and then. This did not look very good, and so I decided to use two threads instead. One is in charge of flashing the lights in a random pattern and the other loads the RSS feed from my blog and checks the date of the most recent publication. Note that the threading I am using is exactly the same as threading in the full .NET Framework. Thread Communication The two threads communicate by means of a single boolean variable which is set to true when the blog alert is to take place. The display thread reads this flag and flashes the lights red if it is time to alert. public static bool alert = false ; public static void flasher() { setupOutputs(); while ( true ) { randomDisplay(400, 10); if (alert) { alert = false ; flashRed(600, 20); } } } The methods randomDisplay and flashRed do exactly what you would expect. Each of them gets two numbers to control the lights. The first number gives the delay in milliseconds between flashes (a few hundred milliseconds giving best results). The second gives the number of times that the lights should be flashed before the method finishes. RandomDisplay displays random colors, whereas flashRed is simply flashes the red lights. The alert flag is cleared before the red lights are flashed so that the system will resume normal display after the alert. The second thread in the system performs the blog download and date test behaviour. This all happens inside the Main method: public static void Main() { flashThread = new System.Threading.Thread(flasher); flashThread.Start(); string address = "www.robmiles.com" ; string name = "/journal/rss.xml" ; String html = GetWebPage(address, name); DateTime lastUpdate = getRecentPubDate(html, "<pubDate>" ); Debug.Print( "Initial Update Value : " + lastUpdate.ToString()); while ( true ) { System.Threading.Thread.Sleep(10000); html = GetWebPage(address, name); DateTime blogUpdate = getRecentPubDate(html, "<pubDate>" ); if (!blogUpdate.Equals(lastUpdate)) { Debug.Print( "Updated at : " + blogUpdate.ToString()); lastUpdate = blogUpdate; alert = true ; } } } To reduce the load on the network the system only checks the blog feed every 10 seconds. Future Work The program itself works fine, but the error handling is not wonderful. Although some of the methods used throw exceptions these are not caught anywhere, causing the blog reading thread to terminate. This does not stop the lights flashing, but it does mean that there will be no more alerts. However, it is not too hard to create a version which contains proper error handling and even flashes the lights different colors to indicate the alarm conditions. I have created a version that flashes the lights yellow every now and then if the network connection fails. There is also considerable scope for reading other web based sources and changing the output accordingly. Feel free to do all these things and make sure that you have fun. Thanks Thanks go to Ian Mitchell of Ormston Technology (http://www.ormtec.co.uk/) for doing such a great job of hardware design and build. 
Author: Clint Rutkas: twitter, blog Download: http://peacelovecode.com/code/cowbell/cowbell.zip Software: Visual Studio 2008 Standard or better (Trial Download) Windows Mobile 6.0 SDK Windows Mobile 6.1 Emulator Images (Optional) Hardware: Windows Mobile Pro (Optional) Time Required: 2 hours Cost: Free-ish After playing way too much guitar hero, bought a guitar only to quickly realize I’m horrible. Asking my friends, I decided to adopt the world’s hardest instrument to play, that’s right, the cow bell. It takes years to master and I decide to turn my cell phone, an HTC Touch Diamond into this magical instrument too. This is a fairly straight forward application. We’ll touch on altering the volume of a cell phone along with interacting with a touch screen and how to do a deploy for Windows Mobile so you can have a cowbell band of your own. The application will also properly resize an image to match the screen proportions, however you’ll have to download the source code for that. You’ll see while getting your system up and working with the emulator may take a few additional steps, it will code mostly like a typical windows application for this instance. The source code shown isn’t everything you need to get the application running, please download the source code to get the rest. The link is at the top of the article. Getting up and running with baby steps After you get Visual Studio 2008 up and running, if you don’t have the Windows Mobile 6.0 SDK, please download it and install it. The link is at the top of this article. Visual Studio 2008 only comes with the Windows mobile 5.0 SDK. On the right is a picture of the emulator in one of styling forms. On top of just being able to test on an emulator, you can do a direct deploy to your cell phone. Now a Windows Mobile Professional phone can do this directly, however a Windows Mobile Standard (non-touch screen) does require a few extra steps. I blogged about these steps over on my blog. After your phone is properly configured, all you need to do is change your target deployment. I highlighted the device deploy instead of the emulator. Smart Device Creation To create a new solution we go to File –> New –> Project. We’ll select the language of our choice (c# for me) then go to “Smart Device” Easy so far, right? Next comes selecting the framework along with your target platform. Select “Windows Mobile 6 Professional SDK” then click OK and you’re good to start. Altering Volume To alter your volume currently in the compact framework, you have to do some pinvokes to call native API calls. This is scary for some but don’t fret. There is a site called pinvoke.net that makes doing these calls simple as pie. We’ll create a new class called Audio to encapsulate all audio functions. This includes playing a file and altering the volume. First we’ll do the simple ability to play an audio file. Since we’ll be grabbing our data from the resources, we’ll use a MemoryStream object convert the bytes to a stream so we can read it in. We’ll need to include the System.Media namespace to be able to play audio. To do the pinvokes, we’ll include the System.Runtime.InteropServices. C# private readonly SoundPlayer sp; public Audio( byte [] AudioResource) { sp = new SoundPlayer( new MemoryStream(AudioResource)); sp.Load(); } public void Play() { sp.Play(); } public void PlayLoop() { sp.PlayLooping(); } public void Stop() { sp.Stop(); } VB Private ReadOnly sp As SoundPlayer Public Sub New ( ByVal AudioResource As Byte ()) sp = New SoundPlayer( New MemoryStream(AudioResource)) sp.Load() End Sub Public Sub Play() sp.Play() End Sub Public Sub PlayLoop() sp.PlayLooping() End Sub Public Sub [ Stop ]() sp.[ Stop ]() End Sub And in the application, we’ll call the audio file like the following: c# private readonly Audio audio = new Audio(Properties.Resources.CowBellAudio); VB Private ReadOnly audio As New Audio(My.Resources.CowBellAudio) Next we’ll add in the ability to alter the volume with the pinvokes. Sadly, I’m not sure why the set volume numbers act as they do. After searching the internet, these numbers kept appearing. In a perfect world, I would have liked to see 0 to 100 or something like that but it isn’t so. We’ll create get / sets in the wrapper to make a more seamless wrapper object to hide the pinvoke calls. C# [DllImport( "coredll.dll" )] private static extern int waveOutGetVolume(IntPtr hwo, out uint dwVolume); [DllImport( "coredll.dll" )] private static extern int waveOutSetVolume(IntPtr hwo, uint dwVolume); public static uint Volume { get { uint _volume; waveOutGetVolume(IntPtr.Zero, out _volume); return _volume; } set { waveOutSetVolume(IntPtr.Zero, value ); } } public enum Volumes { OFF = 0, LOW = 858993459, NORMAL = 1717986918, MEDIUM = -1717986919, HIGH = -858993460, VERY_HIGH = -1 } VB Declare Function waveOutGetVolume Lib "coredll.dll" ( ByVal uDeviceID As Integer , ByRef lpdwVolume As Integer ) As Integer Declare Function waveOutSetVolume Lib "coredll.dll" ( ByVal device As IntPtr, ByVal volume As Integer ) As Integer Public Shared Property AudioVolume() As Integer Get Dim _volume As Integer waveOutGetVolume(IntPtr.Zero, _volume) Return _volume End Get Set ( ByVal value As Integer ) waveOutSetVolume(IntPtr.Zero, value) End Set End Property Public Enum Volumes OFF = 0 LOW = 858993459 NORMAL = 1717986918 MEDIUM = -1717986919 HIGH = -858993460 VERY_HIGH = -1 End Enum Creating the Cowbell Pretty Pictures and sounds This is a cowbell application so we need an actual cowbell on it. After a quick image search, I found the cowbell image on the internet and the X is from the Famfamfam Silk icon collection. We’ll want to embed these images to make deployment easier and have everything self-contained so we’ll use the resource in the project’s property tab. To reach this, you go to Project->Properties or right click on your project and at the bottom, there is the Properties menu option. In here, we’ll want to use the Images section along with the audio section. When you select the section you want, click the Add Resource button and track down the file you need. Referencing these resources is pretty straight forward now but each language has a slightly different way to do it. C# Properties.Resources.cowBellImage; VB My.Resources.cowBellImage Easy no? Items on the form We’ll want to turn off some elements on the form to maximize the cowbell. We’ll set the WindowState to Maximum, TopMost to True, MinimizeBox to False and FormBorder to None. We’ll drag on two picture boxes on the form naming them picCowbell and picClose. We’ll set the background to both elements to be transparent. We’ll also want to double click on each picture boxes to create a Click event. For the picClose element, we’ll add in the Close() method to shut down the application. We’ll also create a MouseDown event on the picCowbell. Here we’ll determine how loud to play then play the sound. We’ll create the Audio object during the form creation so it is already loaded into memory and will only be created once. C# private readonly Audio audio = new Audio(Properties.Resources.CowBellAudio); private void picCowbell_MouseDown( object sender, MouseEventArgs e) { Audio.Volumes value ; switch (( int )Math.Round((e.Y / (Height * 1.0)) * 5)) { //case 0: // implied with default; //case 1: default : value = Audio.Volumes.LOW; break ; case 2: value = Audio.Volumes.NORMAL; break ; case 3: value = Audio.Volumes.MEDIUM; break ; case 4: value = Audio.Volumes.HIGH; break ; case 5: value = Audio.Volumes.VERY_HIGH; break ; } // no need to take the performance hit of changing the volume // if the values aren't different if ( oldValue != value ) { Audio.Volume = ( uint ) value ; oldValue = value ; } audio.Play(); } VB Private ReadOnly audio As New Audio(My.Resources.CowBellAudio) Private Sub picCowbell_MouseDown( ByVal sender As System. Object , ByVal e As System.Windows.Forms.MouseEventArgs) Dim value As Audio.Volumes Select Case Convert.ToInt32(Math.Round((e.Y / (Height * 1)) * 5)) Case 2 value = audio.Volumes.NORMAL Exit Select Case 3 value = audio.Volumes.MEDIUM Exit Select Case 4 value = audio.Volumes.HIGH Exit Select Case 5 value = audio.Volumes.VERY_HIGH Exit Select Case Else 'case 0: // implied with default; 'case 1: value = audio.Volumes.LOW Exit Select End Select ' no need to take the performance hit of changing the volume ' if the values aren't different If oldValue <> value Then audio.AudioVolume = Convert.ToInt32(value) oldValue = value End If audio.Play() End Sub Hailing a CAB for deployment We’ll right click on the solution in the solution explorer or we can go to File->New->Project. If you do the file menu route, you’ll have a screen that is slightly different than mine. At the bottom you’ll have a drop down menu where you’ll want to change it from “Create a new solution” to “Add to Solution”. Since I want a CAB install, I’ll go to the “Other Project Types” then to “Setup and Deployment” and select a Smart Device CAB Project”. From there, right click on “Application Folder” and go to “Add”. Select “Primary Output” and hit OK. So with the output added in now, we’ll need to do some more magic. In the “File System” window, we’ll right-click on “File System on Target Machine” and go “Add Special Folder” then to “Start Menu Folder”. Now that we have our start menu, we’ll go back to the “Application Menu” and right click on the “Primary output from Cowbell”. Select “Create Shortcut” and drag that shortcut to your start menu folder. I suggest you rename it too. Then you can build it, drag the CAB over and install on your device. Rocking out To download the application’s full source code, please download it from http://peacelovecode.com/code/cowbell/cowbell.zip About the Author Clint Rutkas works for Microsoft and has created a few cool projects in the past like a Disco Dance Floor, an automated bartender system and a self-balancing skateboard. His blog is http://BetterThanEveryone.com where he posts about what crazy application idea he is working on next. 
Have you ever been interested in creating a game that harnesses XNA’s powerful network library to create multiplayer experiences for the Zune device? The Zune firmware version 3.1 brought us a professionally built incarnation of Texas Holdem that supports network play. Understanding how to send and receive data with the Zune can be a little daunting at first, but once you understand the pattern, it’s easy. To build out the entire game, you probably need about a week, but you can build some simpler examples in far less time. This is an earlier project from before the release of my Zune game development book. Accordingly, some of the code samples you see in this article may be inconsistent with what you find in the download. The code in the article is the “correct” way to do things. The code in the download is still a work in progress. The Workflow Developing multiplayer games for the Zune is interesting because you have to deploy to each device individually. Once you have a build that works for you, it’s helpful to run-deploy (Control+F5) to one device, leave it running there, and then plug in the other Zune and debug-deploy (F5) to it. This way you have one debuggable instance of the game running. Make sure to set the appropriate Zune device as the default in the XNA Game Studio Device Center (accessible from the Tools menu). Starting A Network Session Because the Zunes connect over an ad-hoc, peer-to-peer connection rather than through an access point, you will have to designate one Zune as the host device. The host is usually determined to be the one that creates a new game. Therefore, all Zunes that join the host’s network session are simply peers. The difference between the host and the peers is that the host usually maintains the game state on top of executing the game as well, because the game data has to be centralized somewhere. Keep that in mind, because if one Zune is doing substantially more processing, it can lag behind and mess up your network session. Also, remember that all Zunes are running the exact same copy of the game, so the game must support both host and peer scenarios. Create / Join / Lobby Model Most peer-to-peer connected games allow a user to create or join a game. After doing so, the player is funneled into an area called the Lobby where they can specify their readiness. When all players are ready, the host can start the game. Some of this functionality is provided directly by the XNA Framework’s Net and GamerServices libraries. I normally create three separate screens based on the Network Game State Management sample from the Creators Club website. The first is the Create screen, which looks exactly like a lobby. It starts up a network session and waits for players to join and become ready. The code to create a network session looks like this (note that I have employed some abstraction to make my code a little more cohesive): C# public void CreateZuneSession( int maxNetworkPlayers) { KillSession(); try { Session = NetworkSession.Create(NetworkSessionType.SystemLink, 1, maxNetworkPlayers); Me = Session.LocalGamers[0]; } catch (NetworkNotAvailableException) { throw new NetworkNotAvailableException( "Zune wireless is not enabled." ); } catch (NetworkException ne) { throw ne; } if (Session == null ) throw new NetworkException( "The network session could not be created." ); } All Zune network sessions are of the type SystemLink, much like LAN-networked Xbox consoles. The ‘1’ parameter specifies the number of local players – of course, on a Zune, there can only be one. This line of code sets the Session property to a newly created network session. The Join screen, running on another Zune, will find this session asynchronously as an available network session and attempt to join it. The first step is to enumerate available network sessions: C# /// <summary> /// Asynchronous: Begins to discover network sessions. /// </summary> public void BeginGetAvailableSessions() { // Destroy any existing connections KillSession(); NetworkSession.BeginFind(NetworkSessionType.SystemLink, 1, null , new AsyncCallback(SessionsFoundCallback), null ); } This code looks for SystemLink sessions and calls the callback method SessionsFoundCallback when the operation completes (successfully or unsuccessfully). If sessions are found, an event is fired. Other screens can subscribe to this event and transition to other screens or do other processing with the network session. C# /// <summary> /// Called when network sessions are found. /// </summary> /// <param name="result"></param> public void SessionsFoundCallback(IAsyncResult result) { AvailableNetworkSessionCollection availableSessions = null ; availableSessions = NetworkSession.EndFind(result); if (NetworkSessionsFound != null ) NetworkSessionsFound(availableSessions); } When all players are ready and the host presses the middle button, the network session’s StartGame() method is called, which will cause all connected peers to receive a GameStarted event. This loads up the playing screen. C# if (allPlayersReady && atLeastTwoPlayers) { if (ScreenManager.Network.Session != null ) ScreenManager.Network.Session.StartGame(); } That’s basically how to connect up two Zunes in a network session. See my book for a much more detailed explanation. Dealing With Cards As Poker is a card game, you might want to develop a testable, standalone library that you can use not only to house your objects, but also to write out all the intense logic required for a game like poker. The task of determining what a player’s best hand is out of seven cards (and whether it beats another player’s hand) is more in-depth than you might expect. An easy way to go about this is to create a Zune game library. I called mine CardLib. CardLib has objects such as these: - Card which has a suit, a value, some comparers and some utility methods)
- Deck, which has a collection of cards and methods like Shuffle, ResetDeck, etc.
- Dealer, which has a deck, a list of the five community cards, and methods like Shuffle, DealCard, Burn, DealFlop, DealTurn, etc.
- HoldemHand, which contains all of the logic for determining what a hand is (always 7 cards)
- BestHand, which takes a HoldemHand and determines what the best possible hand is within that hand
Shuffling Cards Shuffling cards is surprisingly easy. I use the Knuth-Fisher-Yates shuffling algorithm, which takes every card in the deck and randomly swaps it with another. C# /// <summary> /// Knuth-Fisher-Yates shuffling algorithm: /// http://www.codinghorror.com/blog/archives/001015.html /// </summary> public void Shuffle() { Random rand = new Random(); for ( int cardIndex = _cards.Count - 1; cardIndex > 0; cardIndex--) { int randomIndex = rand.Next(cardIndex + 1); SwapCardsByIndex(cardIndex, randomIndex); } _dealIndex = 0; } Poker Logic An example method you’ll see in this library is this snippet to check if the hand is a royal flush: C# public bool IsRoyalFlush( out List<Card> winningCards) { List<Card> straightFlushCards; if (IsStraightFlush( out straightFlushCards)) { // Check to make sure the straight flush cards are: 10 J Q K A straightFlushCards.Sort(); if (straightFlushCards[0].Value == 10 && straightFlushCards[1].Value == Card.Jack && straightFlushCards[2].Value == Card.Queen && straightFlushCards[3].Value == Card.King && straightFlushCards[4].Value == Card.Ace) { winningCards = straightFlushCards; return true ; } else { winningCards = null ; return false ; } } else { winningCards = null ; return false ; } } There is a ton of similar and even more disgusting logic to peruse in the HoldemHand.cs file in the region entitled “Poker Logic Fun Times.” This logic covers every possible poker hand and could probably be refactored to be way more elegant. When it comes time to evaluate the winner, each player’s best hand is determined and they are evaluated against each other to determine the winner. This code returns a list of WinnerInfo objects, which returns the player info and a list of the cards they won with. C# public List<WinnerInfo> DetermineWinners() { BestHand overallBestHand = null ; HoldemPlayer winner = null ; List<WinnerInfo> winners = new List<WinnerInfo>(); foreach (HoldemPlayer player in _players) { if (player.Status != PlayerStatus.Folded) { HoldemHand tempHand = new HoldemHand(player.Pocket, _communityCards); BestHand bestHand = tempHand.GetBestHand(); if (overallBestHand == null ) // if there is no best hand yet { overallBestHand = bestHand; winner = player; winners.Add( new WinnerInfo(overallBestHand, winner, Me)); } else { // if this hand beats the current best hand if (BestHand.Beats(bestHand, overallBestHand)) { winners.Clear(); overallBestHand = bestHand; winner = player; winners.Add( new WinnerInfo(overallBestHand, winner, Me)); } else { if (BestHand.IsEquivalentTo(bestHand, overallBestHand)) { winners.Add( new WinnerInfo(bestHand, player, Me)); } } } } } _winners = winners; return winners; } Drawing Cards On The Screen Rather than have 52 different sprites that represent each possible card, I just made my own sheet of Zune-sized playing cards. Horizontally, they are ordered by value (1-13) and vertically they are ordered by suit (both alphabetically and corresponding to the numeric value of the Suit enumeration in the project, 1-4). When a specific card is requested to be drawn, a formula is used to calculate the source rectangle from this larger image. This is very similar to how fonts worked before spritefonts were introduced into XNA. This allows us to strip out the graphic of a specific card and draw it onscreen at some other location. C# private void DrawCard(Card card, Vector2 position) { if (card != Card.Undefined && card.Suit != Suit.Unassigned) ScreenManager.SpriteBatch.Draw(_texDeck, position, GetCardSourceRect(card), Color.White); } private Rectangle GetCardSourceRect(Card card) { if (card.Suit == Suit.Unassigned) throw new ArgumentException( "Unassigned cards cannot be drawn." ); int cardColumn = card.Value - 1; int cardRow = ( int )card.Suit - 1; int x = cardColumn * MainScreenElements.CARD_WIDTH; int y = cardRow * MainScreenElements.CARD_HEIGHT; return new Rectangle(x, y, MainScreenElements.CARD_WIDTH, MainScreenElements.CARD_HEIGHT); } Managing Network Data One thing that increases the complexity of this game (in no small way) is the management of network data. First of all, the host’s actions are a superset of the peer’s actions (a host is also a peer, but it also has to manage the game and network state). For example, when a peer decides to bet, it has to send a message to the host saying “I would like to bet,” and then the host will process that message and relay it to the other peers. The host is also responsible for managing whose turn it is and determining who the winner is. Theoretically, you could do a lot of this with every peer acting equally but it feels safer to me to have the host responsible for important activities like dealing cards. In fact, the host has to be the only one who can deal cards, because if each client maintained its own deck, it would be randomized for every peer when the deck is shuffled. How Data Is Sent And Received I will usually create a static class called NetworkMessageSender that is responsible for sending various messages. I keep an enumeration of type byte that holds the possible network messages. I always send the byte indicating the message type first, so that when the peer receives a byte, it knows how to respond. For example, if the peer receives a CardDealt message, it can pop off a string and a card from the incoming packets. If the card is not intended for the peer, it can simply discard the message. Data you want to send is written to a packet writer object. When the data is ready to be sent, you call the SendData method of the LocalGamer object. Depending on how important packet receipt is, you can specify the type of transmission. I use ReliableInOrder during poker because network data is exchanged relatively infrequently. Although there is only one local network gamer, you can use a foreach loop to ensure this code will work on other platforms. C# private static void SendData() { foreach (LocalNetworkGamer gamer in NetworkSessionManager.Session.LocalGamers) gamer.SendData(NetworkSessionManager.PacketWriter, SendDataOptions.ReliableInOrder); } Specific chunks of data are then sent using various static methods. Each piece of information is written sequentially. The card is serialized into a string format before being sent. The following chunk of code is used to send a card to a player. C# public static void DealCards(HoldemPlayer player) { NetworkSessionManager.PacketWriter.Write(( byte )NetworkMessageType.Deal); NetworkSessionManager.PacketWriter.Write(player.Name); NetworkSessionManager.PacketWriter.Write(player.Pocket.Card1.Serialize()); NetworkSessionManager.PacketWriter.Write(player.Pocket.Card2.Serialize()); SendData(); } On the other side of that, whenever the screen updates, the game is constantly checking for new network data. The following code is part of a method that is called from the game screen’s Update loop. C# private void UpdateNetworkSession() { NetworkSessionManager.Update(); foreach (LocalNetworkGamer localGamer in NetworkSessionManager.Session.LocalGamers) { while (localGamer.IsDataAvailable) { NetworkGamer sender; localGamer.ReceiveData(NetworkSessionManager.PacketReader, out sender); // Interpret the first piece of information, the message. NetworkMessageType message = (NetworkMessageType)NetworkSessionManager.PacketReader.ReadByte(); // Determine what to read and what to do based on this message type switch (message) { // major snippage case NetworkMessageType.Deal: // Happens when a card is dealt { string playerName = NetworkSessionManager.PacketReader.ReadString(); string card1 = NetworkSessionManager.PacketReader.ReadString(); string card2 = NetworkSessionManager.PacketReader.ReadString(); _gameplayManager.Players[playerName].Pocket.Set(PlayerPocket.FIRST_CARD_INDEX, new Card(card1)); _gameplayManager.Players[playerName].Pocket.Set(PlayerPocket.SECOND_CARD_INDEX, new Card(card2)); } break ; } } } } There is a whole bunch of similar code that underlies how the game executes. When a message needs to be sent, the NetworkMessageSender class is used. To determine what happens when a message is received, we look at that ginormous switch statement that gets called in the screen update loop. Remember that the host is also a peer and it will receive the very same messages it sends (unless you specify otherwise). Be careful not to double-process messages if they are sent by the host. In some cases you will need to check that the current device is or is not the host. In the sample download, this is achieved just by checking a Boolean value that is set early on in the game. Conclusion Building Poker for the Zune, from the ground up, is no small feat! Networked Zune games are far simpler when you have a much more limited set of possible data and messages that could be sent. For example, networked Pong, Battleship or Tetris would be pretty easy compared to poker. Turn-based games on the Zune also provide an interesting challenge in terms of what happens when a Zune drops from the network session. This is just a small dip into networked Zune game development. For a deep dive, check out my book, Zune Game Development using XNA 3.0, also available on Apress.com as an eBook. The final chapter is a sprawling 120 pages covering how to build Crazy Eights for the Zune from the ground up. About The Author Dan Waters is an Academic Developer Evangelist at Microsoft, based in Tampa, FL. When he’s not out showing the latest and greatest MS technology to students and faculty, he’s spending time with his wife and young daughter or rocking out on one of his (far too numerous) guitars. Follow Dan on Twitter or check out his blog. 
|
|
Discount Computer and Hardware Interface Books (US FREE Shipping)
 
PC Hardware Interfaces: A Developer's Reference save 18%
 
USB Complete: Everything You Need to Develop Custom USB Peripherals save 37%
 
Serial Port Complete: COM Ports, USB Virtual COM Ports, and Ports for Embedded Systems (Complete Guides series)
See all Computer interface Books
|