Insteon Programming · PLM Basics · X10 · PLM Receiving · Insteon Commands · Ramp Rates · Devices · i2cs · Deus Ex Machina

Receiving Events from the PLM

Ok, you set up your Serial Port connection and you can send commands. So far easy enough. This is the part where it gets interesting.

In Visual Basic 2005 (this also applies to 2008), the SerialPort object is assigned its own thread and runs in parallel with the rest of your application. In principle this means it can keep up with the serial device regardless of what the main program gets bogged down with. However, objects and subroutines in one thread are severely limited in their ability to interact with objects and subroutines in other threads, which means that while there is a SerialPort_Datareceived event, you are very limited in what you can do in it. Even if you write the serial data to a global variable, if you also are changing that variable from the other thread, data can easily get lost as the two interfere with each other.

What I ended up doing was creating an array of Bytes. The SerialPort_Datareceived event writes bytes into the array, and updates a pointer to indicate the last byte added. Then the event would Invoke a delegate subroutine in the main thread (which basically means ask the other thread to run that subroutine, at its convenience, with no passed parameters) which would eventually run. The subroutine in the other thread observes (but never changes) the pointer and the array to see what data had come in, while using its own pointer to remember the last byte it has processed. The pointer and array might well be changing while this other subroutine was running.

When referencing the sample code (I'm getting to it), it may also be helpful to know I am storing my Insteon device data in this structure array, defined in my program's definitions area:

   ' Insteon variables
   Structure InsteonDevice
       Dim Address As String
       Dim Name As String
       Dim Device_On As Boolean
       Dim Level As Short   ' Note: I store this as 0-100, not 0-255 as it is truly represented
       Dim Checking As Boolean ' Issued a Status Request to the device, waiting for response
       Dim LastCommand As Byte ' Command1 most recently received (0 if none)
       Dim LastFlags As Byte   ' Just the top three bits (ACK/NAK, group/direct/cleanup/broadcast)
       Dim LastTime As Date        ' Time last command was received
       Dim LastGroup As Byte   ' Group for last group command
   End Structure
   Public Insteon(200) As InsteonDevice
   Public NumInsteon As Short ' Number of assigned Insteon devices

So all the references to Insteon(IAddress).whatever are referring to this array of my user-defined structure. I also call a AddInsteon sub in order to add new devices, but it should be obvious what that does, and you'll probably do something different with your devices anyway.

Also in the definitions area are these constants for colors that are used when calling the WriteEvent sub, which displays updates in a rich text box called rtbEvent. It has a black background (which is why some text is displayed in white).

     Public Const Green As Integer = &H80FF80
     Public Const Gray As Integer = &H808080
     Public Const Black As Integer = 0
     Public Const Red As Integer = &HFF
     Public Const Yellow As Integer = &HFFFF
     Public Const Blue As Integer = &HFF8080
     Public Const White As Integer = &HFFFFFF

The sample code below is the routines from my program with some irrelevant sections stripped out. I've tried to comment it well enough to make sense. For many commands I am just processing them enough to display what is happening, and otherwise not doing anything with the data, but that should make clear what data is where so you can actually use it if it's relevant to you. We do have to recognize the format of every command in order to determine how many bytes it is long, so we can at least skip over it and find the next command, but we don't necessarily have to do anything else with the command. For the event receiving Insteon messages, I have additional code distinguishing Status Request responses from other commands (since these responses use command1 for the link delta rather than echoing the command that is being responded to), and I do some checking so I don't respond both to a group broadcast and a cleanup direct message, so there is more code there.

It may not be obvious, but the DataReceived event triggers more or less whenever a byte of data comes in, and then invokes the PLM() sub. PLM then processes one command worth of data, and stops. Because the Invoke event does not necessarily start PLM right away (Invoke is how the two threads call each other), the PLM sub may end up running with no new data, or with a partial command, etc. So a lot of the logic is handling that without losing any actual data. Also, if the PLM() routine takes a long time to execute, especially if it calls anything that waits (e.g. a macro is called that sends an X10 command), the PLM() routine can be called again before it has finished. So we have to adjust the pointers before doing anything time consuming, or one instance of the PLM() routine will interfere with the other.

You will see references to mnuShowPLC.checked, that's just a menu option which is checked if the user wants to see PLM messages (my code still has some support for the PLC, even though it isn't working at the moment, thus the name).

At several points I have left comments marked with -->  , these indicate where my program is doing other sorts of things not directly related to reading the serial data. Depending on the goal of your own program, these might be good places to add your own functionality.

The code below has several parts. Sub SerialPLM_DataReceived is the event that actually triggers when new data comes in. Sub PLM() is the subroutine that actually does something with the data. Some additional procedures are included since they are called heavily, including WriteEvent (which displays the events in a rich text box called rtbEvent).

 

Private Sub SerialPLM_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPLM.DataReceived

        ' this is the serial port data received event on a secondary thread

        Dim handler As New mySerialDelegate(AddressOf PLM)

 

        Do Until SerialPLM.BytesToRead = 0

            x(x_LastWrite + 1) = SerialPLM.ReadByte

            x(x_LastWrite + 10) = 0

            If x_LastWrite < 30 Then x(x_LastWrite + 1001) = x(x_LastWrite + 1)

' the ends overlap so no message breaks over the limit of the array

            x_LastWrite = x_LastWrite + 1

