Writing RDS Services for the LEGO NXT

RDS includes several services for the LEGO NXT. The basic infrastructure for communicating with the brick via Bluetooth is provided by the Brick and Comm services. To add support for a new sensor, you only need to write the code to handle the sensor and let the LEGO NXT services do the rest.

Before you can create a new service, you need to find the programming details for the sensor or controller. This information for HiTechnic sensors can usually be found on the HiTechnic web site. There are two fundamentally different types of sensors for the LEGO NXT: Digital or Analog. See the Sensor Types page for an explanation.

The steps that follow might seem complicated at first, but they are actually quite straightforward if you understand RDS services and it gets easier with practise. However, this procedure is not recommended for beginners. The infrastructure provided by RDS makes it relatively easy to create new LEGO services. The most complicated part is the XSLT file for displaying the service state as a web page.

Clone the Service

You might wonder whether cloning a service or creating a new one is the easiest way to go, especially as RDS does not have a cloning tool but it does have a tool to create a new service. In the case of the HiTechnic sensors, there is a lot of code that is the same for every sensor and you would probably copy and paste this code anyway. So cloning is a reasonable approach, i.e. the simplest way to create new services for the LEGO NXT is to copy an existing service.

Once you know whether the new sensor is Digital or Analog, you can select an appropriate service to copy. You can use one of the services in this project as a template. (Alternatively, you can use one of the original LEGO NXT services are in the RDS folder samples\Platforms\LEGO\NXT\V2).

NOTE: It is recommended that you do not directly modify the solution that you downloaded from this CodePlex site. Doing so will add your new service into the same DLL and you will not be able to easily take advantage of any future updates on this site.

Copy the Visual Studio solution to a new folder. You do not need to create a DSS service using the Wizard.

Run DssProjectMigration on the new solution folder to make sure that the paths in the solution and project files are correct. To do this, open a DSS Command Prompt. (You can find it in the Start Menu). Then enter the command:
DssProjectMigration /b- SolutionFolder

If you are a perfectionist, you can rename the solution and project files, but then you have to edit them and make sure you change all references to the solution/project name and the corresponding filenames. This is not difficult because the .sln and .csproj files are just XML files. However, if you have never done it before it is a little error-prone and you can break your solution so that it will not load in Visual Studio. Make this change with caution.

Choose a name for your new service. Open the solution in Visual Studio, and rename an existing set of source files making sure that you have selected a Analog or Digital service as appropriate:
  • Main service source file (service.cs)
  • Types file (servicetypes.cs)
  • Manifest file (service.manifest.xml)
  • Corresponding XSLT file in the Transforms folder
  • Relevant image files (service.Image.png and service.Thumbnail.png) in the Resources folder.

Delete all of the other source files and images from the solution (unless you want to use them for other sensors).

Change the Service Details

You should now have a solution that contains a single service. Do NOT compile it yet!

Open the Project Properties and on the Application tab change the Assembly (DLL) Filename and the Default Namespace to use your new service name.

Project Properties - Application

NOTE: Although the namespace in the diagram above says "Microsoft.Robotics", you should really insert your own organization name.

On the Debug tab, change the properties to run your new manifest. See the example below that uses Gyro.manifest.xml.

Proejct Properties - Debug

In the main source file, change the namespace, the class name of the service, etc. You need to change all of the places where the old service name was used. You can do this using a find and replace, but you need to be a little careful. Also, there are interdependencies between the service.cs and servicetypes.cs so Intellisense will not work on data types. You might want to change servicetypes.cs first (see next section), then come back to service.cs so that all the types are defined.

The following items need to be changed. You can use this as a checklist.
  • namespace
  • class name
  • Config filename (in the InitialStatePartner attribute)
  • Data type for the Service State (variable _state)
  • ServicePort attribute
  • Data type for the Main Operations port(s) (variables _internalMainPort and _reliableMainPort)
  • EmbeddedResource attribute
  • Method name for the class constructor

The EmbeddedResource attribute specifies an XSLT page that is used to display the service state in a web browser. The format of the resource name is:
DefaultNamespace.Folder.Filename

For example, the Gyro sensor has a resource name of Microsoft.Robotics.Services.Sample.HiTechnic.Transforms.Gyro.xslt and the XSLT file resides in the Transforms folder under the solution. If you do not get this correct, the XSLT file will not be found and you will get an error message when you try to view the service in a web browser. You do not need a working XSLT page to use your service. See the bottom of this page for more information.

