Introduction To Developing ASCOM Drivers

Download as pdf or txt
Download as pdf or txt
You are on page 1of 14
At a glance
Powered by AI
The document provides an overview of how to develop ASCOM drivers using C# and the ASCOM templates. It discusses prerequisites, starting a new driver project, common errors, and creating a test executable.

The prerequisites for developing ASCOM drivers include basic software development skills in C#, C++ or similar languages, familiarity with using Windows, installing applications, and a willingness to learn. The document assumes the developer has Visual Studio and the ASCOM Platform installed.

The first step when starting a new ASCOM driver project is to create a new project using the ASCOM templates. This will set up the basic driver structure and functionality. The template selected should match the type of driver being created, such as a focuser driver.

Introduction to Developing ASCOM Drivers

Introduction
ASCOM driver development is not terribly difficult but it isn't the simplest way to start C# or VB.NET development. There seem to be a lot of people who are starting this as almost their first .NET based software development project. These notes are an attempt to help.

Prerequisites and Requirements


I'm going to assume that you have Windows 7 64 bits but will try to note where things are different. I'm going to assume that you have the following skills: Using a PC, including finding and manipulating files and folders and setting permissions. Using the Internet, including searching for data and downloading and installing applications. Some experience with software development using a modern language such as C, C++, Visual Basic or Delphi. Some familiarity with C# development. A willingness to learn. If you don't have these skills then it's a good idea to get some practice first; you will need to be able to do these things and it's not fair on the ASCOM people to expect them to try to teach you these basic software development skills. I'm not covering basic software development or C#. You need to learn this for yourself. No amount of tutorials, help or guidance can be a substitute for thinking about what is going on. I'm going to cover this using C# only, using the C# Visual Studio Express download from Microsoft. You must download and install this. You must also download and install ASCOM Platform 6 and the ASCOM Platform 6 developer components. This is based on a simple focuser driver example. The principles apply to any other sort of ASCOM driver.

Starting the Driver


Use the ASCOM templates! They provide a very good way to get the basic functionality implemented. Start Visual Studio and select New Project.... Select the ASCOM6 templates and select the driver template that you want. Set the project name. Choose this carefully because it will become your driver name. It's much easier to get this right at the start.

This is the sort of thing you will get. Click on OK and your project will be built. You will get a project and will be shown a help file. The first instructions are about modifying the project name and the default namespace; these things should now be done by the template so you do not need to make any changes. The project properties should look like this:

Click on the Start Debugging button or press F5. You will probably get your first error message:

Click on No. The error message will be shown:

The message says that the assembly cannot be registered; this is because, on W7 or Vista access to some parts of the registry are restricted to administrators. The good news is that this is only done after the driver has been built successfully. If you're using XP you won't see this. Close down Visual Studio, saving your project, and restart it as administrator. The simplest way to do this is bring up the context menu on the short cut to Visual Studio and select Run As Administrator. Select your project in the recent projects and build again. Now you get a different error

message:

This means that your driver has been successfully built and registered but it cannot be run because it's a dll and it needs an executable project. The intent was to run a script at this point but that doesn't work in the Express editions. So you need to create an executable project: In the Solution Explorer, select the Solution and use the context menu to select Add - New Project... Select Visual C# and Console Application and choose a project name:

Click on OK and your new project will be created in this example it's called FocuserTest. Use the Context menu to Add References to the ASCOM Client Toolkit and ASCOM Device Interfaces. Make sure the Version 6.0.0.0 versions are selected: Check the project properties and make sure that your test project uses the save version of .NET as the driver, probably .NET 3.5. This is needed to be able to debug into the driver from the test program.

Use the context menu to set FocuserTest to be the Startup Project. Add this line to the Main subroutine in Program.cs:
string id = ASCOM.DriverAccess.Focuser.Choose("");

Click on Debug and you program should compile and run:

Select your focuser and click on Properties:

There you are! Your driver is working! It isn't doing much yet but it's a start.

I will cover adding functionality to your new driver next.

Notes: If you aren't making a focuser select the driver type you are using everywhere that we have mentioned a focuser. I've skimmed a number of basic development concepts, if you aren't familiar with these search on the web. Typing your question into a search engine should come up with something useful. Good ways to get help are to highlight the error and press F1 or use the context menu and select Show Error help. Another useful way is to search using the error message. It's worth getting several opinions though. No amount of advice or tutorials can be a substitute for thought. Experiment and expect to throw a lot of code away.

Developing the Setup Dialog


The setup dialog is how the driver properties are set, in this case we are setting a serial port that's used to communicate with the focuser hardware. Open the SetupDialogForm in the design mode and add a label and combo box called comboBoxComPort.

