Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to create simple one way drivers
#1
There was discussion elsewhere about it being too hard to create simple, ad hoc drivers when someone needs to get at least simple support for a device into place, if there's not a driver available currently. Since there are so many devices, this isn't a terribly unlikely scenario.

So, I figured I'd provide a quick overview of how you can go about doing such a thing. It's not that hard, and I doubt it would be much easier in any other product. But of course lots of people have the perception that everything in CQC is extremely complex, so I guess not to many people bother to look into it.

Anyway, my own bitterness aside, there are two ways to approach this.

The Passthrough Driver
[Indent]The simplest way is to use the passthrough driver. Here is the driver documentation, but I'll also provide some higher level guidance here.

http://www.charmedquark.com/Web2/ExtCont...hrough.htm

Basically, this driver allows you to just send through raw commands to the device. So it's very simple but it's the most limited of the schemes available. It has two fields:
  • SendBinMsg - Send a command to a device that uses a binary protocol
  • SendTextMsg - Send a command to a device that uses a text protocol

Every device will fall into one of these. And of course since text is just numbers as well (with special meaning assigned to those numbers, e.g. 32 is a space, 65 is an upper case A, in the example of ASCII text), you can always use the SendBinMsg if you want to. But usually it's easier to send it as text if the device defines it's commands in terms of text strings, since it's easier for you to read and write.

When you install the driver, it will ask you two questions. One is the text encoding, which is only used if the device accepts text formatted commands. Most of the time the encoding will be ASCII, and that is the default but you can pick another. This will allow the driver to convert the text into the appropriate numbers, via the type of predefined mapping I mentioned above, and send those numbers to the device, in the form of a sequence of bytes.

The other prompt, also for text style devices only, is the extra characters that need to be added to the text messages, which it refers to as the 'text decoration'. There has to be some way for the device to recognize a message. It is most common for there to be two schemes. One is a start/stop scheme, where two special characters are used as the first and last character of the message. The other is an end line type scheme, where one or two special characters indicate the end of a command. These are often referred to by us geeks as 'delimiters', because the indicate limits of the message.

The driver supports some very commonly used schemes. If your driver uses text messages with those of those types of message delimiting schemes, you should select that scheme. If your device uses one not supported, you will have to send your messages as binary, so that you can support the required delimiter scheme yourself. But most devices will use one of the supported ones.

So, let's say, to take an example from a real device, the command to power on an A/V receiver zone is:

Code:
PWON<CR>

This is a text type control protocol, so you are just sending readable text (or semi-readable since they are usually heavily abbreviated for efficiency.) And, in this case, the delimiting scheme is that the commands end with a carriage return character. So you should have indicated the CR terminated text decoration scheme when you installed the driver.

So, if you want to power on the device, you would do this action command:

Code:
Devices::FieldWrite(MyDenon.SendTextMsg, PWON);

That would send the text string PWON to the driver. It would take that, apply the CR to the end of the string, convert that combined text into a sequence of bytes, and it would send the resulting bytes to the device. All other commands would work similarly. You would look up in the control protocol document the form of the command you want to send, and you would just send that text string.


If the device is binary, i's a little more complicated, but not much so. Instead of sending readable text, you must send the raw numbers. You are still actually typing out the numbers as text of course, but the driver knows in this case that it has to convert the characters not into their ASCII values, but to treat them as written out numbers. So 10 doesn't become the two binary values 31 30 (ASCII 1 and 0 digits), it becomes the binary value 10.

In this case those numbers must be in hexadecimal format, which means base 16. That might sound overly geeky, but in many to most cases the numbers will be presented in that format in the protocol documentation, and it also means that every number can be two bytes, since the range of values a single byte can contain (in hex) is 0 to FF. Any values less than 10 (hex) should be given a leading zero, e.g. 0A. So a command might look like:

Code:
50 0A FF 11 13

The driver will parse this string, and convert every two character block into a binary number. It will then send those numbers as is to the device. So the above command would cause five bytes to be sent to the device. If there is any sort of delimiter scheme used, you have to just write it into the value you send. But most binary schemes don't use delimiters, because it's difficult to insure that those characters wouldn't also show up as actual command values. So the above command becomes:

Code:
Devices::FieldWrite(MyDenon.SendTextMsg, 50 0A FF 11 13);


That's it. Basically the passthrough driver just gives you a means to send raw commands to the device, so you can do whatever the protocol allows you to do. If you only need a few small commands, it's pretty practical.
[/Indent]
Dean Roddey
Software Geek Extraordinaire
Reply
#2
The PDL Language
[INDENT]The passthrough driver is quite useful. It's simple and you can quickly gen up a handful of commands that you might need to get basic control over a device. However it has some big deficiencies:
  • You are hard coding device specific commands into automation logic, instead of dealing with meaningful field names like Power, Mute, etc...
  • If a command takes a number of possible varitions (such as source input selection, or audio processing mode selection), you have to separately build up the commands for each of them and send them literally.
  • You have no way to get back information from the device, i.e. the control is one way only.