The following items are "cosmetic", but should be changed because they document the service. They are just above the class declaration.
  • Description attribute
  • DisplayName attribute
  • DssServiceDescription attribute

The Description and DisplayName are important for VPL.
The DssServiceDescription is the URL of a documentation page for the service on the Internet. In VPL, a small "i" is displayed beside the service in the Service List to indicate that information is available. If you don't have a doc page, comment it out.

Change the Service Types

In the servicetypes.cs file, change the following:
  • namespace
  • Identifier in the Contract class
  • DeviceModel variable
  • Fields in the Contract class as appropriate
  • Service State class name
  • Main Operations port class name and constructor method name
  • Any other affected classes (Get, Update, etc.) that refer to the State

It is very important to change the Contract Identifier. If you forget to do this, you will "overload" an existing service with the same identifier and strange things will happen if you try to use the service because DSS will not be able to resolve to the correct assembly.

Fortunately, if you have made the changes listed above you can use the compiler to find any remaining references to data types that you have missed.

Also change all of the Description attributes as appropriate. (There are a lot of these!) These descriptions appear as tooltips in C# and VPL so you should make an effort to ensure that they are correct and provide useful information.

Update the Digital Sensor Classes

If the sensor is a Digital sensor, then there will be two important classes that need to be updated based on the programming information for the sensor. They are sub-classed off the LegoLSWrite and LegoResponse classes in the LEGO NXT services. The LegoLSWrite class defines the command that will be sent to the LEGO brick to read data, and the LegoResponse defines the format of the resulting response.

For the class derived from LegoLSWrite (Low Speed Write), you need to supply the appropriate I2C Bus Address (usually 0x02) and the Data Register Address (usually 0x42). (For consistency, I have added these values as fields in the Contract class. This is a small abuse of the Contract class, but it keeps the info together in one place and allows you to refer to the values by name instead of having "magic" values in the code. If you update the values in the Contract class, you should not need to modify any code.) You also have to specify the ExpectedI2CResponseSize (which is the DataLength field in the Contract class). This is the number of bytes of data you want to read from the sensor.

For the class derived from LegoResponse, you need to create a set of fields and get/set methods to extract the relevant data. It is impossible to generalize this because it depends on the contents of the sensor's registers. You should be able to follow the pattern from what is already there. Note that the data you want starts at offset 4 in CommandData.

Depending on the sensor, there might be several enums at the bottom of the file that are used to define sensor commands.

Update the Initialize Method

Back in the service.cs file, locate the Initialize method. This is used to create a new Service State instance (if the config file cannot be found or fails to load) and to initialize fields in the state (because the config file might contain stale data). The process is quite easy to understand, but you will have to check carefully that all of the fields are accounted for. In particular make sure that you new any fields like lists or arrays if they are null.

Notice in the code that SaveState is called if the state was null initially. This is very useful for creating an initial config file. (See below).

Update the ConnectToBrick Handler

Find the ConnectToBrick handler and change it as appropriate for the sensor.

The code contains various references to fields in the state. Make sure that these are appropriate.

In the AttachRequest make sure that the InitializationCommands and PollingCommands are set up correctly. You will need to understand how the sensor works to enter the correct commands. The commands that you use are different for Analog and Digital sensors.

The ColorV2 sensor service shows how you can build up a set of initialization commands that leave out any default values. In this case, the reason for doing so is that these values are written to EEPROM every time they are set and there is a limited number of erase/rewrite cycles for the EEPROM so it is best to avoid setting values every time.

The polling commands will usually consist of just a single command. For Analog sensors, this is a LegoGetInputValues and for Digital sensors it will be based on LegoLSWrite (from the types file).

There are several strings in this method that need to be updated to refer to the correct sensor. The service will work if these are not updated, but the error messages will be confusing!

Update the Notifications Handler

Now find the NotificationHandler and change it as appropriate.

For an Analog sensor, the message body contains a RawValue which will be from 0 to 1023 (from the 10-bit A/D converter). You need to transform this value into something useful in appropriate units, e.g. degrees, temperature, etc.

For a Digital sensor, there will be several fields in the message body. The various fields are defined in the LegoResponse class that you updated earlier. How you process them is up to you.