Move to the SetupDialogForm code and add code to the SetupDialogForm constructor to fill the combo box with the Com port names:
comboBoxComPort.Items.Clear(); using (ASCOM.Utilities.Serial serial = new Utilities.Serial()) { foreach (var item in serial.AvailableCOMPorts) { comboBoxComPort.Items.Add(item); } }

Make the driverId constant internal instead of private. Save the com port name in the CmdOkClick event handler:
private void CmdOkClick(object sender, EventArgs e) { using (ASCOM.Utilities.Profile p = new Utilities.Profile()) { p.DeviceType = "Focuser"; p.WriteValue(Focuser.driverId, "ComPort", (string)comboBoxComPort.SelectedItem); } Dispose(); } That's enough to be able to select and save the com port, it would be nice to set the current com port when you load the setup dialog but I'll leave that for now.

Setting up the driver Start by going through the public properties setting the ones that aren't implemented so they throw

the ASCOM.PropertyNotImplemented exception, here's an example:


public bool TempComp { get { throw new ASCOM.PropertyNotImplementedException("TempComp", false); } set { throw new ASCOM.PropertyNotImplementedException("tempComp", true); } } Set properties such as Name, Description and DriverInfo so they return suitable strings: public string Name { get { return driverDescription; } } Set any other capabilities appropriately. You will need to think about this because it's where you start to implement the capabilities of your hardware. It's a good idea to have the ASCOM device interface specification open so you can refer to it as you go. It's a good idea to set things so it does very little, then extend things once you have things working. Connecting to the Hardware. This happens in the Set Connected property: Add a field to hold the serial port: private Serial serialPort; I won't describe everything that's done, here is a pretty minimal connect property public bool Connected { get { if (serialPort == null) return false; return serialPort.Connected; } set { if (value) { // check if we are connected, return if we are if (serialPort != null && serialPort.Connected) return; // get the port name from the profile string portName; using (ASCOM.Utilities.Profile p = new Profile()) { p.DeviceType = "Focuser"; portName = p.GetValue(driverId, "ComPort"); } if (string.IsNullOrEmpty(portName)) { // report a problem with the port name throw new ASCOM.NotConnectedException("no Com port selected"); } // try to connect using the port try { serialPort = new Serial(); serialPort.PortName = portName; serialPort.Speed = SerialSpeed.ps9600; serialPort.Connected = true; } catch (Exception ex) { // report any error throw new ASCOM.NotConnectedException("Serial port connection error", ex);

} } else { // disconnect if (serialPort != null && serialPort.Connected) serialPort.Connected = false; } } } Additional things would be to send a command to the hardware to check that it's there. It's now possible to extend the focuser test application to check a few things: static void Main(string[] args) { string id = ASCOM.DriverAccess.Focuser.Choose(""); ASCOM.DriverAccess.Focuser focuser = new ASCOM.DriverAccess.Focuser(id); Console.WriteLine("name " + focuser.Name); Console.WriteLine("description " + focuser.Description); Console.WriteLine("DriverInfo " + focuser.DriverInfo); Console.WriteLine("driverVersion " + focuser.DriverVersion); Console.WriteLine("Press Enter to finish"); Console.ReadLine();

} debug and you get:

Add a few more things: Console.WriteLine("Absolute Focuser " + focuser.Absolute); Console.WriteLine("MaxStep " + focuser.MaxStep); Console.WriteLine("MaxIncrement " + focuser.MaxIncrement); Console.WriteLine("Connected " + focuser.Connected);

Keep adding tests as you add more functionality: focuser.Connected = true; Console.WriteLine("Connected " + focuser.Connected); If you get errors set breakpoints and debug to see what's happening.