The PDL language can get around all of those issues without introducing a lot more complexity. Though, we won't deal with the two way thing here. You can do two way drivers using PDL, but we are looking here at how you can get quick, simple control over devices, so we will concentrate on one way PDL drivers, i.e. outgoing commands only.

Here is an example of a simple, one-way PDL driver. Basically all it has to do is define the fields the driver will support, and then to define what will be sent out to the device when one of those fields is written to:

Code:
[CQCProto Version="2.0" Encoding="ISO-8859-1"]

ProtocolInfo=
    TextEncoding="ASCII";
    ProtocolType="OneWay";
EndProtocolInfo;

Fields=

    Field=Power
        Type=Boolean;
        Access=Write;
    EndField;

EndFields;

WriteCmds=

    WriteCmd=Power
        Send=
            BoolSel(&WriteVal, "PWON", "PWOFF");
            "\r";
        EndSend;
    EndWriteCmd;

EndWriteCmds;

As you can see there is not much to it. There is an opening line that you would use as is. In this case, the protocol is text based, so we indiate that the text encoding is ASCII. And we indicate it is a one way protocol, which means that we only have to provide the two sections mentioned above.

In the Fields= section, we define a field named Power. It is a boolean field, so it has True/False values, and it is write only (they would all always be write only in a one driver.) In the WriteCmds= section, we indicate what to send to the device when each field is written to. There is a WriteCmd= block for each field. Inside that is a Send= block to indicate what to send. In a two way device there would also be a block to indicate what replies to expect but we don't deal with that here.

The Send= block is a just a sequence of values to concatenate together and send. Since this is an ASCII device, it's just text strings to send. In our case, we want to send PWON if the user writes True to the field and PWOFF if the user writes False. So we use a simple 'expression' called BoolSel, which just takes a boolean value and based on that returns one of the two other values. In our case we pass it &WriteVal, which is the value that was written to the field. So, if the user writes True, it returns PWON, else it returns PWOFF. We then on the next line indicate we want to add a \r, which reprsents a carriage return.

The driver evaluates any expressions, then builds up the final string by concatenating all the values (in order) together, then converts the string to the equivalent byte values and sends them out.

You can see that this is not that much more complicated than the pass through driver. But, it means you are now dealing with real driver fields, and you don' have to have separate commands for every possible variation, such as off and on in this case. You have one field that takes the value written and incorporates it into the message to send.

Once you have your file written, which should be named along the lines of MakeModel.CQCProto so something like MyDevice.CQCProto, you can then test it out. For now it doesn't matter where you save it.

You will also need to create a manifest file for the driver. This file tells CQC about the driver, what type of device connection it uses and so forth. Here is a minimal example that would be fine for the above driver, assuming a socket connection:

Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE CQCCfg:DriverCfg PUBLIC
          "urn:charmedquark.com:CQC-DrvManifest.DTD"
          "CQCDriverCfg.DTD">

<CQCCfg:DriverCfg>

    <!-- Define the server driver config -->
    <CQCCfg:ServerCfg   CQCCfg:LibName="MyDevice"
                        CQCCfg:DrvType="GenProto"/>


    <!-- Define the common driver config -->
    <CQCCfg:CommonCfg   CQCCfg:Author="Bubba Jones"
                        CQCCfg:Contact="bubba@jones.com"
                        CQCCfg:Description="Simple, one way driver for
                                            my device"
                        CQCCfg:DisplayName="My Device"
                        CQCCfg:Category="Miscellaneous"
                        CQCCfg:Make="MyStuff"
                        CQCCfg:Model="MyDevice"
                        CQCCfg:Version="1.0"/>

    <CQCCfg:ConnCfg CQCCfg:Editable="True">
        CQCCfg:Port="5780" CQCCfg:SockProto="SockProto_TCP"/>
    </CQCCfg:ConnCfg>

</CQCCfg:DriverCfg>

The manifest is a little technical, so if you need help, just ask on the forum. It's not too hard to set up though once you've done one. Basically you define the server side driver, by indicating the name (MyDevice as we named it previous) and indicate it's a generic protocol (PDL) driver. Then there's some descriptive info that is displayed to the user when he selects the driver. Then there is information describing the socket port or socket connection info, if that is relevant. Save this using the same base name as the PDL file, so in our case MyDevice.Manifest. For now, it doesn't matter where you save it.

