Improved Z-Wave message handling in C#

February 6th, 2010 by Henrik Leidecker Jørgensen | Edit this entry Leave a reply »

In this article a queue is implemented to allow the device objects to request messages to be send at any time. The messages are enqueued and then send to the Z-Wave controller when possible.
An additional thread is created to decouple the sending and receiving of messages. It will dequeue messages from the SendMessage queue and send them to the Z-Wave controller.
The entire discovery functionality will be moved to the Controller class and a mechanism is provided to allow other objects to subscribe to events concerning updates to the Z-Wave network e.g. added nodes.

class ZWavePort
{
    public delegate void MessageHandler(byte[] sentMessage, byte[] receivedMessage);
 
    ...
 
    private Thread senderThread
 
    ...
 
    private PriorityQueue<ZWaveMessage, ZWaveMessagePriority> queue = new PriorityQueue<ZWaveMessage, ZWaveMessagePriority>();
 
    ...
 
}
 
class Controller : ZWaveNode
{
    public event DiscoveryEventHandler DiscoveryEvent;
 
    ...
 
    private Dictionary<byte, ZWaveNode> devices = new Dictionary<byte, ZWaveNode>();
 
    ...
 
    public void EnqueueMessage(ZWaveMessage message)
    {
        queue.Enqueue(message, message.Priority);
    }
 
    ...
 
}

This article is just one out of a series of articles about the Z-Wave protocol and how it can be implemented in C#. Reading the previous articles before continuing with this one will provide a clear advantage.

The queue framework is based on an implementation done by Jim Mischel, but the actual implementation has been heavily modified. The ‘SendMessage’ queue is prioritized in order to allow for ACK messages to be send immediately. This requires that two priority levels are defined

public enum ZWaveMessagePriority
{
    Interactive,
    Control
}

‘Control’ is for ACK messages and have the highest priority. ‘Interactive’ is e.g. for switching on a unit and has less priority. Besides from the priority the queue is operating in accordance with the FIFO principle (First In First Out).

The SendMessage method in the ZWavePort class is now executed in a separate thread to ensure that the sending of messages isn’t blocked by processing of incoming messages.

senderThread = new Thread(
    new System.Threading.ThreadStart(SendMessage));

The SendMessage method is modified to make it operate on the priority queue and the following flowchart demonstrates the principles behind the SendMessage method.

public void SendMessage()
{
    byte[] message = null;
    while (sp.IsOpen) 
    {
        while (queue.Count == 0) Thread.Sleep(100); // Loop until the queue has elements
        while ((queue.Peek().Value.Message[0] != 0x06) && (!SetMessagingLock(true))) 
        {
            Thread.Sleep(100);
        }
        message = queue.Dequeue().Value.Message;
        if (message[0] != 0x06) 
        {
            sendACK = false;
            message[message.Length - 1] = GenerateChecksum(message); // Insert checksum
            sentMessage = message;
        }
        System.Console.WriteLine("Message sent: " + ByteArrayToString(message));
        sp.Write(message, 0, message.Length);
    }
}

In the previous articles the level of support for the callback id was discussed – Not all message pairs support this flow control mechanism and in order to overcome this limitation the SendMessage and ReceiveMessage methods are modified. The SendMessage method stores the sent message in a variable and the ReceiveMessage method then retrieves the sent message and links it to the received message. This enables the device objects to link the received message with the sent message.
The following sequence of messages shows what is sent and received when requesting type information for node ‘6′. The last message received does not support the callback id nor does it contain the node id and this is why the link between the message originally sent and the received message is needed.

/*
    Message sent: 01 04 00 41 06 BC
    Message received: 06
    Message received: 01 09 01 41 D2 9C 00 04 11 01 EC
    Message sent: 06
*/

The delegate MessageHandler in the ZWavePort class is modified to also include a byte array with the sent message to support the linkage of sent and received messages.

The device discovery implementation can now be moved to the Controller class and an event is implemented to allow the Controller object to signal about updates to the ZWave network – added or removed nodes.

public delegate void DiscoveryEventHandler(object source, DiscoveryEventArgs e);

The C# client no longer has to take part in the discovery process. This simplifies the implementation. A local list of devices in the Z-Wave network, based on the events send by the Controller object, is maintained in the C# client.

class Program
{
    public Dictionary<byte, ZWaveNode> Devices = new Dictionary<byte, ZWaveNode>();
    private Controller controller;
 
    public Program()
    {
        ZWavePort zp = new ZWavePort();
 
        // Execute discovery
        controller = new Controller(zp);;
        controller.DiscoveryEvent += new DiscoveryEventHandler(DiscoveryEvent);
        controller.Discovery();
    }
 
    void DiscoveryEvent(object sender, DiscoveryEventArgs e)
    {
        // Add device to dictionary
        Devices.Add(e.NodeId, controller.GetDevice(e.NodeId));
    }
 
    static void Main(string[] args)
    {
        Program p = new Program();
 
        Thread.Sleep(3000);
        Switch s = (Switch) p.Devices[43];
        s.On();
 
    }
}

The journey is about to end …

Only the very first steps have been taken in terms of the actual Z-Wave protocol implementation. In the first article it was possible to switch on or switch off a device. With the most recent implementation it is now possible to control a switch/dimmer and initiate a discovery of devices in the network.
So what has actually been achieved? Some of the basic principles of the Z-Wave protocol have been highlighted. A framework for handling the messaging has been initiated. A number of the obstacles in terms of handling the messaging have been discussed and proposals for how to overcome these have been provided.
Based on the feedback I have received there is no doubt that this has inspired other people to get started with their own implementation of the Z-Wave protocol – and this is exactly what I intended to achieve from the beginning.

The next article will provide a list of ideas for further improvements to the framework and also touch on the subject of how to obtain more information about the protocol. I encourage you to join the forum and share your experiences related to the Z-Wave protocol and the implementation of it.

The source code can be downloaded via this link.

Advertisement

2 comments

  1. Thomas Andresen says:

    Your checksum method threw an Overflow Exception. I took the freedom to correct it:

    private static byte GenerateChecksum(byte[] data)
    {
    byte ret = 0xff;
    for (int i = 1; i < (data.Length – 1); i++)
    {
    ret ^= data[i];
    }
    return ret;
    }

  2. Paricker says:

    I’d love to see more of this series, or a spin-off open source project.

Leave a Reply