Note that it is good practise not to send notifications unless something has changed. (The NotificationHandler will be called every PollingFrequencyMs milliseconds, unless the commands back up because you are polling too fast). This can be a dilemma. For instance, the RGB values of the Color Sensor often flucuate without changing the Color Number. If you want to see any changes in the RGB values, you can send notifications to subscribers on every update. However, if you want the sensor to classify the color and you are only interested in significant changes you might only want to send notifications when the Color Number changes. It all depends on whether you want to be flooded with notifications or not.

Note that you will need to use an appropriate data type for the call to SendNotification. This is usually an Update or a Replace, or even both if that makes sense.

Also, the sample services have LogInfo calls that are commented out. These are for debugging and should not be left enabled because they significantly slow down the service. However, when you are first writing a service they can be helpful.

Create a Config File

The first time you run your service, it should write out a new config file (which is an XML serialized version of the service state). The service will not work because the SensorPort will be set to AnySensorPort (and this rarely works). However, you can easily edit the config file to enter the correct sensor port, e.g. Sensor3.

Now that you have an XML file for the config, you can easily make changes to parameters in the service state that affect how the sensor operates, e.g. the Offset value for the Gyro sensor which is necessary to subtract out the "not moving" value.

Test your new Service

Run the service in the debugger and see if it displays data in the console. (This assumes that you have LogInfo calls in the NotificationHandler). Obviously you can set breakpoints and debug as usual.

Update the XSLT File

Explaining how XSLT files work is beyond the scope of this document. The best approach is to examine some existing XSLT files to see how they work.

It is not necessary to update the XSLT file if you do not plan to use a web browser to view the service state. However, it is "nice to have" and it makes your service look more professional. Also, it is not necessary to have the XSLT page automatically refresh itself (which requires JavaScript code) because you can manually refresh the page using the browser's Refresh button.

Basically XSLT pages work as follows:
  • The service state is sent in XML format
  • The XSLT code "transforms" the XML fields so that they will be displayed as HTML
  • There is JavaScript code that uses a timeout to refresh the web page using XML HTTP Requests (this is similar to AJAX)
  • The JavaScript code dynamically updates fields on the web page via the Document Object Model (DOM)

Note that you need to use Common.js which contains several utility functions. This is in the Transforms folder along with the XSLT files. The sample web pages also use the "Master" page that is provided with RDS.

Figuring out why your XSLT does not work and debugging the JavaScript code is a skilled task. IE8 has a set of Developer Tools that help, including a JavaScript debugger and a DOM browser.

When you run DssHost, you can examine services by pointing your web browser to http://localhost:50000 (assuming you used the default port) and looking in the Service Directory. Click on your service to see its web page. If you get an error saying that a resource cannot be found, there is a problem with your XSLT page: Either you have not specified the resource path correctly or there is an error in the XML code.

You can use the web browser to go to http://localhost:50000/resources to browse through the available resources. You should see the name of your assembly in the list. Click on this to drill down to your XSLT page.

Advanced Note
If you have an XSLT page open in Visual Studio when you compile your service, you might get error messages related to the XSLT page. These message are probably irrelevant. To eliminate them, you need to run DssHost, browse to "resources" and then download all of the DSS files (images, XSLT, JavaScript, etc.) into corresponding folders on your hard drive starting at C:\resources. You will find that there are two folders: dss and .dss. The .dss folder contains most of the files, but you should place them into the C:\resources\dss folder (without the dot) on your hard drive. DssHost knows to look in .dss if it cannot find the requested file in dss, which is a versioning feature to support RDS and the CCR & DSS Toolkit. However, you never need to use .dss in a path. For more information on using XSLT, refer to the RDS documentation.

Icons and Images

The XSLT files use images to show information about the sensors. These images are in the Resources folder. Their properties are set to Embedded Resource in the Project.

There are two special-case images: the Icon and the Thumbnail.

An Icon image should be 32x32 pixels in size. It is displayed inside a block in VPL when you add the service to your diagram. A thumbnail should be 16x16 pixels and it is displayed next to the service name in the Service List in VPL. These images are also used when you view the services via a web browser. The filenames are very specific: ServiceClassName.Image.png and ServiceClassName.Thumbnail.png. They should have transparent backgrounds to work best in VPL.

Last edited Feb 3, 2010 at 7:02 AM by TrevorTaylor, version 14

Comments

No comments yet.