In this article the message handling is being improved and a Dimmer is added to the device classes.
The article is building upon a number of other articles previously published on this site. It is highly recommended to read those before proceeding.
- An introduction to Z-Wave programming in C#
- An introduction to the Z-Wave protocol
- The Z-Wave protocol in C# – Part 1
- The Z-Wave protocol in C# – Part 2
A message sequence has to be terminated before a new one can be initiated – in order to comply with the specifications. The current strategy is relying on the device object to determine when a message sequence is completed and to send a notification.
In the previous article only one device was being used and the message sequence for switching on node ‘6′ was looking like the below sequence.
/*
Message sent: 01 09 00 13 06 03 20 01 FF 05 3B
Message received: 06
Message received: 01 04 01 13 01 E8
Message sent: 06
*/All the messages coming from the Z-Wave controller were intented for only that specific device object. In this article multiple devices are in play and this adds a complexity in terms of identifying the owner of the messages from the Z-Wave controller. All the device objects will signal ‘message completed’ if they can’t distinguish between messages that they are suppose to react to and those that are not relevant. Remember that all incoming messages are passed on to all the device objects.
The Z-Wave protocol specifies something called a callback id. It can be used to keep track of request/response pairs. A callback id is being defined every time a device object sends a request. The callback id is always added to the byte sequence right before the checksum but it is optional to use. The call back id is ‘3′ in the following message sequence for switching on node ‘6′.
/*
Message sent: 01 0A 00 13 06 03 20 01 00 05 03 C4
Message received: 06
Message received: 01 04 01 13 01 E8
Message sent: 06
Message received: 01 05 00 13 03 00 EA
Message sent: 06
*/An additional request/response pair is added. The Z-Wave controller is responsible for the last request sent and the callback id, originating from the initial request from the C# client, is part of that request (byte 5). It is this callback id that the device objects use for keeping track of which object that has to terminate the message sequence.
The last ACK message from the C# client is being used as a response to the request from the Z-Wave controller.
The following devices and configuration are being used for the implementation in this article.
- Z-Wave controller (node id: 0×03)
- Dimmer (node id: 0×06)
- Switch (node id: 0×2B)
A new function GetCallbackId is being provided by the ZWavePort object. The device objects use the method for retrieving a new callback id before they send a request.
public int GetCallbackId() { lock (this.callbackIdLock) { return ++this.callbackIdPool; } }
The device classes need to be changed in order to make the callback id strategy work. Every device class is inherited from the ZWaveNode class – only generic modifications to the ZWaveNode class is needed.
- A list of all related callback ids.
- A new callback id is being requested in the SendMessage method.
- The callback id is being removed from the list when a message with that specific callback id is received and the MessagingCompleted is called.
- Verify that the message received in the MessageHandler method has a ‘valid’ callback id.
- Print out debug information – when the MessagingCompleted method is called.
abstract class ZWaveNode { ... protected List callbackIds = new List(); ... protected Boolean SendMessage(byte[] message) { int callbackId = zp.GetCallbackId(); // Retrieve a callback id from the ZWavePort object callbackIds.Add(callbackId); // Add callback id to the list message[message.Length - 2] = callbackId; // Insert the callback id into the message while (!zp.SendMessage(message)) Thread.Sleep(100); return true; } protected void MessagingCompleted(byte callbackId) { callbackIds.Remove(callbackId); // Remove the callback id zp.MessagingCompleted(); } public virtual void MessageHandler(byte[] message) { if ((message[0] != 0x06) && (sessionIds.Contains(sessionId))) { // Check the callback id byte callbackId = message[4]; if ((message.Length == 7) && callbackIds.Contains(callbackId)) { System.Console.WriteLine(this.GetType().Name.ToString() + " (node id: " + this.NodeId + "): Messaging completed"); MessagingCompleted(callbackId); } } } }
The device class family is extended with a new Dimmer class. This requires that the byte sequence for dimming is known.
/*
0x01, 0x09, 0x00, 0x13, nodeId, 0x03, 0x20, 0x01, level, 0x05, callbackId, checksum
*/The byte sequence is very similar to the byte sequence needed for switching on or off a device with one exception . Byte 9 is no longer restricted to the values 0 and 255. These are the allowed values
- 0×00 – Switch off
- 0xFF – Switch on (the level vil be restored to the level before the dimmer was switched off)
- 0xXX – Dimming (possible values: 0×00- 0×63)
The Switch class is already utilizing a method called SetLevel. This makes the Dimmer class very simple.
class Dimmer : Switch { public Dimmer(byte nodeId, ZWavePort zp) : base(nodeId, zp) { } public void Dim(byte level) { if ((level >= 0x00) && (level <= 0x63)) { SetLevel(level); } } }
The C# client is changed in order to take the new Dimmer device class into use. The dim level is set to a random value between o and 99.
static void Main(string[] args) { ... byte nodeId = 0x06; Dimmer d = new Dimmer(nodeId, zp); nodeId = 0x2B; Switch s = new Switch(nodeId, zp); // Generate random number Random r = new Random(); byte level = (byte)r.Next(99); s.Off(); // Switch off node '6' d.Off(); // Switch off node '43' s.On(); // Switch on node '6' d.Dim(level); // Dim node '43' to random level ... }
Dimmer devices are now supported and the device objects are now able to evaluate whether or not they are allowed to signal ‘messaging completed’.
In the next article the implementation will be further developed in order to enable automatic discovery of the devices in the Z-Wave network and to create device objects accordingly.
The source code can be downloaded via this link. You are free to use the code in any manner you like.
Great series! Keep it up!
Hey we are doing some work in this fashion, would like to chat with you about it. Could not find contact information for you, if you wouldn’t mind contacting me, I would appreciate it.
Great posts!
The only tutorials I found online that actually made sense for a programmer.
If you woudln’t mind also can I get in contact with you.
Hi Henrik!
thanks for such a great post and tutorial. i am trying to work on project based on zwave tech.
would you please allow me to have an email conversation.
regards
Love your work…