Z-Wave protokollen i C# – Del 1

13. december, 2009 af Henrik Leidecker Jørgensen Skriv en kommentar »

I denne artikel om Z-Wave programmering i C# kan programmet tænde og derefter slukke for en Z-Wave enhed, hvilket kræver, at nogle basale principper i Z-Wave protokollen overholdes.

Det er vigtigt at have forstået stoffet i de to tidligere artikler for at få det fulde udbytte af eksemplet.

Til eksemplet anvendes de samme enheder og den samme konfiguration, som blev brugt i artiklen ‘En introduktion til Z-Wave programmering i C#‘.

Den basale kommunikation og protokolhåndtering samles i klassen ZWavePort.

class ZWavePort
{
    ...
}

Kommunikationsdelen består i at sende og modtage beskeder via seriel porten, hvor protokolhåndteringen dækker over automatisk afsendelse af ACK beskeder.

Herunder ses sekvensen af beskeder, i forbindelse med at node ‘6′ tændes.

/*
    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
*/

I dette eksempel er der kun to typer af beskeder, der kan afsendes fra C# klienten.

  1. Request – næste besked fra Z-Wave controlleren er ACK og C# klienten skal ikke send ACK
  2. ACK – næste besked fra Z-Wave controlleren er et response, hvilket kræver en ACK besked fra C# klienten

Der indføres en variabel til at styre om C# klienten skal sende en ACK besked i forbindelse med den følgende indkommende besked fra Z-Wave controlleren.

private Boolean sendACK = true;

I funktionen SendMessage er reglen om, hvorvidt, der skal sendes ACK fra C# klienten eller ej implementeret på følgende måde.

public void SendMessage(byte[] message)
if (sp.IsOpen == true)
{
    if (message != MSG_ACKNOWLEDGE)
    {
        sendACK = false;
        message[message.Length - 1] = GenerateChecksum(message); // Insert checksum
    }
    sp.Write(message, 0, message.Length);
}

Funktionen tilføjer også en checksum til beskeden, hvis det er en request.

I eksemplet fra den indledende artikel om Z-Wave programmering blev modtagelsen af beskeder helt ignoreret, men da protokollen nu skal overholdes mht. afsendelse af ACK beskeder, er det nødvendigt at holde styr på, hvornår der sendes beskeder til C# klienten.
Modtagelsen af beskeder fungerer helt enkelt ved, hele tiden at spørge seriel porten, om der er modtaget data og derefter læse eventuelle data fra porten.

private void ReceiveMessage()
{
    while (sp.IsOpen == true)
    {
        int bytesToRead = sp.BytesToRead;
        if ((bytesToRead != 0) & (sp.IsOpen == true))
        {
            byte[] message = new byte[bytesToRead];
            sp.Read(message, 0, bytesToRead);
            if (sendACK) // Does the incoming message require an ACK?
            {
                SendACKMessage();
            }
            sendACK = true;
        }
    }
}

ReceiveMessage funktionen anvender sendACK variablen til at afgøre, om der skal sendes en ACK besked, når C# klienten modtager en besked fra Z-Wave controlleren. Variablen sendACK sættes altid til true efter en besked er modtaget. Det betyder, at der som udgangspunkt sendes en ACK besked hver gang en besked modtages af C# klienten.

Der oprettes en seperat tråd til at køre ReceiveMessage funktion for at sikre, at C# klienten altid kan modtage beskeder fra Z-Wave controlleren.

receiverThread = new Thread(new System.Threading.ThreadStart(ReceiveMessage));

Tråden startes i forbindelse med, at seriel porten åbnes.

public void Open()
{
    if (sp.IsOpen == false)
    {
        sp.Open();
        receiverThread.Start();
    }
}

Z-Wave protokollen kræver, at en beskedsekvens afsluttes før en ny påbegyndes. Det betyder, at afsendes en besked om at tænde for en enhed, så skal alle responses og ACK beskeder være sendt før en ny beskedsekvens om at slukke for enheden kan påbegyndes.
For at gøre implementationen simpel forsøges dette overholdt ved i selve Main funktionen at vente 5 sekunder efter en besked er afsendt med SendMessage – antagelsen er, at kommunikationen efter dette tidsrum er ophørt.

static void Main(string[] args)
{
    byte nodeId = 0x06;
 
    byte level = 0xFF; // On
    byte[] message = new byte[] { 0x01, 0x09, 0x00, 0x13, nodeId, 0x03, 0x20, 0x01, level, 0x05, 0x00 };
 
    ZWavePort zp = new ZWavePort();
    zp.Open();
 
    zp.SendMessage(message);
    Thread.Sleep(5000); // Wait for 5 seconds
 
    level = 0x00; // Off
    message = new byte[] { 0x01, 0x09, 0x00, 0x13, nodeId, 0x03, 0x20, 0x01, level, 0x05, 0x00 };
    zp.SendMessage(message);
    Thread.Sleep(5000); // Wait for 5 seconds
 
    System.Console.ReadLine() // Wait for the user to terminate the program
 
    zp.Close();
}

Der er indsat et kald af ReadLine lige inden Z-Wave porten lukkes. Prøv at trykke på selve switchen inden porten lukkes og se hvad der sker – i kildekoden til eksemplet er der indsat et par linier i SendMessage og ReceiveMessage, så indholdet af de beskeder, der sendes til og fra C# klienten skrives ud på skærmen.

Med eksemplet i denne artikel er det nu muligt at sende flere kommandoer efter hinanden, men princippet om at vente 5 sekunder efter SendMessage er ikke særlig elegant. I næste artikel ændres koden, så ZWavePort klassen selv håndterer dette.

Kildekoden kan hentes via dette link. Der er ingen begrænsninger på anvendelsen af kildekoden.

Annoncer

Kommentarer er lukket.