Once you have the manifest, you can test it by running the "Develop PDL Drivers" option from the Windows start menu. Use the Session -> New Session menu option to start a new session. Open up your manifest file. It will then prompt you for the CQCProto file as well, so select that. You will then go through the standard driver installation wizard to set up the driver, just as you would when you normally load it. If there are errors in your manifest file it will tell you the line and describe the error. So edit it and save it and try to load it again until you get it happy. Ask on the forum if you can't figure it out.

You can now use the Start button to start the driver running. If there are any errors in your PDL file, it will complain when you press Start, and tell you the line where the error occurred. Make the required changes and save the file and press Start again. Iterate this process until you get no more errors.

Once there are no errors, the driver will start running. Since it's a completely one way driver, it won't do anything until you write a field. You will just see that it went through the usual driver steps of initializing, getting its comm resource (opening the socket connection in this case), and got connected to the device, like this:

[Image: PDLDebug1.png]

So let's test it by changing a field. First, make sure the "Send Bytes" check box is checked. That will make it show you the outgoing bytes you are sending. Then select a field and press the Change Fld... button. Enter a new value to send, and press Save. If it works, the debugger will show you the bytes you sent. It doesn't show the text, it actually shows the raw bytes that the text was converted into, so that you can be sure that you are seeing exacly what was sent.

[Image: PDLDebug2.png]

You should also see that the device responded correctly. If not, something is wrong with your command, so check the bytes shown in the output to make sure they are correct. If not, press Stop to stop the session, edit your PDL file, then press Start to start it up again and try again until you get the desired result.[/INDENT]
Dean Roddey
Software Geek Extraordinaire
Reply
#3
[indent]
continued...

Adding more fields is must more of the same. In order to handle some field types you will want to learn more about the available PDL expressions, which are in the Driver Development technical document on the web site. One common thing you will need to do, is to map a readable text value to some driver specific value. The most common reason you do this is for enumerated fields. So for instance the driver has a playback command and the values for the command PB1, PB2, and PB3, for run, stop and pause, you wouldn't want to make the user deal with those heavily abreviated values. You would want to let the user deal with human readable values, like Run, Stop and Pause. That means your field will get that values written to it, but it must map them to the device specific values. To deal with this, PDL provides a mapping mechanism. Here is the PDL driver updated to support such a field.

Code:
[CQCProto Version="2.0" Encoding="ISO-8859-1"]

ProtocolInfo=
    TextEncoding="ASCII";
    ProtocolType="OneWay";
EndProtocolInfo;

Maps=

    // A map for some miscellaneous adjustment (inc/dec) operations
    Map=PlaybackMap
        Type=Card1;
        Items=
            Item="Run"      , 49;
            Item="Stop"     , 50;
            Item="Pause"    , 51;
        EndItems;
    EndMap;
EndMaps;

Fields=

    Field=Power
        Type=Boolean;
        Access=Write;
    EndField;

    Field=Playback
        Type=String;
        Access=Write;
        Limits="Enum: Run, Stop, Pause";
    EndField;

EndFields;

WriteCmds=

    WriteCmd=Power
        Send=
            BoolSel(&WriteVal, "PWON", "PWOFF");
            "\r";
        EndSend;
    EndWriteCmd;

    WriteCmd=Playback
        Send=
            MapTo(PlaybackMap, &WriteVal);
            "\r";
        EndSend;
    EndWriteCmd;

EndWriteCmds;

Here you see that we have added a Maps= section, and within that we defined a map. A map has an entry for each value you want to be able to map between. So we need one for Run, Stop and Pause, which map to the values 49, 50, and 51 respectively (the ASCII values for 1, 2, and 3.) In the Playback field's Send block, we use the MapTo() expression to map the value written to the field, using the PlaybackMap map. So writing Pause to the field will result in the string "PB3\r" being sent to the device (which in ASCII map to the hex byte values "50 42 33 0D", and we should see that in the debug output.

[Image: PDLDebug3.png]

Once you are happy with your driver, use the Tools -> Package Driver to package up the driver. You can then import this package file and how your driver will show in CQC. Any time you want to make changes, just work with your local copies of the PDL/Manifest files and make the desired changed. Package them up again and import the package. The new files will overwrite the previous ones. Do a Reconfigure on the driver to pick up the changes.

[/Indent]
Dean Roddey
Software Geek Extraordinaire
Reply


Possibly Related Threads...
Thread Author Replies Views Last Post
  create a grid of temp values from DataLogDB IVB 6 2,184 12-13-2015, 09:37 PM
Last Post: IVB
  Create a Customizable Dialog wuench 24 14,966 05-27-2015, 09:12 AM
Last Post: wuench
  Create Custom DropDown Widget jrlewis 2 10,118 08-05-2011, 08:26 AM
Last Post: jrlewis
  How to create a "Run Time" counter and display beelzerob 13 9,124 01-12-2009, 08:06 AM
Last Post: beelzerob

Forum Jump:


Users browsing this thread: 1 Guest(s)