You may find that you can't debug from the test program to the driver, the simplest way is to set breakpoints in the driver a run to those. It's important to make sure that both the driver and the test application are using the same version of .NET, if they aren't you can't debug into the driver. Now to send some commands: A good way to implement sending commands is to put the command in the CommandString method: public string CommandString(string command, bool raw) { if (!this.Connected) throw new ASCOM.NotConnectedException(); serialPort.ClearBuffers(); serialPort.Transmit(command); return serialPort.ReceiveTerminated("#"); } This assumes that every command is terminated with a '#' character AND that this chanacter cannot appear in a message. You may need to add message terminators to the command. Every method that sends a command calls CommandString, for example: public int Position { get { // position command is P#, returns nnnn# string ret = CommandString("P#", true); return int.Parse(ret); } } One thing that trips people up is that Visual Studio re-registers the driver every time it's compiled so you need to run the setup dialog to select the COM port each time, otherwise you get an error in connected because the com port name is not set. I can't run this fully because I don't have hardware that implements this protocol. Obviously a real driver would need to implement a real protocol. There are a number of things missing: Very little error checking. No checks on the validity of data. Here's the complete driver: using using using using using using System; System.Collections; System.Globalization; System.Runtime.InteropServices; ASCOM.DeviceInterface; ASCOM.Utilities;

namespace ASCOM.AcmeAstro { // // Your driver's ID is ASCOM.AcmeAstro.Focuser // // The Guid attribute sets the CLSID for ASCOM.AcmeAstro.Focuser // The ClassInterface/None addribute prevents an empty interface called // _Focuser from being created and used as the [default] interface // [Guid("d762e5e0-f093-4852-b081-8e0206192c45")] [ClassInterface(ClassInterfaceType.None)] [ComVisible(true)] public class Focuser : IFocuserV2 { #region Constants

// // Driver ID and descriptive string that shows in the Chooser // internal const string driverId = "ASCOM.AcmeAstro.Focuser"; // TODO Change the descriptive string for your driver then remove this line private const string driverDescription = "AcmeAstro Focuser"; private Serial serialPort; #endregion #region ASCOM Registration // // Register or unregister driver for ASCOM. This is harmless if already // registered or unregistered. // private static void RegUnregASCOM(bool bRegister) { using (var p = new Profile()) { p.DeviceType = "Focuser"; if (bRegister) p.Register(driverId, driverDescription); else p.Unregister(driverId); } } [ComRegisterFunction] public static void RegisterASCOM(Type t) { RegUnregASCOM(true); } [ComUnregisterFunction] public static void UnregisterASCOM(Type t) { RegUnregASCOM(false); } #endregion #region Implementation of IFocuserV2 public void SetupDialog() { using (var f = new SetupDialogForm()) { f.ShowDialog(); } } public string Action(string actionName, string actionParameters) { throw new ASCOM.MethodNotImplementedException("Action"); } public void CommandBlind(string command, bool raw) { throw new ASCOM.MethodNotImplementedException("CommandBlind"); } public bool CommandBool(string command, bool raw) { throw new ASCOM.MethodNotImplementedException("CommandBool"); } public string CommandString(string command, bool raw)

if (!this.Connected) throw new ASCOM.NotConnectedException(); serialPort.ClearBuffers(); serialPort.Transmit(command); return serialPort.ReceiveTerminated("#");

} public void Dispose() { throw new System.NotImplementedException(); } public void Halt() { // halt command is H# CommandString("H#", false); //throw new System.NotImplementedException(); } public void Move(int value) { // move command is Mnnnn# where nnn is the target position CommandString(string.Format("M{0}#", value), false); //throw new System.NotImplementedException(); } public bool Connected { get { if (serialPort == null) return false; return serialPort.Connected; } set { if (value) { // check if we are connected, return if we are if (serialPort != null && serialPort.Connected) return; // get the port name from the profile string portName; using (ASCOM.Utilities.Profile p = new Profile()) { p.DeviceType = "Focuser"; portName = p.GetValue(driverId, "ComPort"); } if (string.IsNullOrEmpty(portName)) { // report a problem with the port name throw new ASCOM.NotConnectedException("no Com port selected"); } // try to connect using the port try { serialPort = new Serial(); serialPort.PortName = portName; serialPort.Speed = SerialSpeed.ps9600; serialPort.Connected = true; } catch (Exception ex) { // report any error

error", ex);

throw new ASCOM.NotConnectedException("Serial port connection } } else { // disconnect if (serialPort != null && serialPort.Connected) serialPort.Connected = false; }

public string Description { get { return driverDescription; } } public string DriverInfo { get { return driverDescription; } } public string DriverVersion { get { Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; return String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version.Major, version.Minor); } } public short InterfaceVersion { get { return 2; } } public string Name { get { return driverDescription; } } public ArrayList SupportedActions { get { return new ArrayList(); } } public bool Absolute { get { return true; } } public bool IsMoving { get { // ismoving command is ?#, returns 1# if moving, 0# if not string ret = CommandString("?#", true); return (ret == "1"); } } // use the V2 connected property

public bool Link { get { return this.Connected; } set { this.Connected = value; } } public int MaxIncrement { get { return 50000; } } public int MaxStep { get { return 50000; } } public int Position { get { // position command is P#, returns nnnn# string ret = CommandString("P#", true); return int.Parse(ret); } } public double StepSize { get { throw new ASCOM.PropertyNotImplementedException("StepSize", false); } } public bool TempComp { get { throw new ASCOM.PropertyNotImplementedException("TempComp", false); } set { throw new ASCOM.PropertyNotImplementedException("tempComp", true); } } public bool TempCompAvailable { get { throw new ASCOM.PropertyNotImplementedException("TempCompAvailable", false); } } public double Temperature { get { throw new ASCOM.PropertyNotImplementedException("Temperature", false); } } } } #endregion

Chris Rowland August 2011

You might also like