' increment only after the data is written, in case PLM() is running at same time

            If x_LastWrite > 1000 Then x_LastWrite = 1

        Loop

 

        ' invoke delegate on primary UI thread

        Me.BeginInvoke(handler)

    End Sub

 

    Public Delegate Sub mySerialDelegate()

    Public Sub PLM()

        ' This routine handles the serial data on the primary thread

        Dim i As Short

        Dim X10House As Byte

        Dim X10Code As Byte

        Dim X10Address As String

        Dim FromAddress As String

        Dim ToAddress As String

        Dim IAddress As Short ' Insteon index number

        Dim Flags As Byte

        Dim Command1 As Byte

        Dim Command2 As Byte

        Dim ms As Short          ' Position of start of message ( = x_Start + 1)

        Dim MessageEnd As Short  ' Position of expected end of message (start + length, - 1000 if it's looping)

        Dim DataAvailable As Short ' how many bytes of data available between x_Start and X_LastWrite?

        Dim data(2) As Byte

        Dim Group As Byte

        Dim FromName As String

        Dim DataString As String

        Dim HasName As Boolean

 

        If x_Start = x_LastWrite Then Exit Sub ' reached end of data, get out of sub

 

        ' x_Start = the last byte that was read and processed here

        ' Each time this sub is executed, one command will be processed (if enough data has arrived)

        ' This sub may be called while it is still running, under some circumstances (e.g. if it calls a slow macro)

 

        ' Find the beginning of the next command (always starts with 0x02)

        Do Until (x(x_Start + 1) = 2) Or (x_Start = x_LastWrite) Or (x_Start + 1 = x_LastWrite)

            x_Start = x_Start + 1

            If x_Start > 1000 Then x_Start = 1 ' Loop according to the same looping rule as x_LastWrite

        Loop

 

        ms = x_Start + 1  ' ms = the actual 1st byte of data (which must = 0x02), whereas x_Start is the byte before it

 

        If x(ms) <> 2 Then Exit Sub ' reached the end of usable data, reset starting position, get out of sub

 

        ' How many bytes of data are available to process? (not counting the leading 0x02 of each message)

        If x_Start <= x_LastWrite Then

            DataAvailable = x_LastWrite - ms

        Else

            DataAvailable = 1000 + x_LastWrite - ms

        End If

        If DataAvailable < 1 Then Exit Sub ' not enough for a full message of any type

 

        ' Interpret the message and handle it

        Select Case x(ms + 1)

            Case 96 ' 0x060 response to Get IM Info

                MessageEnd = ms + 8

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 8 Then

                    x_Start = MessageEnd

                    ' Display message

                    WriteEvent(Gray, "PLM response to Get IM Info: PLM ID: ")

                    PLM_Address = GetHex(x(ms + 2)) & "." & GetHex(x(ms + 3)) & "." & GetHex(x(ms + 4))

                    WriteEvent(White, PLM_Address)

                    WriteEvent(Gray, " Device Category: ")

                    WriteEvent(White, GetHex(x(ms + 5)))

                    WriteEvent(Gray, " Subcategory: ")

                    WriteEvent(White, GetHex(x(ms + 6)))

                    WriteEvent(Gray, " Firmware: ")

                    WriteEvent(White, GetHex(x(ms + 7)))

                    WriteEvent(Gray, " ACK/NAK: ")

                    WriteEvent(White, GetHex(x(ms + 8)) & vbCrLf)

 

                    ' Set the PLM as the controller

                    ' --> I use this to verify the PLM is connected, disable some menu options, enable others, etc

 

                End If

            Case 80 ' 0x050 Insteon Standard message received

                ' next three bytes = address of device sending message

                ' next three bytes = address of PLM

                ' next byte = flags

                ' next byte = command1   if status was requested, this = link delta (increments each time link database changes)

                '                        if device level was changed, this = command that was sent

                ' next byte = command2   if status was requested, this = on level (00-FF)

                '                        if device level was changed, this also = on level (00-FF)

                MessageEnd = ms + 10

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 10 Then

                    x_Start = MessageEnd

                    FromAddress = GetHex(x(ms + 2)) & "." & GetHex(x(ms + 3)) & "." & GetHex(x(ms + 4))

                    ToAddress = GetHex(x(ms + 5)) & "." & GetHex(x(ms + 6)) & "." & GetHex(x(ms + 7))

                    Flags = x(ms + 8)

                    Command1 = x(ms + 9)

                    Command2 = x(ms + 10)

                    ' Check if FromAddress is in device database, if not add it (ToAddress will generally = PLM)

                    If InsteonNum(FromAddress) = 0 And FromAddress <> PLM_Address Then

                        AddInsteonDevice(FromAddress)

                        SortInsteon()

                    End If

                    If mnuShowPLC.Checked Then

                        WriteEvent(Gray, "PLM: Insteon Received: From: ")

                        WriteEvent(White, FromAddress)

                        WriteEvent(White, " (" & DeviceNameInsteon(FromAddress) & ")")

                        WriteEvent(Gray, " To: ")

                        WriteEvent(White, ToAddress)

                        If ToAddress = PLM_Address Then

                            WriteEvent(White, " (PLM)")

                        Else

                            WriteEvent(White, " (" & DeviceNameInsteon(ToAddress) & ")")

                        End If

                        WriteEvent(Gray, " Flags: ")

                        WriteEvent(White, GetHex(Flags))

                        Select Case Flags And 224

                            Case 0 ' 000 Direct message

                                WriteEvent(White, " (direct) ")

                            Case 32 ' 001 ACK direct message

                                WriteEvent(White, " (ACK direct) ")

                            Case 64 ' 010 Group cleanup direct message

                                WriteEvent(White, " (Group cleanup direct) ")

                            Case 96 ' 011 ACK group cleanup direct message

                                WriteEvent(White, " (ACK Group cleanup direct) ")

                            Case 128 ' 100 Broadcast message

                                WriteEvent(White, " (Broadcast) ")

                            Case 160 ' 101 NAK direct message

                                WriteEvent(Red, " (NAK direct) ")

                            Case 192 ' 110 Group broadcast message

                                WriteEvent(White, " (Group broadcast) ")

                            Case 224 ' 111 NAK group cleanup direct message

                                WriteEvent(White, " (NAK Group cleanup direct) ")

                        End Select

                        WriteEvent(Gray, " Command1: ")

                        WriteEvent(White, GetHex(Command1) & " (" & CommandsInsteon(Command1) & ")")

                        WriteEvent(Gray, " Command2: ")

                        WriteEvent(White, GetHex(Command2) + vbCrLf)

                    End If

 

                    ' Update the status of the sending device

                    IAddress = InsteonNum(FromAddress)  ' already checked to make sure it was in list

                    FromName = Insteon(IAddress).Name

                    If FromName = "" Then FromName = Insteon(IAddress).Address

                    If (Flags And 160) <> 160 Then

                        ' Not a NAK response, could be an ACK or a new message coming in

                        ' Either way, update the sending device

                        Select Case Command1

                            Case 17, 18 ' On, Fast On

                                Insteon(IAddress).Device_On = True

                                If (Flags And 64) = 64 Then

                                    ' Group message (broadcast or cleanup)

                                    Insteon(IAddress).Level = 100  ' the real level is the preset for the link, but...

                                Else

                                    ' Direct message

                                    Insteon(IAddress).Level = Command2 / 2.55  ' change to scale of 0-100

                                End If

                            Case 19, 20 ' Off, Fast Off

                                Insteon(IAddress).Device_On = False

                                Insteon(IAddress).Level = 0

                            Case 21 ' Bright

                                Insteon(IAddress).Device_On = True

                                If Insteon(IAddress).Level > 100 Then Insteon(IAddress).Level = 100

                                Insteon(IAddress).Level = Insteon(IAddress).Level + 3

                            Case 22 ' Dim

                                Insteon(IAddress).Level = Insteon(IAddress).Level - 3

                                If Insteon(IAddress).Level < 0 Then Insteon(IAddress).Level = 0

                                If Insteon(IAddress).Level = 0 Then Insteon(IAddress).Device_On = False

                        End Select

                        ' Check whether this was a response to a Status Request

                        If (Flags And 224) = 32 Then

                            ' ACK Direct message

                            If Insteon(IAddress).Checking = True Then

                                Insteon(IAddress).Level = Command2 / 2.55

                                If Insteon(IAddress).Level > 0 Then

                                    Insteon(IAddress).Device_On = True

                                Else

                                    Insteon(IAddress).Device_On = False

                                End If

                            End If

                        End If

                        ' --> At this point I update a grid display, but you can do whatever you want...

                    End If

 

                    ' Now actually respond to events...

                    If Insteon(IAddress).Checking = True Then

                        ' It was a Status Request response, don't treat it as an event

                        Insteon(IAddress).Checking = False

                        Insteon(IAddress).LastCommand = 0

                        Insteon(IAddress).LastFlags = Flags And 224

                        Insteon(IAddress).LastTime = Now

                        Insteon(IAddress).LastGroup = 0

                    Else

                        ' It wasn't a Status Request, process it

                        Select Case Flags And 224

                            Case 128 ' 100 Broadcast message

                                ' Button-press linking, etc. Just display a message.

                                ' Message format: FromAddress, DevCat, SubCat, Firmware, Flags, Cmd1, Cmd2 (=Device Attributes)

                                ' Time stamp in blue

                                WriteEvent(Blue, VB6.Format(TimeOfDay) & " ")

                                If Command1 = 1 Then

                                    WriteEvent(Green, FromName & " broadcast 'Set Button Pressed'")

                                Else

                                    WriteEvent(Green, FromName & " broadcast command " & GetHex(Command1))

                                End If

                                Insteon(IAddress).LastCommand = Command1

                                Insteon(IAddress).LastFlags = Flags And 224

                                Insteon(IAddress).LastTime = Now

                                Insteon(IAddress).LastGroup = 0

                            Case 0, 64, 192 ' 000 Direct, 010 Group cleanup direct, 110 Group broadcast message

                                ' Message sent to PLM by another device - trigger sounds, macro, etc

 

                                If Flags And 64 Then

                                    ' Group message

                                    If Flags And 128 Then

                                        ' Group broadcast - group number is third byte of ToAddress

                                        Group = x(ms + 7)

                                        If Command1 = Insteon(IAddress).LastCommand And (Group = Insteon(IAddress).LastGroup) Then

                                            ' This is the same command we last received from this device

                                            ' Is this a repeat? (Some devices, like the RemoteLinc, seem to double their group broadcasts)

                                            If (Flags And 224) = (Insteon(IAddress).LastFlags) Then

                                                If DateDiff(DateInterval.Second, Insteon(IAddress).LastTime, Now) < 1 Then

                                                    ' Same command, same flags, within the last second....

                                                    Exit Select ' So skip out of here without doing anything else

                                                End If

                                            End If

                                        End If

                                    Else

                                        ' Group cleanup direct - group number is Command2

                                        Group = Command2

                                        If Command1 = Insteon(IAddress).LastCommand And (Group = Insteon(IAddress).LastGroup) Then

                                            ' This is the same command we last received from this device

                                            ' Is this a Group Cleanup Direct message we already got a Group Broadcast for?

                                            If (Insteon(IAddress).LastFlags And 224) = 192 Then

                                                ' Last message was, in fact, a Group Broadcast

                                                If DateDiff(DateInterval.Second, Insteon(IAddress).LastTime, Now) < 3 Then

                                                    ' Within the last 3 seconds....

                                                    Exit Select ' So skip out of here without doing anything else

                                                End If

                                            End If

                                        End If

                                    End If

                                Else

                                    ' Direct message

                                    Group = 0

                                End If

                                Insteon(IAddress).LastCommand = Command1

                                Insteon(IAddress).LastFlags = Flags And 224

                                Insteon(IAddress).LastTime = Now

                                Insteon(IAddress).LastGroup = Group

 

                                ' Time stamp in blue

                                WriteEvent(Blue, VB6.Format(TimeOfDay) & " ")

 

                                ' Write command to event log

                                If Group > 0 Then

                                    WriteEvent(Green, FromName & " " & CommandsInsteon(Command1) & " (Group " & VB6.Format(Group) & ")" & vbCrLf)

                                Else

                                    WriteEvent(Green, FromName & " " & CommandsInsteon(Command1) & vbCrLf)

                                End If

 

                                ' Handle incoming event and play sounds

                                ' --> at this point I play a WAV file and run any macro associated with the device

 

                            Case 32, 96 ' 001 ACK direct message, 011 ACK group cleanup direct message

                                ' Command received and acknowledged by another device - update device status is already done (above).

                                ' Nothing left to do here.

                                Insteon(IAddress).LastCommand = Command1

                                Insteon(IAddress).LastFlags = Flags And 224

                                Insteon(IAddress).LastTime = Now

                                Insteon(IAddress).LastGroup = 0

                            Case 160, 224 ' 101 NAK direct message, 111 NAK group cleanup direct message

                                ' Command received by another device but failed - display message in log

                                ' Time stamp in blue

                                WriteEvent(Blue, VB6.Format(TimeOfDay) & " ")

                                WriteEvent(Green, DeviceNameInsteon(FromAddress) & " NAK to command " & GetHex(Command1) & " (" & CommandsInsteon(Command1) & ")")

                                Insteon(IAddress).LastCommand = Command1

                                Insteon(IAddress).LastFlags = Flags And 224

                                Insteon(IAddress).LastTime = Now

                                Insteon(IAddress).LastGroup = 0

                        End Select

                    End If

                End If

            Case 81 ' 0x051 Insteon Extended message received - 23 byte message

                MessageEnd = ms + 24

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 24 Then

                    x_Start = MessageEnd

                    FromAddress = GetHex(x(ms + 2)) & "." & GetHex(x(ms + 3)) & "." & GetHex(x(ms + 4))

                    ToAddress = GetHex(x(ms + 5)) & "." & GetHex(x(ms + 6)) & "." & GetHex(x(ms + 7))

                    Flags = x(ms + 8)

                    Command1 = x(ms + 9)

                    Command2 = x(ms + 10)

                    If mnuShowPLC.Checked Then

                        WriteEvent(Gray, "PLM: Insteon Extended Received: From: ")

                        WriteEvent(White, FromAddress)

                        WriteEvent(White, " (" & DeviceNameInsteon(FromAddress) & ")")

                        WriteEvent(Gray, " To: ")

                        WriteEvent(White, ToAddress)

                        If ToAddress = PLM_Address Then

                            WriteEvent(White, " (PLM)")

                        Else

                            WriteEvent(White, " (" & DeviceNameInsteon(ToAddress) & ")")

                        End If

                        WriteEvent(Gray, " Flags: ")

                        WriteEvent(White, GetHex(Flags))

                        Select Case Flags And 224

                            Case 0 ' 000 Direct message

                                WriteEvent(White, " (direct) ")

                            Case 32 ' 001 ACK direct message

                                WriteEvent(White, " (ACK direct) ")

                            Case 64 ' 010 Group cleanup direct message

                                WriteEvent(White, " (Group cleanup direct) ")

                            Case 96 ' 011 ACK group cleanup direct message

                                WriteEvent(White, " (ACK Group cleanup direct) ")

                            Case 128 ' 100 Broadcast message

                                WriteEvent(White, " (Broadcast) ")

                            Case 160 ' 101 NAK direct message

                                WriteEvent(Red, " (NAK direct) ")

                            Case 192 ' 110 Group broadcast message

                                WriteEvent(White, " (Group broadcast) ")

                            Case 224 ' 111 NAK group cleanup direct message

                                WriteEvent(White, " (NAK Group cleanup direct) ")

                        End Select

                        WriteEvent(Gray, " Command1: ")

                        WriteEvent(White, GetHex(Command1) & " (" & CommandsInsteon(Command1) & ")")

                        WriteEvent(Gray, " Command2: ")

                        WriteEvent(White, GetHex(Command2))

                        If Command1 = 3 Then

                            ' Product Data Response

                            Select Case Command2

                                Case 0 ' Product Data Response

                                    WriteEvent(White, " Product Data Response")

                                    WriteEvent(Gray, " Data: ")

                                    For i = 11 To 24

                                        WriteEvent(White, GetHex(x(ms + i)) & " ")

                                    Next

                                    WriteEvent(White, "--> Product Key " & GetHex(x(ms + 12)) & GetHex(x(ms + 13)) & GetHex(x(ms + 14)))

                                    WriteEvent(White, " DevCat: " & GetHex(x(ms + 15)))

                                    WriteEvent(White, " SubCat: " & GetHex(x(ms + 16)))

                                    WriteEvent(White, " Firmware: " & GetHex(x(ms + 17)))

                                Case 1 ' FX Username Response

                                    WriteEvent(White, " FX Username Response")

                                    WriteEvent(Gray, " D1-D8 FX Command Username: ")

                                    For i = 11 To 18

                                        WriteEvent(White, GetHex(x(ms + i)) & " ")

                                    Next

                                    WriteEvent(Gray, " D9-D14: ")

                                    For i = 19 To 24

                                        WriteEvent(White, GetHex(x(ms + i)) & " ")

                                    Next

                                Case 2 ' Device Text String

                                    WriteEvent(White, " Device Text String Response")

                                    WriteEvent(Gray, " D1-D8 FX Command Username: ")

                                    DataString = ""

                                    For i = 11 To 24

                                        WriteEvent(White, GetHex(x(ms + i)) & " ")

                                    Next

                                    For i = 11 To 24

                                        If x(ms + i) = 0 Then Exit For

                                        DataString = DataString + Chr(x(ms + i))

                                    Next

                                    WriteEvent(White, Quote & DataString & Quote)

                                Case 3 ' Set Device Text String

                                    WriteEvent(White, " Set Device Text String")

                                    WriteEvent(Gray, " D1-D8 FX Command Username: ")

                                    DataString = ""

                                    For i = 11 To 24

                                        WriteEvent(White, GetHex(x(ms + i)) & " ")

                                    Next

                                    For i = 11 To 24

                                        If x(ms + i) = 0 Then Exit For

                                        DataString = DataString + Chr(x(ms + i))

                                    Next

                                    WriteEvent(White, Quote & DataString & Quote)

                                Case 4 ' Set ALL-Link Command Alias

                                    WriteEvent(White, " Set ALL-Link Command Alias")

                                    WriteEvent(Gray, " Data: ")

                                    For i = 11 To 24

                                        WriteEvent(White, GetHex(x(ms + i)) & " ")

                                    Next

                                Case 5 ' Set ALL-Link Command Alias Extended Data

                                    WriteEvent(White, " Set ALL-Link Command Alias Extended Data")

                                    WriteEvent(Gray, " Data: ")

                                    For i = 11 To 24

                                        WriteEvent(White, GetHex(x(ms + i)) & " ")

                                    Next

                                Case Else

                                    WriteEvent(White, " (unrecognized product data response)")

                                    WriteEvent(Gray, " Data: ")

                                    For i = 11 To 24

                                        WriteEvent(White, GetHex(x(ms + i)) & " ")

                                    Next

                            End Select

                        Else

                            ' Anything other than a product data response

                            WriteEvent(Gray, " Data: ")

                            For i = 11 To 24

                                WriteEvent(White, GetHex(x(ms + i)) & " ")

                            Next

                        End If

                        WriteEvent(White, vbCrLf)

                    End If

                End If

                ' I’m not planning on actually doing anything with this data, just displayed if mnuShowPLC.Checked

 

            Case 82 ' 0x052 X10 Received

                ' next byte: raw X10   x(MsStart + 2)

                ' next byte: x10 flag  x(MsStart + 3)

                MessageEnd = ms + 3

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 3 Then

                    x_Start = MessageEnd

                    If mnuShowPLC.Checked Then WriteEvent(Gray, "PLM: X10 Received: ")

                    X10House = X10House_from_PLM(x(ms + 2) And 240)

                    Select Case x(ms + 3)

                        Case 0 ' House + Device

                            X10Code = X10Device_from_PLM(x(ms + 2) And 15)

                            If mnuShowPLC.Checked Then WriteEvent(White, Chr(65 + X10House) & (X10Code + 1) & vbCrLf)

                            PLM_LastX10Device = X10Code ' Device Code 0-15

                        Case 63, 128 ' 0x80 House + Command    63 = 0x3F - should be 0x80 but for some reason I keep getting 0x3F instead

                            X10Code = (x(ms + 2) And 15) + 1

                            X10Address = Chr(65 + X10House) & (PLM_LastX10Device + 1)

                            If mnuShowPLC.Checked Then WriteEvent(White, Chr(65 + X10House) & " " & Commands(X10Code) & vbCrLf)

                            ' Now actually process the event

                            ' Does it have a name?

                            If DeviceName(X10Address) = X10Address Then HasName = False Else HasName = True

                            ' Time stamp in blue

                            WriteEvent(Blue, VB6.Format(TimeOfDay) & " ")

                            If LoggedIn And HasName Then frmHack.WriteWebtrix(Blue, VB6.Format(TimeOfDay) & " ")

                            ' Write command to event log

                            WriteEvent(Green, DeviceName(X10Address) + " " + Commands(X10Code) + vbCrLf)

 

                            ' Handle incoming event

                            Select Case X10Code

                                Case 3 ' On

                                ' --> at this point I play a WAV file and run any macro associated with the device

 

                                Case 4 ' Off

                                ' --> at this point I play a WAV file and run any macro associated with the device

 

                                Case 5 ' Dim

                                ' --> at this point I play a WAV file and run any macro associated with the device

 

                                Case 6 ' Bright

                                ' --> at this point I play a WAV file and run any macro associated with the device

 

                            End Select

                        Case Else ' invalid data

                            If mnuShowPLC.Checked Then WriteEvent(White, "Unrecognized X10: " & GetHex(x(ms + 2)) & " " & GetHex(x(ms + 3)) & vbCrLf)

                    End Select

                End If

            Case 98 ' 0x062 Send Insteon standard OR extended message

                ' PLM is just echoing the last command sent, discard this: 7 or 21 bytes

                MessageEnd = ms + 8

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 8 Then

                    If (x(ms + 5) And 16) = 16 Then

                        ' Extended message

                        MessageEnd = x_Start + 1 + 22

                        If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                        If DataAvailable >= 22 Then

                            x_Start = MessageEnd

                            If mnuShowPLC.Checked Then

                                WriteEvent(Gray, "PLM: Sent Insteon message (extended): ")

                                For i = 0 To 22

                                    WriteEvent(White, GetHex(x(ms + i)) & " ")

                                Next

                                WriteEvent(White, vbCrLf)

                            End If

                        End If

                    Else

                        ' Standard message

                        x_Start = MessageEnd

                        If mnuShowPLC.Checked Then

                            WriteEvent(Gray, "PLM: Sent Insteon message (standard): ")

                            For i = 0 To 8

                                WriteEvent(White, GetHex(x(ms + i)) & " ")

                            Next

                            WriteEvent(White, vbCrLf)

                        End If

                    End If

                End If

            Case 99 ' 0x063 Sent X10

                ' PLM is just echoing the command we last sent, discard: 3 bytes --- although could error check this for NAKs...

                MessageEnd = ms + 4

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 4 Then

                    x_Start = MessageEnd

                    WriteEvent(Gray, "PLM: X10 Sent: ")

                    X10House = X10House_from_PLM(x(ms + 2) And 240)

                    Select Case x(ms + 3)

                        Case 0 ' House + Device

                            X10Code = X10Device_from_PLM(x(ms + 2) And 15)

                            WriteEvent(White, Chr(65 + X10House) & (X10Code + 1))

                        Case 63, 128 ' 0x80 House + Command    63 = 0x3F - should be 0x80 but for some reason I keep getting 0x3F instead

                            X10Code = (x(ms + 2) And 15) + 1

                            If X10Code > -1 And X10Code < 17 Then

                                WriteEvent(White, Chr(65 + X10House) & " " & Commands(X10Code))

                            Else

                                WriteEvent(White, Chr(65 + X10House) & " unrecognized command " & GetHex(x(ms + 2) And 15))

                            End If

                        Case Else ' invalid data

                            WriteEvent(White, "Unrecognized X10: " & GetHex(x(ms + 2)) & " " & GetHex(x(ms + 3)))

                    End Select

                    WriteEvent(Gray, " ACK/NAK: ")

                    Select Case x(ms + 4)

                        Case 6

                            WriteEvent(White, "06 (sent)" + vbCrLf)

                        Case 21

                            WriteEvent(White, "15 (failed)" + vbCrLf)

                        Case Else

                            WriteEvent(White, GetHex(x(ms + 4)) & " (?)" + vbCrLf)

                    End Select

                End If

            Case 83 ' 0x053 ALL-Linking complete - 8 bytes of data

                MessageEnd = ms + 9

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 9 Then

                    x_Start = MessageEnd

                    If mnuShowPLC.Checked Then

                        WriteEvent(Gray, "PLM: ALL-Linking Complete: 0x53 Link Code: ")

                        WriteEvent(White, GetHex(x(ms + 2)))

                        Select Case x(ms + 2)

                            Case 0

                                WriteEvent(White, " (responder)")

                            Case 1

                                WriteEvent(White, " (controller)")

                            Case 244

                                WriteEvent(White, " (deleted)")

                        End Select

                        WriteEvent(Gray, " Group: ")

                        WriteEvent(White, GetHex(x(ms + 3)))

                        WriteEvent(Gray, " ID: ")

                        FromAddress = GetHex(x(ms + 4)) & "." & GetHex(x(ms + 5)) & "." & GetHex(x(ms + 6))

                        WriteEvent(White, FromAddress)

                        WriteEvent(White, " (" & DeviceNameInsteon(FromAddress) & ")")

                        WriteEvent(Gray, " DevCat: ")

                        WriteEvent(White, GetHex(x(ms + 7)))

                        WriteEvent(Gray, " SubCat: ")

                        WriteEvent(White, GetHex(x(ms + 8)))

                        WriteEvent(Gray, " Firmware: ")

                        WriteEvent(White, GetHex(x(ms + 9)))

                        If x(ms + 9) = 255 Then WriteEvent(White, " (all newer devices = FF)")

                        WriteEvent(White, vbCrLf)

                    End If

                End If

            Case 87 ' 0x057 ALL-Link record response - 8 bytes of data

                MessageEnd = ms + 9

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 9 Then

                    x_Start = MessageEnd

                    FromAddress = GetHex(x(ms + 4)) & "." & GetHex(x(ms + 5)) & "." & GetHex(x(ms + 6))

                    ' Check if FromAddress is in device database, if not add it

                    If InsteonNum(FromAddress) = 0 Then

                        AddInsteonDevice(FromAddress)

                        SortInsteon()

                    End If

                    If mnuShowPLC.Checked Then

                        WriteEvent(Gray, "PLM: ALL-Link Record response: 0x57 Flags: ")

                        WriteEvent(White, GetHex(x(ms + 2)))

                        WriteEvent(Gray, " Group: ")

                        WriteEvent(White, GetHex(x(ms + 3)))

                        WriteEvent(Gray, " Address: ")

                        WriteEvent(White, FromAddress)

                        WriteEvent(White, " (" & DeviceNameInsteon(FromAddress) & ")")

                        WriteEvent(Gray, " Data: ")

                        WriteEvent(White, GetHex(x(ms + 7)) & " " & GetHex(x(ms + 8)) & " " & GetHex(x(ms + 9)) & vbCrLf)

                    End If

                    ' --> I assume this happened because I requested the data, and want the rest of it. So now

                    ' Send 02 6A to get next record (e.g. continue reading link database from PLM)

                    data(0) = 2  ' all commands start with 2

                    data(1) = 106 ' 0x06A = Get Next ALL-Link Record

                    SerialPLM.Write(data, 0, 2)

                End If

            Case 85 ' 0x055 User reset the PLM - 0 bytes of data, not possible for this to be a partial message

                MessageEnd = ms + 1

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                x_Start = MessageEnd

                WriteEvent(Gray, "PLM: User Reset 0x55" + vbCrLf)

            Case 86 ' 0x056 ALL-Link cleanup failure report - 5 bytes of data

                MessageEnd = ms + 6

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 6 Then

                    x_Start = MessageEnd

                    ToAddress = GetHex(x(ms + 4)) & "." & GetHex(x(ms + 5)) & "." & GetHex(x(ms + 6))

                    If mnuShowPLC.Checked Then

                        WriteEvent(Gray, "PLM: ALL-Link (Group Broadcast) Cleanup Failure Report 0x56 Data: ")

                        WriteEvent(White, GetHex(x(ms + 2)))

                        WriteEvent(Gray, " Group: ")

                        WriteEvent(White, GetHex(x(ms + 3)))

                        WriteEvent(Gray, " Address: ")

                        WriteEvent(White, ToAddress & " (" & DeviceNameInsteon(ToAddress) & ")" & vbCrLf)

                    End If

                End If

            Case 97 ' 0x061 Sent ALL-Link (Group Broadcast) command - 4 bytes

                MessageEnd = ms + 5

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 5 Then

                    x_Start = MessageEnd

                    If mnuShowPLC.Checked Then

                        WriteEvent(Gray, "PLM: Sent Group Broadcast: 0x61 Group: ")

                        WriteEvent(White, GetHex(x(ms + 2)))

                        WriteEvent(Gray, " Command1: ")

                        WriteEvent(White, GetHex(x(ms + 3)))

                        WriteEvent(Gray, " Command2 (Group): ")

                        WriteEvent(White, GetHex(x(ms + 4)))

                        WriteEvent(Gray, " ACK/NAK: ")

                        Select Case x(ms + 5)

                            Case 6

                                WriteEvent(White, "06 (sent)" + vbCrLf)

                            Case 21

                                WriteEvent(White, "15 (failed)" + vbCrLf)

                            Case Else

                                WriteEvent(White, GetHex(x(ms + 5)) & " (?)" + vbCrLf)

                        End Select

                    End If

                End If

            Case 102 ' 0x066 Set Host Device Category - 4 bytes

                MessageEnd = ms + 5

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 5 Then

                    x_Start = MessageEnd

                    If mnuShowPLC.Checked Then

                        WriteEvent(Gray, "PLM: Set Host Device Category: 0x66 DevCat: ")

                        WriteEvent(White, GetHex(x(ms + 2)))

                        WriteEvent(Gray, " SubCat: ")

                        WriteEvent(White, GetHex(x(ms + 3)))

                        WriteEvent(Gray, " Firmware: ")

                        WriteEvent(White, GetHex(x(ms + 4)))

                        If x(ms + 4) = 255 Then WriteEvent(White, " (all newer devices = FF)")

                        WriteEvent(Gray, " ACK/NAK: ")

                        Select Case x(ms + 5)

                            Case 6

                                WriteEvent(White, "06 (executed correctly)" + vbCrLf)

                            Case 21

                                WriteEvent(White, "15 (failed)" + vbCrLf)

                            Case Else

                                WriteEvent(White, GetHex(x(ms + 5)) & " (?)" + vbCrLf)

                        End Select

                    End If

                End If

            Case 115 ' 0x073 Get IM Configuration - 4 bytes

                MessageEnd = ms + 5

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 5 Then

                    x_Start = MessageEnd

                    WriteEvent(Gray, "PLM: Get IM Configuration: 0x73 Flags: ")

                    WriteEvent(White, GetHex(x(ms + 2)))

                    If x(ms + 2) And 128 Then WriteEvent(White, " (no button linking)")

                    If x(ms + 2) And 64 Then WriteEvent(White, " (monitor mode)")

                    If x(ms + 2) And 32 Then WriteEvent(White, " (manual LED control)")

                    If x(ms + 2) And 16 Then WriteEvent(White, " (disable deadman comm feature)")

                    If x(ms + 2) And (128 + 64 + 32 + 16) Then WriteEvent(White, " (default)")

                    WriteEvent(Gray, " Data: ")

                    WriteEvent(White, GetHex(x(ms + 3)) & " " & GetHex(x(ms + 4)))

                    WriteEvent(Gray, " ACK: ")

                    WriteEvent(White, GetHex(x(ms + 5)) + vbCrLf)

                End If

            Case 100 ' 0x064 Start ALL-Linking, echoed - 3 bytes

                MessageEnd = ms + 4

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 4 Then

                    x_Start = MessageEnd

                    WriteEvent(Gray, "PLM: Start ALL-Linking 0x64 Code: ")

                    WriteEvent(White, GetHex(x(ms + 2)))

                    Select Case x(ms + 2)

                        Case 0

                            WriteEvent(White, " (PLM is responder)")

                        Case 1

                            WriteEvent(White, " (PLM is controller)")

                        Case 3

                            WriteEvent(White, " (initiator is controller)")

                        Case 244

                            WriteEvent(White, " (deleted)")

                    End Select

                    WriteEvent(Gray, " Group: ")

                    WriteEvent(White, GetHex(x(ms + 3)))

                    WriteEvent(Gray, " ACK/NAK: ")

                    Select Case x(ms + 4)

                        Case 6

                            WriteEvent(White, "06 (executed correctly)" + vbCrLf)

                        Case 21

                            WriteEvent(White, "15 (failed)" + vbCrLf)

                        Case Else

                            WriteEvent(White, GetHex(x(ms + 4)) & " (?)" + vbCrLf)

                    End Select

                End If

            Case 113 ' 0x071 Set Insteon ACK message two bytes - 3 bytes

                MessageEnd = ms + 4

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 4 Then

                    x_Start = MessageEnd

                    WriteEvent(Gray, "PLM: Set Insteon ACK message 0x71 ")

                    For i = 2 To 4

                        WriteEvent(White, GetHex(x(ms + i)) & " ")

                    Next

                    WriteEvent(White, vbCrLf)

                End If

            Case 104, 107, 112 ' 0x068 Set Insteon ACK message byte, 0x06B Set IM Configuration, 0x070 Set Insteon NAK message byte

                ' 2 bytes

                MessageEnd = ms + 3

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 3 Then

                    x_Start = MessageEnd

                    WriteEvent(Gray, "PLM: ")

                    For i = 0 To 3

                        WriteEvent(White, GetHex(x(ms + i)) & " ")

                    Next

                    WriteEvent(White, vbCrLf)

                End If

            Case 88 ' 0x058 ALL-Link cleanup status report - 1 byte

                MessageEnd = ms + 2

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 2 Then

                    x_Start = MessageEnd

                    WriteEvent(Gray, "PLM: ALL-Link (Group Broadcast) Cleanup Status Report 0x58 ACK/NAK: ")

                    Select Case x(ms + 2)

                        Case 6

                            WriteEvent(White, "06 (completed)" + vbCrLf)

                        Case 21

                            WriteEvent(White, "15 (interrupted)" + vbCrLf)

                        Case Else

                            WriteEvent(White, GetHex(x(ms + 2)) & " (?)" + vbCrLf)

                    End Select

                End If

            Case 84, 103, 108, 109, 110, 114

                ' 0x054 Button (on PLM) event, 0x067 Reset the IM, 0x06C Get ALL-Link record for sender, 0x06D LED On, 0x06E LED Off, 0x072 RF Sleep

                ' discard: 1 byte

                MessageEnd = ms + 2

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 2 Then

                    x_Start = MessageEnd

                    WriteEvent(Gray, "PLM: ")

                    For i = 0 To 2

                        WriteEvent(White, GetHex(x(ms + i)) & " ")

                    Next

                    WriteEvent(White, vbCrLf)

                End If

            Case 101 ' 0x065 Cancel ALL-Linking - 1 byte

                MessageEnd = ms + 2

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 2 Then

                    x_Start = MessageEnd

                    WriteEvent(Gray, "PLM: Cancel ALL-Linking 0x65 ACK/NAK: ")

                    Select Case x(ms + 2)

                        Case 6

                            WriteEvent(White, "06 (success)" + vbCrLf)

                        Case 21

                            WriteEvent(White, "15 (failed)" + vbCrLf)

                        Case Else

                            WriteEvent(White, GetHex(x(ms + 2)) & " (?)" + vbCrLf)

                    End Select

                End If

            Case 105 ' 0x069 Get First ALL-Link record

                MessageEnd = ms + 2

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 2 Then

                    x_Start = MessageEnd

                    If mnuShowPLC.Checked Then

                        WriteEvent(Gray, "PLM: 0x69 Get First ALL-Link record: ")

                        WriteEvent(White, GetHex(x(ms + 2)))

                        Select Case x(ms + 2)

                            Case 6

                                WriteEvent(White, " (ACK)")

                            Case 21

                                WriteEvent(White, " (NAK - no links in database)")

                        End Select

                        WriteEvent(White, vbCrLf)

                    End If

                End If

            Case 106 ' 0x06A Get Next ALL-Link record

                MessageEnd = ms + 2

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 2 Then

                    x_Start = MessageEnd

                    If mnuShowPLC.Checked Then

                        WriteEvent(Gray, "PLM: 0x6A Get Next ALL-Link record: ")

                        WriteEvent(White, GetHex(x(ms + 2)))

                        Select Case x(ms + 2)

                            Case 6

                                WriteEvent(White, " (ACK)")

                            Case 21

                                WriteEvent(White, " (NAK - no more links in database)")

                        End Select

                        WriteEvent(White, vbCrLf)

                    End If

                End If

            Case 111 ' 0x06F Manage ALL-Link record - 10 bytes

                MessageEnd = ms + 11

                If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000

                If DataAvailable >= 11 Then

                    x_Start = MessageEnd

                    ToAddress = GetHex(x(ms + 5)) & "." & GetHex(x(ms + 6)) & "." & GetHex(x(ms + 7))

                    If mnuShowPLC.Checked Then

                        WriteEvent(Gray, "PLM: Manage ALL-Link Record 0x6F: Code: ")

                        WriteEvent(White, GetHex(x(ms + 2)))

                        Select Case x(ms + 2)

                            Case 0 ' 0x00

                                WriteEvent(White, " (Check for record)")

                            Case 1 ' 0x01

                                WriteEvent(White, " (Next record for...)")

                            Case 32 ' 0x20

                                WriteEvent(White, " (Update or add)")

                            Case 64 ' 0x40

                                WriteEvent(White, " (Update or add as controller)")

                            Case 65 ' 0x41

                                WriteEvent(White, " (Update or add as responder)")

                            Case 128 ' 0x80

                                WriteEvent(White, " (Delete)")

                            Case Else ' ?

                                WriteEvent(White, " (?)")

                        End Select

                        WriteEvent(Gray, " Link Flags: ")

                        WriteEvent(White, GetHex(x(ms + 3)))

                        WriteEvent(Gray, " Group: ")

                        WriteEvent(White, GetHex(x(ms + 4)))

                        WriteEvent(Gray, " Address: ")

                        WriteEvent(White, ToAddress & " (" & DeviceNameInsteon(ToAddress) & ")")

                        WriteEvent(Gray, " Link Data: ")

                        WriteEvent(White, GetHex(x(ms + 8)) & " " & GetHex(x(ms + 9)) & " " & GetHex(x(ms + 10)))

                        WriteEvent(Gray, " ACK/NAK: ")

                        Select Case x(ms + 11)

                            Case 6

                                WriteEvent(White, "06 (executed correctly)" + vbCrLf)

                            Case 21

                                WriteEvent(White, "15 (failed)" + vbCrLf)

                            Case Else

                                WriteEvent(White, GetHex(x(ms + 11)) & " (?)" + vbCrLf)

                        End Select

                    End If

                End If

            Case Else

                ' in principle this shouldn't happen... unless there are undocumented PLM messages (probably!)

                x_Start = x_Start + 1  ' just skip over this and hope to hit a real command next time through the loop

                If x_Start > 1000 Then x_Start = x_Start - 1000

                WriteEvent(Gray, "PLM: Unrecognized command received: ")

                Debug.WriteLine("Unrecognized command received " & GetHex(x(ms)) & " " & GetHex(x(ms + 1)) & " " & GetHex(x(ms + 2)))

                For i = 0 To DataAvailable

                    WriteEvent(White, GetHex(x(ms + DataAvailable)))

                Next

        End Select

 

        Debug.WriteLine("PLM finished: ms = " & ms & " MessageEnd = " & MessageEnd & " X_Start = " & x_Start)

        Exit Sub

 

    End Sub

 

 

   Public Sub WriteEvent(ByRef Color As Integer, ByRef NewText As String)

        Dim last As Short

        ' WriteEvent(Color, Text) prints Text to the rtbEvent box, in Color

        ' --> Obviously this will only work if you have a rich text box named rtbEvent.

        ' Colors:

        '   Blue: time

        '   Green: events (macros)

        '   Yellow: commands sent

        '   Red: errors (also debug info: gives info about Play and If/Then/Else/Endif macro commands)

        last = Len(rtbEvent.Text)

        rtbEvent.SelectionStart = last

        rtbEvent.SelectedText = NewText

        rtbEvent.SelectionStart = last

        rtbEvent.SelectionLength = Len(NewText)

        rtbEvent.SelectionColor = System.Drawing.ColorTranslator.FromOle(Color)

        rtbEvent.SelectionStart = Len(rtbEvent.Text)

        rtbEvent.ScrollToCaret()

    End Sub

 

    Public Function InsteonNum(ByVal Address As String) As Short

        ' return the array index for this insteon device based on the address

        ' or 0 if address is not found

 

        Dim i As Short

        Address = UCase(Address)

        i = 1

        Do Until UCase(Insteon(i).Address) = Address Or i >= NumInsteon

            i = i + 1

        Loop

        If UCase(Insteon(i).Address) = Address Then

            InsteonNum = i

        Else

            InsteonNum = 0

        End If

    End Function

 

    Public Function X10House_from_PLM(ByVal Index As Byte) As Short

        Dim i As Short

        ' Given the MSB from the PLM, return the House (0-15). If not found, return -1.

        i = 1

        Do Until PLM_X10_House(i) = Index Or i = 16

            i = i + 1

        Loop

        If PLM_X10_House(i) = Index Then

            X10House_from_PLM = i - 1

        Else

            X10House_from_PLM = -1

        End If

    End Function

 

    Public Function X10Device_from_PLM(ByVal Index As Byte) As Short

        Dim i As Short

        ' Given the LSB from the PLM, return the Device (0-15). (Also works for commands.) If not found, return -1.

        i = 1

        Do Until PLM_X10_Device(i) = Index Or i = 16

            i = i + 1

        Loop

        If PLM_X10_Device(i) = Index Then

            X10Device_from_PLM = i - 1

        Else

            X10Device_from_PLM = -1

        End If

    End Function